Merge branch 'master' into showcase-liked
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
4. [平台入口与玩法链路](./【玩法创作】平台入口与玩法链路-2026-05-15.md)
|
||||
5. [本地开发验证与生产运维](./【开发运维】本地开发验证与生产运维-2026-05-15.md)
|
||||
6. [AI 游戏创作项目开发工作台 PRD](./prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md)
|
||||
7. [AI 游戏创作智能体 App 实施计划](./technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)
|
||||
8. [客户端素材创作无限画布阶段一合同](./technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md)
|
||||
|
||||
团队长期约定、决策、流程和排障摘要统一从 [项目记忆入口](./project-memory/README.md) 读取。代码、当前融合文档与项目记忆冲突时,以代码和最新融合文档为准。
|
||||
|
||||
@@ -17,6 +19,7 @@
|
||||
|
||||
### 图片编辑器与 Agent
|
||||
|
||||
- [客户端素材创作无限画布阶段一合同](./technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md)
|
||||
- [图片画布编辑器 MVP 接入方案](./technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md)
|
||||
- [图片画布编辑器前端拆分计划](./technical/【前端架构】图片画布编辑器前端拆分计划-2026-06-17.md)
|
||||
- [图片画布游戏场景生成链路](./technical/【技术方案】图片画布游戏场景生成链路-2026-08-04.md)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# AI 游戏创作项目开发工作台 PRD
|
||||
|
||||
更新时间:`2026-08-10`(现有 Godot 项目入口与项目根绑定)
|
||||
更新时间:`2026-08-11`(资源卡预览、分区布局与依赖聚类,图片素材无限画布及全类型非破坏性资源编辑收口;现有 Godot 项目入口与项目根绑定)
|
||||
|
||||
## 1. 产品定位
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
3. 右侧 Project Supervisor 对话与确认区。
|
||||
4. 底部专业 Agent 状态栏。
|
||||
|
||||
中央主视窗在“资源管理”和“运行测试”之间切换。正式预览始终在客户端当前窗口内展开,只允许载入当前项目启动的 `127.0.0.1:<port>` 本地 HTTP 预览,不调用系统外部浏览器。
|
||||
中央主视窗明确区分“资源总览画布”“素材创作无限画布”和“运行测试”三个状态。资源总览与素材创作不是两套项目页:前者负责 manifest 资源投影、依赖关系和类型布局,后者负责单个图片素材的无限画布创作;两者共用当前工作台中央区域。正式预览始终在客户端当前窗口内展开,只允许载入当前项目启动的 `127.0.0.1:<port>` 本地 HTTP 预览,不调用系统外部浏览器。
|
||||
|
||||
## 2. 创作工具平台接入声明
|
||||
|
||||
@@ -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 数值微调
|
||||
|
||||
- 数值修改立即写入当前项目的编辑态配置。
|
||||
@@ -83,7 +99,20 @@
|
||||
- 未开放选项使用“视觉不可用但可点击说明原因”,不使用无法触发说明的原生 `disabled`。
|
||||
- 普通用户暂不开放 Agent.md 编辑和自定义 Skill 安装;后续必须先定义来源审核、版本、权限、沙箱和回滚合同。
|
||||
|
||||
### 3.7 现有 Godot 项目
|
||||
### 3.7 主站 UI 对齐与共享视觉边界
|
||||
|
||||
实现状态(2026-08-10):主站与 Tauri 已完成同源 chrome 接入。Tauri 现有中央素材画布直接消费共享动作按钮、工具栏、工具组和分隔符;工作台外围继续保留四区结构,并以平台 token 统一中央壳、Supervisor、Agent Dock、状态提示和主要操作。当前普通用户入口禁用“新增资源”,现有资源“编辑资源”按图片、SVG、视频、音频、文档/代码、Agent 回执和项目版本分流,所有结果均以新 asset 或子版本保存。生成、保存、登录、计费、草稿、manifest、Runtime 和审批语义不因入口分流而改变。
|
||||
|
||||
- 项目工作台继续保留左侧平台导航、中央主视窗、右侧 Project Supervisor 和底部专业 Agent 状态栏四区结构;主站图片编辑器只作为视觉语言和共享画布组件的事实源,不把其素材库侧栏、账号业务或云端项目外壳整体搬入客户端。
|
||||
- 平台主题事实源固定为 `packages/shared/src/theme.css`。画布通用 chrome 固定落在 `@genarrative/image-canvas-react`,主站与 Tauri 必须直接 import 同一组件和作用域样式;客户端不得复制 `src/components/image-editor/`,也不得导入主站完整 `src/index.css`。
|
||||
- 第一批共享 chrome 固定覆盖画布动作按钮、工具栏、工具分组和分隔符。按钮的默认、悬停、键盘焦点、选中、禁用和主次色语义由共享层表达;宿主只提供图标、文案、事件与业务禁用条件。
|
||||
- 主站账号、钱包、服务端项目、云端素材库和生成面板仍留在网站宿主;客户端本地项目、manifest、Runtime、审批、草稿、生成与正式提交仍留在 Tauri 宿主。共享视觉组件不得读取这些业务事实。
|
||||
- 客户端 UI 对齐采用“同视觉、同组件、保留四区布局”,不复制主站整页布局。资源总览、运行视图和 Supervisor 对话继续是 Game Agent 工作台独有语义。
|
||||
- 客户端正式产品仍只按 `1280×800` 横屏合同交付;更窄浏览器样式只负责不崩溃和开发兼容,不改成移动端创作工作台。
|
||||
- 普通用户界面不默认展示内部错误码、开发者 API 配置或本机绝对路径;确需诊断的信息进入受控详情或开发模式,不与主要动作并列。
|
||||
- 素材画布的“素材名称”是用户可编辑的正式输出名称;“资源用途”是 manifest subtype,不向普通用户开放自由文本。新增资源默认“普通游戏美术”,可从普通游戏美术、统一视觉规范、游戏界面原型、核心美术图集四项中选择;精修资源继承源用途且不可改。导出格式继续限定 PNG/JPEG/WebP。工具动作与保存设置分层展示,“保存到项目”在 `1280×800` 和窄容器中都必须完整可见。
|
||||
|
||||
### 3.8 现有 Godot 项目
|
||||
|
||||
- 首页和项目组复用同一个“打开 Godot 项目”动作,用户选择的目录必须包含普通文件 `project.godot`。
|
||||
- 该目录直接成为当前项目根;后续文件读取、修改、命令 cwd、对话、Runtime 和最近项目记录都绑定这个根目录,不复制工程,也不建立 `game/`、`assets/`、`memory/`、`exports/` 平行目录。
|
||||
@@ -95,20 +124,27 @@
|
||||
### 4.1 主视窗
|
||||
|
||||
```text
|
||||
resources
|
||||
resource-overview
|
||||
-> asset-canvas.create(当前临时禁用,不向普通用户开放)
|
||||
-> asset-canvas.refine(静态图片“编辑资源”)
|
||||
-> resource-editor.derive(SVG、视频、音频、文档/代码、Agent 回执“编辑资源”)
|
||||
-> resource-editor.version-branch(项目版本“编辑资源”)
|
||||
-> run(存在 runnableVersion 且 loopback preview 可启动)
|
||||
|
||||
asset-canvas.create|refine
|
||||
-> resource-overview(取消、保留草稿退出或保存投影完成)
|
||||
|
||||
run.playing
|
||||
-> run.paused(用户暂停或切片结束)
|
||||
-> resources(先暂停当前预览表现,再切换视图)
|
||||
-> resource-overview(先暂停当前预览表现,再切换视图)
|
||||
|
||||
run.paused
|
||||
-> run.playing(继续当前切片)
|
||||
-> run.relaunching(数值或版本编辑态发生变化)
|
||||
-> resources
|
||||
-> resource-overview
|
||||
```
|
||||
|
||||
运行入口不可用时仍允许点击,显示“当前无可运行版本”,但不切换状态。
|
||||
运行入口不可用时仍允许点击,显示“当前无可运行版本”,但不切换状态。素材创作的完整 opening/editing/generating/saving/cancelling/failed/recovering 状态、草稿身份和迟到结果门禁以 [`【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`](../technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md) 为准。
|
||||
|
||||
### 4.2 测试切片
|
||||
|
||||
@@ -131,14 +167,17 @@ 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 状态栏保持原位;聚焦容器只包含标题、资源主体、必要元数据与右上角收起按钮,不使用页面级浮层或可拖动标题栏。
|
||||
- 音频:只读取 manifest 已登记音频或已成功导入且登记到 manifest 的附件,按文件签名接受 MP3、WAV、OGG / Opus、M4A、AAC、FLAC;聚焦态展示实际格式、浏览器解码后的时长以及带播放进度和暂停能力的内置播放器。音频任务声明中的未登记路径继续不得读取或播放。
|
||||
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源编辑只允许追加继承源绑定并记录提示词的子版本,不允许原地替换或修改源版本。
|
||||
- 资源聚焦不提供通用工具栏或工具侧边栏;图片聚焦态允许一个明确的“精修资源”业务动作进入素材创作无限画布,该动作不是在聚焦容器中内嵌编辑器或恢复通用工具栏。
|
||||
- 点击资源后,中央主视窗从 `resource-overview.list` 切换为 `resource-overview.focused.document / art / audio / version`,左侧平台导航、右侧 Supervisor 对话和底部 Agent 状态栏保持原位;聚焦容器以路径、类型、来源任务、依赖层级、同类型上下游和版本字段为首屏主体,不使用页面级浮层或可拖动标题栏。文档正文与按意图加载的音频控制位于元数据之后。
|
||||
- 焦点转换以稳定资源 ID 为准。只有从资源列表进入详情或从一个资源 ID 切换到另一个 ID 时聚焦详情 region;同一资源 ID 因 manifest 更新而重新投影时,不得抢走详情内音频 / 视频控件、文档链接或收起按钮的当前焦点。
|
||||
- 显式收起或按 Escape 后恢复进入前的搜索条件、dependency / type 布局模式、资源画布滚动位置和选中资源,并优先把键盘焦点还给原触发资源卡;这些只属于当前前端会话,不写入布局 sidecar。若资源已经被后台删除,必须清理 stale focused / selected ID、关闭详情并把焦点落到“搜索项目资源”,不得落到 `body`。项目切换和进入运行视图必须取消旧项目的焦点恢复意图。
|
||||
- 阶段四只新增上述受控读取与媒体展示;阶段六在同一聚焦容器内补齐正式版本只读展示和引用高亮,但不新增资源聚焦工具栏 / 工具侧边栏,不新增美术编辑、音频编辑 / 替换、资源重新生成、版本替换或运行模块。飞书原需求中“编辑并生成新资源”的条件项仍暂缓,不能只打开画板却缺少回写、`referenceResourceIds` 血缘登记、新资源自动选中与邻近布局的完整闭环。
|
||||
- 资源管理阶段四至阶段七交付上述受控读取、媒体展示、正式版本展示和引用高亮;`2026-08-05` 起图片编辑闭环生效,`2026-08-10` 起全类型非破坏性派生与版本子分支覆盖旧的只读限制。入口必须与草稿或 operation 恢复、正式事务提交、`referenceResourceIds` 血缘、新资源即时投影、两份布局和焦点竞态一次实现,不能只打开一个没有回写的面板。
|
||||
|
||||
### 4.4 历史成果与当前状态
|
||||
|
||||
@@ -152,23 +191,32 @@ idle -> focused(document|art|audio|version) -> idle
|
||||
|
||||
以下合同先冻结字段语义;P0 只实现标注为 P0 的部分。
|
||||
|
||||
### 5.1 工作台视图状态(P0)
|
||||
### 5.1 工作台中央状态(素材创作阶段一目标合同)
|
||||
|
||||
```ts
|
||||
type ProjectWorkbenchViewState = {
|
||||
schemaVersion: 'game-creator-workbench-view.v1';
|
||||
schemaVersion: 'game-creator-workbench-view.v2';
|
||||
projectId: string;
|
||||
mode: 'resources' | 'run';
|
||||
centerState:
|
||||
| { kind: 'resource-overview' }
|
||||
| {
|
||||
kind: 'asset-canvas';
|
||||
sessionId: string;
|
||||
draftId: string;
|
||||
intent: 'create' | 'refine';
|
||||
sourceAssetId: string | null;
|
||||
}
|
||||
| { kind: 'run' };
|
||||
approvalMode: 'strict' | 'risk' | 'none';
|
||||
expandedAgentGroups: Array<'balance' | 'audio' | 'publishing'>;
|
||||
};
|
||||
```
|
||||
|
||||
P0 中 `approvalMode` 只能有效写入 `strict`;其它值只能作为不可用选项展示。
|
||||
旧 `game-creator-workbench-view.v1.mode='resources'` 读取时只映射到 `resource-overview`,`mode='run'` 映射到 `run`;旧状态不能合成 asset-canvas 草稿。`sessionId` 是本次进入流程的短生命周期 UUID,`draftId` 是本地可恢复草稿 UUID。`create` 必须没有源资产,`refine` 必须绑定当前 manifest 中唯一图片资产。`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-11):dependency / type 双模式通过项目内 CAS sidecar 独立持久化;dependency 模式由 Tauri Rust 只读构建关系拓扑与确定性依赖深度、前端 SVG 派生几何,图结构和线段均不写入布局 sidecar。依赖图加载完成前设布局初始化屏障,避免以临时 `dependencyDepth=0` 生成并持久化错误坐标。资源总览当前只允许自动布局与资源卡点击,资源卡手动拖动继续暂缓;素材创作无限画布独立支持 viewport 与图层变换,分区高度/倍率与 sidecar 完全分离。
|
||||
|
||||
```ts
|
||||
type ProjectResourceCanvasLayout = {
|
||||
@@ -251,10 +299,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,21 +320,22 @@ 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)
|
||||
|
||||
实现状态(2026-08-03):当前资源投影已收口到固定的“文档 -> 项目版本 -> 美术资源 -> 音乐音效资源”四区。文档只接收 Markdown / 文本 / JSON / YAML 等正式项目文档和合法 Agent 文本回执;项目版本只接收显式 `ProjectVersionResourceSummary` read model,未知任务产物不得兜底为版本;美术接收图片、SVG、动画和视频类产物;音频只接收 manifest 已登记音频资产或已成功导入并登记到 manifest 的音频附件,任务声明中的未登记音频路径不冒充正式音频资源。无法识别的二进制任务产物和附件不进入资源画布。阶段四已为本地文档、安全 SVG / 扩展图片 / 视频和音频补齐受控读取、中央聚焦、失败空态与媒体播放;这些都是只读表现层,不改变资源投影或 manifest 真相。
|
||||
实现状态(2026-08-10):当前资源投影已收口到固定的“文档 -> 项目版本 -> 美术资源 -> 音乐音效资源”四区。文档接收受支持的 UTF-8 文档/代码和合法 Agent 文本回执;项目版本只接收显式 `ProjectVersionResourceSummary` read model,未知任务产物不得兜底为版本;美术接收图片、SVG、动画和视频类产物;音频接收 manifest 资产、上传登记资产和已完成任务 `artifacts` 明确声明的音频产物。无法识别的二进制任务产物和附件不进入资源画布。受控读取、中央聚焦、失败空态与媒体播放不改变 manifest 真相;编辑成功后只追加新的 asset 或版本子记录。
|
||||
|
||||
资源身份固定使用 manifest asset ID、正式 version ID、Agent ID + run ID 或已导入资源稳定路径;显示标题、来源文案变化不得改变 `resourceId`,从而避免布局、依赖边、选择和聚焦状态因改名失效。
|
||||
|
||||
@@ -394,14 +444,15 @@ type ProjectAgentMudPointAttribution = {
|
||||
- 当前 run 专业状态与项目历史成果分离。
|
||||
- 默认三专业组,并可展开另外三组。
|
||||
- 严格审批有效;风险/无需审批可点击查看未开放原因。
|
||||
- 橙色低保真视觉与 `1280×800` 横屏边界。
|
||||
- 与主站图片编辑器一致的平台轻色主题、共享画布 chrome 与 `1280×800` 横屏边界。
|
||||
|
||||
### P1
|
||||
|
||||
- 已实施依赖/类型两套坐标持久化、首次默认不重叠布局、历史坐标跨重启恢复与自动协调 CAS 冲突处理;资源卡手动拖动暂缓。
|
||||
- 资源关系线在布局持久化验收通过后单独实施,不与本切片捆绑伪造完成。
|
||||
- 已实施正式版本只读模型、版本卡、父子关系与引用资源高亮;资源兼容性判断和不可变下一迭代版本创建仍待后续切片。
|
||||
- 美术/音频编辑状态接线。
|
||||
- 已实施正式版本不可变模型、版本卡、父子关系与引用资源高亮;“编辑资源”可追加继承源绑定并记录提示词的子版本,资源直接替换、运行版本切换和兼容性迁移仍待后续切片。
|
||||
- 素材创作无限画布阶段一按权威专题一次交付图片导入、编辑、生成、导出、草稿恢复、正式本地回写、即时投影和焦点竞态闭环。
|
||||
- 高级抠图、图集、角色动画、视频时间线编辑和音频波形级编辑按后续切片实施;当前视频走源引用派生,音频只做语义重制。
|
||||
|
||||
### P2
|
||||
|
||||
@@ -439,37 +490,81 @@ 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 正式项目版本阶段六验收
|
||||
|
||||
1. manifest 缺少 `versions` 时旧项目正常打开且不显示伪造版本;存在合法记录时,固定“项目版本”分区按追加顺序显示稳定版本卡。
|
||||
2. 根版本、父版本和直接子版本关系在卡片或聚焦态可见;悬空父版本、自引用、重复 ID、非递增修订、倒退时间、重复 slot 和超限数字均失败关闭。
|
||||
3. 点击版本卡后,当前 manifest 中仍存在的绑定资产卡被高亮;历史已删除资产只在版本详情保留 ID,不创建幽灵卡,也不把 External Editor resource ID 猜成 manifest asset ID。
|
||||
4. 版本聚焦态只读展示身份、修订、创建原因、父子关系、创建时间和 slot 绑定,不提供编辑、替换、切换、回滚或运行按钮。
|
||||
4. 版本聚焦态展示身份、修订、创建原因、父子关系、创建时间和 slot 绑定;“编辑资源”只追加继承源绑定并记录提示词的子版本,不提供原地替换、切换、回滚或运行按钮。
|
||||
5. 任意现有 manifest 写入只能保留磁盘版本前缀并追加新记录;存储边界以跨进程专用锁串行覆盖旧状态读取、前缀校验、安装和回读,修改、删除、重排或并发旧快照覆盖已有版本时写入失败。
|
||||
6. 版本选择和高亮不写 manifest、布局 sidecar 或 project revision;dependency / type 两种布局都可显示绑定高亮,既有依赖关系 SVG 语义不变。
|
||||
|
||||
### 7.5 阶段七完整验收
|
||||
### 7.5 资源分区独立高度验收
|
||||
|
||||
1. 对照飞书需求、当前 PRD、技术方案、代码、测试与阶段提交复核阶段零至阶段六;美术编辑生成新资源继续按本 PRD 已确认的闭环条件暂缓,不作为遗漏或伪完成。
|
||||
2. AppSurface 同时覆盖文档、图片、SVG、音频和视频聚焦;视频必须使用原生 `controls` 且 `preload="metadata"`,读取策略失败时中央主视窗显示安全空态,右侧对话和底部 Agent 状态栏继续存在。
|
||||
3. `1280×800` 应用内浏览器实测 `window`、document 与 body 均无页面级横向或纵向溢出。浏览器开发页受真实登录门禁保护,不为验收绕过认证或伪造 Tauri;工作台内部结构由 AppSurface 集成测试与资源布局 CSS 合同测试复核。
|
||||
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、技术方案、代码、测试与阶段提交复核资源管理阶段零至阶段六及素材创作阶段;图片、SVG 和视频主体只在资源卡中展示,中央详情以元数据和依赖信息为主,文档正文与按意图读取的音频控件保留。
|
||||
2. AppSurface 覆盖资源卡媒体预览、资源聚焦、素材创作无限画布、全类型非破坏性编辑、恢复 modal、右侧对话和底部 Agent 状态栏;视频使用 `controls` 与 `preload="metadata"`,读取失败显示安全空态。
|
||||
3. 预览调度、布局 scope、CAS、项目切换和 `1280×800` 无页面溢出均须通过对应定向测试、真实登录门禁和编码/diff 门禁验证。
|
||||
4. 根目录全量 Vitest、前后端 typecheck / lint / build、Rust workspace test / check、SpacetimeDB schema、原生壳、内容 / 编码、生产运维与部署门禁全部通过后,阶段七才允许提交。
|
||||
5. 本地 `.env`、`.env.local`、密钥、缓存、日志和构建产物不进入阶段七提交;提交前再次执行编码检查和 `git diff --check`。
|
||||
|
||||
### 7.6 素材创作无限画布阶段一至五最终验收
|
||||
|
||||
实现状态(2026-08-11):当前产品切片禁用“新增资源”,只从现有资源进入非破坏性编辑。图片进入中央 refine 画布,其他现役类型进入统一派生编辑壳;取消恢复、正式 manifest/revision 实时合并、依赖图重建、dependency/type 双布局协调和三阶段自动定位已经接通。command/event 任意顺序按项目、commit、event 与 revision 去重;低 revision、旧 graph/layout 和失效 focus generation 均不能倒灌。Tauri 远端媒体编辑固定使用 AppData 私有 `editorApi.baseUrl/apiKey` 访问 `/api/external/v1/*`,不把 Key 传入 WebView、项目事实、账本、日志或普通错误;画布 UI 不提供 Base URL/API Key 输入。重启恢复只继续原 operation,不以新请求、新幂等键或新 operationId 替代结果未知的旧任务。
|
||||
|
||||
1. 网站与 Tauri 实际 import 同一份 `@genarrative/image-canvas-core` 和 `@genarrative/image-canvas-react`,客户端没有复制的主站画布目录;viewport、selection、变换、renderer 与 history 算法位于共享层,宿主只保留事件接线与 adapter 副作用。
|
||||
2. “新增资源”在当前产品切片中保持禁用;“编辑资源”只接受现有资源。图片 refine 保留原资产与原文件、创建新资产,并用规范 `referenceResourceIds` 登记直接血缘;其他类型同样只追加派生文件/asset 或子版本,不覆盖、删除或重排源记录。
|
||||
3. 草稿 schema、revision、容量、项目身份、OS 锁、CAS、恢复副本和媒体引用符合权威专题;损坏、未知 schema、身份错配和超限均失败关闭。应用重启后必须按 `projectId + intent + sourceAssetId + active status` 从正式 sidecar 唯一发现原 refine 草稿并保留 `draftId`、生成新 `sessionId`;零条才允许创建,多条必须进入对账,不得依赖进程内 Map 或按时间猜测。
|
||||
4. 正式提交携带 `expectedProjectId + expectedRevision + expectedDraftRevision + commitId + idempotencyKey`,按事务快照/journal/ledger、文件、manifest/revision、回读、ledger/draft、事件顺序完成;首个快照、全部快照、journal 写入、文件、manifest、revision、验证和 ledger 各崩溃阶段均有确定结果,未发布快照残留只在证明正式文件、manifest 与 revision 均未变化时清理。
|
||||
5. 保存成功后不刷新、不重开项目即可进入 manifest 投影、依赖图、dependency/type 布局和允许时的选中定位;切项目、切中央状态、改选择或改搜索后的迟到结果不得抢焦点。
|
||||
6. 搜索/筛选隐藏新资源时保留条件,明确提示“新资源已保存,当前筛选条件下不可见”,只通过显式动作清除条件并定位。
|
||||
7. 现有资源编辑、生成、保存、取消、失败和恢复必须覆盖权威专题 §13 中与当前非破坏性编辑切片对应的验收矩阵;只完成画布 UI 或只完成本地写文件都不能算正式闭环。
|
||||
8. 自动定位必须分别证明资源已投影、dependency/type 两份布局都 settled 且存在目标位置、目标卡 DOM 已提交;搜索隐藏走显式清除/定位,任何 commit 最多自动聚焦一次。
|
||||
9. Tauri 远端媒体编辑只使用发布 AppData 私有 External v1 配置;后端按 owner 预扣/退款泥点并返回可轮询 operation。凭据失效、余额不足、平台生成配置故障和远端失败必须在画布内可见;生成状态不得因固定高度或 `overflow` 裁剪而消失,终态后刷新钱包余额。
|
||||
10. 素材画布生成账本的服务身份固定为 `service-origin-v1`,绑定规范化 External base URL 的服务指纹,不绑定 Developer API Key;确认面板只展示去除路径与凭据的服务 origin。无法用当前 Key 验证的升级前 Key-bound 账本在任何网络动作前显示当前服务 origin 并要求用户确认;确认挑战过期、账本变化或 base URL 变化均失败关闭。确认后 `accepted/running` 只恢复原 GET,`prepared` 只可精确重放冻结的原 POST。全类型资源编辑复用相同显式确认迁移:旧 Key-bound 指纹无法验证时确认前保持零网络动作,确认后已存在 `operationId` 的任务只 GET 原 operation,尚未受理的冻结请求才可复用原幂等键精确 POST。
|
||||
11. 资源编辑恢复面板必须为独立 modal,展示后端权威队列的全部 operation。用户可继续任意可恢复项;`remote-failed` 只允许显式移出活动队列,并保留私有账本审计;`reconciliation-required` 只读展示对账。读取失败必须提供重试,不得伪装空队列;操作后必须重读后端。
|
||||
12. `remote-failed` 已是远端明确终态,重启后不再 POST、不再轮询、不再扣费;`archived` 仅表示用户已将它移出活动恢复队列,不等于 `committed`。`result-unknown`、鉴权临时失败和 `reconciliation-required` 均不允许归档或重新生成。
|
||||
13. 派生资产提交必须通过 durable asset transaction journal 串起最终文件、manifest 和 project revision。任一崩溃阶段恢复后只有一份派生文件、一条 manifest asset,revision 精确推进一次;manifest 已写而 revision 未写时只前向补 revision,无法证明的组合进入人工对账。journal 已证明目标 asset、媒体和 target revision 写入后,即使后续合法提交继续推进 manifest/revision,也应按目标 asset 精确身份与 `currentRevision >= targetRevision` 补齐 ledger/draft,不得要求整个 manifest 永远等于历史 after 快照。durable committed 后遗留的 staging 只有在 staging/正式媒体摘要一致,且 manifest 中按 asset ID 或路径唯一命中并与 journal asset 精确相等时才尽力删除;删除失败不降级已提交结果,身份或媒体漂移则保留 staging 并进入对账。
|
||||
14. generation progress、草稿保存队列、生成/提交回包与延迟 `loadDraft` 必须共用单调 revision 门禁,低 revision 不得覆盖已落地的新草稿。Shift 指针与键盘选择必须与共享 core 一致;单选自身 Shift 不能清空选择,已选多图层普通指针拖动应保持并同步移动选择集。零位移不得产生 undo、documentVersion 或草稿保存。
|
||||
15. 失败 UI 必须按 `generation / draft-save / asset-commit / recovery / cancellation` 五类 operation 显示可访问名称与安全动作。只有生成失败可以保留参数并“返回修改/重新确认”;草稿、CAS、提交、恢复和取消故障不得出现会发起新生成的按钮。任何非 `editing` 生命周期都必须同时禁用或 inert 背景画布/顶部工具栏,并由生成 handler 再次校验当前状态;modal 遮罩、视觉 disabled 或旧闭包都不能充当业务门禁。
|
||||
16. 派生子版本 journal 必须冻结 project revision before/after 身份和目标 after 记录。manifest 已有目标子版本但 journal 缺失时失败关闭;旧 journal 缺少 revision 身份且当前 revision 已推进、无法证明是同一事务写入时进入人工对账,不得把现状猜测为已提交。
|
||||
17. 文本、SVG 与 Agent 回执编辑必须在调用 Provider 前持久化 request-issued;Provider 成功正文必须在解析、格式校验和 staging 之前原子写入私有 durable handoff,并绑定原 operation、请求指纹和内容摘要。issued 后缺少可信 handoff 只能对账,已有可信 handoff 则只消费原正文,两者都不得再次调用 Provider。401/403 只表示当前凭据不能继续授权,不能把已受理 operation 改写为永久对账;修正 Key 后仍只查询原 operation。
|
||||
|
||||
### 7.7 主站 UI 对齐验收
|
||||
|
||||
1. `packages/image-canvas-react` 暴露共享画布动作按钮、工具栏、工具分组和分隔符;主站图片编辑器直接消费这些组件,旧网站组件只允许保留薄适配,不得继续维护另一份按钮可访问性或选中态实现。
|
||||
2. 共享样式全部使用 `.genarrative-image-canvas*` 作用域并消费 `--platform-*` / `--image-canvas-brand-*` token;Tauri 不导入主站完整 `src/index.css`,主站也不复制共享样式回业务 CSS。
|
||||
3. 客户端接入完成后,`1280×800` 下中央画布工具栏保持单行或受控横向收纳,中文动作不得逐字换行;聊天输入、主要保存动作和底部 Agent Dock 始终可见,document/body 不产生页面级溢出。
|
||||
4. 主站现有图片编辑器的按钮名称、tool selection、hover/focus/pressed/disabled 行为与工具栏位置不因共享抽取回归;网站账号、钱包、素材库、生成和导出逻辑不下沉共享层。
|
||||
5. 客户端生成、保存、取消、恢复、登录失效与余额不足只更换视觉承载,不改变 Host Port 调用、幂等身份、草稿 CAS、manifest/revision、Runtime 审批或钱包刷新语义。
|
||||
6. 阶段一、二冻结合同并让主站消费共享 chrome;2026-08-06 的阶段三至五已经完成客户端中央区、Supervisor 和 Agent Dock 的正式换肤、定向测试与 `1280×800` 验收。独立 Vite 没有登录会话时继续显示真实登录门禁,不为视觉测试增加绕过入口。
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
- 当前收口不实现资源卡手动拖动,也不实现资源聚焦工具栏、资源聚焦工具侧边栏、美术编辑、音频编辑 / 替换、资源重新生成、资源替换、下一迭代版本创建入口、运行版本切换、版本回滚、运行模块扩展、测试切片、运行态消费版本、数值参数或泥点归因。正式版本记录已经成为 manifest 业务真相,但当前只读取、校验和展示已有记录。
|
||||
- 本切片不持久化资源聚焦状态、画布缩放 / 平移、搜索条件、筛选条件或当前 mode;聚焦退出时的列表上下文恢复只限当前前端会话,这些状态如需跨重启保存必须另行扩展合同,不能塞入 `game-creator-resource-layout.v1`。
|
||||
- 当前不开放“新增资源”产品入口;现有资源只允许非破坏性编辑并追加新文件、asset 或子版本,源记录不覆盖、删除或重排。
|
||||
- 资源总览不实现资源卡手动拖动、通用聚焦工具栏/侧边栏、资源直接替换、运行版本切换、版本回滚、运行模块扩展、测试切片、运行态消费版本、数值参数或泥点归因。
|
||||
- 素材创作不实现高级蒙版/毛发级抠图、图集、角色动画、视频时间线或音频波形级编辑;图片画布提供现役编辑闭环,其他类型使用统一非破坏性派生面板。
|
||||
- 资源聚焦、搜索、筛选、dependency/type mode 与分区滚动/倍率均为当前会话态;素材创作 viewport 和图层状态使用独立草稿 schema,不能混用资源总览 sidecar。
|
||||
- 不修改 SpacetimeDB schema。
|
||||
- 不开放普通用户 Agent.md/Skill。
|
||||
- 不自动确认 Agent 动作,不自动触发可能扣费的生成。
|
||||
|
||||
@@ -1,5 +1,135 @@
|
||||
# 决策记录
|
||||
|
||||
## 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;单端离屏显示边界继续线,两端离屏隐藏。
|
||||
- 兼容与边界:不修改 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`。
|
||||
- 关联文档:`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-11 Tauri 无限画布以可证明事务和分类恢复收口
|
||||
|
||||
- 终态与队列:资源编辑账本正式区分可继续阶段、`reconciliation-required`、`remote-failed` 和 `archived`。远端明确失败只保存稳定分类与终态时间,不得再 POST、轮询或重新扣费;只有该终态能由用户显式归档并移出活动恢复队列,归档保留账本且不伪装 `committed`。结果未知和需对账项继续失败关闭。
|
||||
- 本地提交:派生 asset 提交新增 `game-creator-resource-edit-asset-transaction.v1` durable journal,冻结 operation/project/source、最终路径/摘要、manifest before/after 和 project revision before/after,以 `prepared -> media-installed -> manifest-written -> revision-written -> committed` 推进。只有文件、manifest、revision 与 journal 全部回读相等才提交 ledger;manifest after/revision before 只前向补 revision,无法证明的组合进入对账,不做猜测回滚。durable committed 后遗留 staging 只有在 staging 与正式媒体摘要一致、manifest 按 asset ID 或路径唯一命中且精确等于 journal asset 时才尽力清理;删除 I/O 失败不改变 committed,身份或媒体漂移则保留 staging,并把 journal 与 ledger 转入对账。
|
||||
- 版本提交:派生子版本 journal 冻结 project revision before/after 摘要和目标 after 记录。manifest 已有目标子版本但 journal 缺失时失败关闭;旧 journal 缺少 revision 身份时,只有当前 revision 仍为 base 才允许补齐身份,revision 已推进且无法证明由同一事务写入时必须进入 `reconciliation-required`;缺少上述 journal 证明时,不得仅以子版本存在或 revision 数值已到达推断 committed。
|
||||
- 服务身份:新生成账本统一写 `service-origin-v1`,绑定规范化 External base URL 的服务指纹,Developer API Key 只负责授权而不拥有 operation;确认面板只展示去除路径与凭据的服务 origin。升级前 Key-bound 指纹可被当前 Key 精确验证时自动迁移;对素材画布生成账本,无法验证时在任何网络动作前显示脱敏服务 origin 并要求用户确认带有期限且绑定账本快照的挑战值,确认后已受理任务只恢复原 GET,prepared 任务只精确重放冻结 POST。全类型资源编辑复用同一身份分类,旧指纹无法验证时保留原 operation 进入对账,不自动接管或重放。
|
||||
- 前端一致性:generation progress、保存队列、生成/提交回包和延迟草稿读取共用单调 revision 门禁;当前 scope 低 revision 不得回退已落地草稿,同 revision 只接受完整相等的幂等回包。Tauri 指针与键盘 Shift 选择共用 `resolveLayerPointerSelection`;移动、缩放和平移只在首次真实变化时 capture history,零位移不生成 undo、documentVersion 或保存。
|
||||
- 交互恢复:工作台用独立 modal 展示全部后端权威 operation,允许选择任意可恢复项;`remote-failed` 只提供归档,`reconciliation-required` 只读展示。读取失败显式重试,操作后重读后端,项目切换后丢弃迟到结果。`canvas.failed` 统一携带 `generation / draft-save / asset-commit / recovery / cancellation` 五类 operation,只有生成失败显示“返回修改/重新确认”。
|
||||
- 产品语义:当前仍禁用“新增资源”,只对现有资源做非破坏性派生编辑;新结果追加为新文件、asset 或子版本,源资源、源文件和原版本保留不变。
|
||||
|
||||
## 2026-08-10 Tauri 客户端远端资源编辑固定使用 External v1
|
||||
|
||||
- 产品边界:主站网页画布继续使用登录态 `/api/editor/*`、`/api/assets/*` 与 `/api/runtime/external-generation/jobs/*`;AI 游戏创作 Tauri 客户端没有网页画布宿主,图片、图片引用、视频、音效和背景音乐等远端媒体编辑固定使用 `/api/external/v1/*`。两条入口继续复用相同请求 DTO、owner 归属、统一生成队列和正式资产结果,不新建平行生成服务。
|
||||
- 凭据边界:客户端沿用发布 AppData 私有运行时配置中的 `editorApi.baseUrl/apiKey`,不实现登录后自动签发 Developer API Key,不把站内 Access Token 传给 Tauri 生成命令,也不把 API Key 打包进仓库、传入 WebView、写入项目、账本、日志或普通错误。Key 缺失投影安全配置错误,`401/403` 投影 External 凭据无效或权限不足,不再描述为站内登录失效。
|
||||
- 路由边界:图片编辑、视频、音效和 BGM 分别使用 `/api/external/v1/editor/images/edits`、`/api/external/v1/editor/videos/generations`、`/api/external/v1/editor/audios/sound-effects/generations` 与 `/api/external/v1/editor/audios/background-music/generations`;上传、确认、轮询和换签固定使用 External v1 对应端点。SVG、UTF-8 文档、代码、Agent 回执与项目版本仍是本地派生,不制造无意义的远端请求。
|
||||
- 可靠性边界:External POST 固定携带原稳定 `Idempotency-Key` 并只接受 `202 + operationId`;`prepared` 只重放原正文和原键,`accepted/running` 只查询 `/api/external/v1/generations/{operationId}`。账本使用 `service-origin-v1` 绑定规范化 External base URL 的服务指纹,不绑定或保存明文 Key;升级前 Key-bound 素材画布生成账本无法用当前 Key 验证时,必须由用户显式确认面板中展示的当前 origin 才能恢复冻结请求;全类型资源编辑的旧指纹无法验证时保留原 operation 对账。升级前已保存的站内 endpoint 只保留为待对账状态,禁止拿 External Key 自动重放。本地参考媒体继续严格执行 ticket → OSS form → confirm,结果只消费稳定 `objectKey/resource/asset`,旧资源保留且派生资源追加。
|
||||
- 关联:`apps/ai-game-creator-shell/src/features/asset-canvas/tauriImageCanvasHostAdapter.ts`、`apps/ai-game-creator-shell/src/view/project-development/resourceEditModel.ts`、`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/src-tauri/src/project/asset_canvas/generation.rs`、`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`。
|
||||
|
||||
## 2026-08-10 客户端参考媒体直传复用已授权私有前缀
|
||||
|
||||
- 根因:客户端素材画布和全类型资源编辑把内部用途目录 `asset-canvas-references`、`resource-editor-references` 直接作为 `legacyPrefix`;api-server 只接受 `platform-oss` 权威白名单,因此请求在票据阶段返回 `400`,OSS 上传、对象确认、生成提交和扣费都没有发生。
|
||||
- 存储决策:继续复用合法私有前缀 `generated-character-drafts`,不扩大 legacy 白名单、不增加平行上传接口。图片画布使用 `editor/asset-canvas-references/<projectId>/<draftId>/<generationId>`,全类型资源编辑使用 `editor/resource-editor-references/<projectId>/<operationId>` 作为 `pathSegments`;生成请求只消费 confirm 后的稳定 `objectKey`。
|
||||
- 错误边界:图片参考资源准备分别投影 `reference-material-invalid`、`reference-ticket-failed`、`reference-object-upload-failed`、`reference-confirm-failed`;`401/403` 继续收敛为 `authentication-required`。这些错误只返回安全阶段,不暴露 ticket host、formFields、policy、signature、Token、API Key 或 Provider 内部正文;取得稳定对象前必须保持 `operationId` 和生成 request body 为空。
|
||||
- 验证:客户端端到端夹具锁定 ticket → OSS form POST → object confirm → image edit POST 顺序,并断言源图片文件和 manifest asset 保留、派生图片追加;失败夹具锁定票据失败后零生成 POST。api-server 契约测试锁定精确前缀、目录和私有对象 key。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/asset_canvas/generation.rs`、`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`、`server-rs/crates/api-server/src/assets.rs`、`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`。
|
||||
|
||||
## 2026-08-10 客户端现有资源编辑扩展到全部现役类型
|
||||
|
||||
- 产品入口:继续禁用“新增资源”,资源聚焦态的 manifest asset、已完成任务产物、上传附件、Agent 文本回执和项目版本统一显示“编辑资源”。静态图片复用 refine 图片画布,其他类型进入同一资源编辑壳,不建立平行资源总览。
|
||||
- 非破坏性边界:图片、SVG、视频、音频、文档和代码生成新的本地文件与 manifest asset;Agent 原回执保持不变,派生文档引用回执身份;版本只追加继承资源绑定的子版本。任何路径都不得覆盖、删除或重排源记录。
|
||||
- 能力分流:SVG/文本/代码走结构化 LLM 内容派生与格式复核;视频使用源视频稳定引用;音效/BGM 因现役接口无源音频字段,固定定义为基于源语义的派生重制,不能宣称波形级编辑;项目版本追加子版本。
|
||||
- 信任边界:前端能力提示不是授权事实,Tauri 在提交前按 manifest、已完成任务或上传登记重新核验来源并复核项目 revision / 源内容摘要。远端生成继续使用稳定幂等身份、`202` 轮询与稳定 object/resource/asset 身份,签名 URL 不落 manifest。
|
||||
- 队列与结果边界:Tauri 媒体派生统一经 External v1 进入现役生成队列;图片 refine、视频、音效和 BGM 复用同一逻辑请求的稳定 `Idempotency-Key`,路由不得丢弃。完成结果只返回裁剪后的稳定 object/resource/asset 引用与必要媒体元数据,不暴露 provider、worker、队列内部字段或临时签名 URL。
|
||||
- 验证边界:资源投影回归必须把已完成任务在 `artifacts` 中明确登记的音频归入音乐音效资源,同时继续排除未登记音频和未知二进制;画布工具栏数量断言必须与全部现役工具清单同步;文本预览回归必须覆盖 HTML 与现役代码扩展名;委派回执测试应在显式进入等待态前完成同 action 幂等断言,避免后台 parent-wake 与断言竞争。
|
||||
- 关联:`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`、`apps/ai-game-creator-shell/src/view/project-development/resourceEditModel.ts`、`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`。
|
||||
|
||||
## 2026-08-10 客户端素材画布收敛为现有图片非破坏性编辑
|
||||
|
||||
- 产品决策:资源总览“新增资源”禁用,普通用户只从现有图片资源进入“编辑资源”。
|
||||
- 编辑语义:refine 自动绑定源图片,Tauri 通过现役 `/api/external/v1/editor/images/edits` 派生新结果;结果创建独立 asset 和文件,源 asset/文件保持不变,新资源以 `referenceResourceIds` 登记源资源血缘。
|
||||
- 可靠性决策:公开生成状态每次落盘推进草稿 revision,并同步私有账本、进度事件、staging 和 commit;已受理或结果未知的 operation 遇到鉴权失效时保留原 operation。Developer API Key 轮换不创建替代任务;升级前 Key-bound 指纹无法直接验证时通过显式服务 origin 确认后继续原 operation。
|
||||
- 交互决策:dirty 返回必须通过独立确认面板选择保留或放弃,默认保留并先 flush;图层选择和缩放同时提供指针与键盘路径,禁止嵌套交互元素。
|
||||
- 关联:`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`、`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx`、`apps/ai-game-creator-shell/src-tauri/src/project/asset_canvas/generation.rs`。
|
||||
|
||||
## 2026-08-06 Game Agent 画布视觉以主站 token 与共享 chrome 为唯一来源
|
||||
|
||||
- 背景:网站与 Tauri 已经共同消费 `image-canvas-core/react` 的 viewport、selection、renderer 和 history,但客户端素材画布仍维护独立的文字工具栏、按钮和状态外观,`1280×800` 下会出现动作逐字换行、主次不清和工作台四区视觉漂移。直接复制主站 `src/components/image-editor` 或整包 `src/index.css` 会重新形成宿主分叉和隐式全局依赖。
|
||||
- 决策:保留 Game Agent 左导航、中央主视窗、右侧 Supervisor 和底部 Agent Dock 四区布局;平台颜色、边框、文本和 surface 继续以 `packages/shared/src/theme.css` 为事实源,画布动作按钮、工具栏、分组与分隔符进入现有 `@genarrative/image-canvas-react`。主站和 Tauri 均直接消费同一 chrome,Tauri 宿主只保留业务接线和布局适配。
|
||||
- 依赖边界:共享组件只接受 React props、图标节点、短文案和事件,不读取账号、钱包、项目、生成、Tauri 或 HTTP。主站 `EditorIconButton` 只保留 Lucide/特殊浮层薄适配,主站业务 CSS 只保留定位和专属状态;Tauri 禁止导入网站完整 CSS。内部错误码、开发者 API 配置和本机绝对路径不作为普通用户默认视觉内容。
|
||||
- 分期:阶段一、二更新权威合同、抽取共享 chrome 并让主站消费;阶段三、四已统一中央素材画布、Supervisor 与 Agent Dock;阶段五在同一变更中补齐测试、真实视口测量和发现回归修复。
|
||||
- 保存字段:`name` 属于用户输出命名,继续可编辑;`assetKind` 属于机器 subtype,create 只允许 Runtime 权威目录中的 `game-art / icon-spec / ui-prototype / art-spritesheet` 并显示中文用途,refine 继承源 kind 且锁定。未知历史 kind 只按原值保存,不在精修中迁移或改写。工具与保存设置显式分为两行,主保存动作不可压缩。
|
||||
- 路径展示:业务内部继续持有和校验绝对项目路径,普通工作区状态只投影项目名称;显式目录选择、非空目录确认和开发诊断仍可按权限边界使用真实路径。该变化不修改项目身份或 Tauri command 参数。
|
||||
- 验证:共享 chrome 与 Tauri Surface 定向测试 `20/20` 通过,覆盖可访问性、pressed/expanded/disabled、工具组/分隔符、生成/失败/保存/取消和非裁剪 CSS;AppSurface 横屏合同 `1/1` 通过,锁定 Supervisor composer、Agent Dock 与 `100dvh` 两行工作台。客户端/全仓 typecheck、定向 ESLint、编码和 diff 检查通过。完整 AppSurface 本次运行是 `352/354` 通过:preview 权限用例单跑通过,属于全量异步顺序波动;仍失败的“画板 API Key”运行配置用例不涉及本次 diff,留在原业务范围处理。应用内浏览器 `1280×800` 实测 window/document/body client 与 scroll 均为 `1280×800`;真实入口没有登录会话时保持登录门禁,不增加测试绕过。
|
||||
- 关联:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`、`packages/image-canvas-react/`。
|
||||
|
||||
## 2026-08-05 无限画布阶段三以 Tauri 持久草稿和项目 mutation 事务形成独立图片闭环
|
||||
|
||||
- 决策:阶段三新增独立 `AssetCanvasSurface` 和 Tauri Host Adapter,直接消费共享 core/React/ports;前端状态固定为 `canvas.creating / editing / saving / recovering / failed`,generation 明确使用 unsupported mock。本阶段不接资源总览入口、保存后自动选中或真实 AI,避免提前修改工作台状态合同。
|
||||
- 草稿边界:`.agent/workbench/asset-canvas/` 使用持久 OS 文件锁和 draft revision CAS,不使用 React state、localStorage 或进程内 mutex 作为正式状态。主草稿损坏时,固定 recovery JSON 必须通过针对实际字节的 SHA-256、schema/身份和全部受控媒体复核才可恢复;连续保存产生的 revision 直接从 CAS 结果推进。
|
||||
- 提交边界:正式提交复用项目 mutation 写锁,锁内按身份/revision 重检、prepared journal/ledger、最终图片安装、manifest、project revision、回读验证、ledger/草稿终态推进。`commitId/idempotencyKey` 与完整请求指纹绑定;同请求重放返回同一 asset/event,同 revision 竞争不得覆盖。释放锁后才发布至少一次 Tauri event,emit 失败不回滚已提交 manifest。
|
||||
- 恢复边界:最终文件分为 absent、matches、mismatch。只有 absent 或摘要/大小/尺寸完全匹配时可自动回滚;mismatch 保留现场并标记 `reconciliation-required`。manifest after/revision before 前向补 revision,二者 after 时补 ledger、草稿和事件。只能删除能证明属于当前事务的新文件。
|
||||
- 精修与异步边界:refine 源必须是 manifest 唯一登记且通过签名、完整解码、容量、普通文件、符号链接/硬链接门禁的图片;源 asset/文件永久保留,新 `canvas-<commitId>` 通过规范 resourceId 引用源。前端以 project/draft/session epoch 丢弃迟到 Promise/事件,以 eventId 去重 command/event 投影。
|
||||
- 验证:阶段三 Surface `8/8`、共享 core/React `7/7`、AppSurface `351/351`、Rust 素材画布定向测试 `11/11`,并执行客户端 typecheck、Rust check、编码与 diff 门禁。
|
||||
- 关联:`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-05 无限画布阶段二以共享包成为网站与 Tauri 唯一核心源码
|
||||
|
||||
- 决策:把现役网站画布的纯交互模型、history/viewport/stage hooks 和通用 React 容器迁到 `packages/image-canvas-core`、`packages/image-canvas-react`;原 `src/components/image-editor` 同名模型/hook 只保留 re-export。网站 Stage/World/Dock/Portal 已直接使用共享 React,Tauri 壳只建立共享源码编译入口和作用域 CSS,不在本阶段接素材创作业务闭环或修改资源总览。
|
||||
- 依赖边界:core 只含纯 TypeScript;React 包只依赖 core、React/ReactDOM 和注入端口。project、asset、generation、completion 四组 ports 不知道 auth、wallet、payment、routing、`editorProjectClient` 或 Tauri `invoke/listen`。网站账号、计费、云端项目、素材库、上传、生成、媒体换签与业务面板继续留在网站 adapter/renderer 边界。
|
||||
- 宿主构建:根站与 AI 游戏创作壳分别 alias 同一 `packages/` 源码,Vite 去重 React/ReactDOM;Tauri dev server 显式允许 repo root。共享 CSS 只使用 `.genarrative-image-canvas` 作用域,Tauri 不导入网站 `src/index.css`。
|
||||
- 实例隔离:小地图优先在当前 viewport 内解析,多实例时禁止全局取第一个;wheel、ResizeObserver、animation frame 与 portal 均按实例挂载清理。重复 mount/unmount 和双画布隔离由共享 React 测试锁定。
|
||||
- 当前未解耦:网站 `ImageCanvasEditorView` 仍负责完整业务编排,媒体换签、视频/音频/序列帧 renderer、Agent 与生成表单未下沉;旧 `ImageCanvasEditorTypes` 和 `ImageCanvasEditorModel` 仍含网站扩展类型与序列化/素材规则。后续接 Tauri 时应通过 ports 和 renderer 插槽继续缩小这些边界,禁止把该目录整体搬入客户端。
|
||||
- 验证:共享 core/React 7 项测试与现役 ImageCanvas 74 个文件、892 项测试通过;网站和 AI 游戏创作壳 typecheck 通过。构建、编码和 diff 门禁以本次任务最终记录为准。
|
||||
- 关联:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`。
|
||||
|
||||
## 2026-08-05 素材创作无限画布共享源码并以本地事务形成图片闭环
|
||||
|
||||
- 背景:资源管理阶段七已经完成 manifest 实时投影、dependency/type 布局、依赖图和中央只读聚焦,但“新增资源/精修资源”仍缺少草稿、生成、正式回写、血缘、布局和焦点竞态的完整合同。网站已有成熟图片画布,若直接复制进 Tauri 会形成两份长期分叉的画布内核和通用 UI。
|
||||
- 产品决策:项目工作台中央主视窗把 `resource-overview` 与 `asset-canvas(create|refine)` 建模为两个状态。“新增资源”和“精修资源”进入素材画布,资源总览卡片继续不可拖动,素材画布图片图层必须支持平移/缩放、选择/移动/缩放、撤销重做、导入、基础编辑、生成、导出和本地回写。首版只正式闭环图片;高级抠图、图集、角色动画、视频和音频编辑后续分期。
|
||||
- 架构决策:现役网站画布抽取到 `packages/image-canvas-core` 与 `packages/image-canvas-react`,网站和 Tauri 实际 import 同一源码,通过 Web/Tauri Host Port 注入差异。Web 保留账户、钱包、服务端 editor project 和云端素材库;Tauri 保留本地项目、受控文件、manifest、项目 revision 和 External Editor API。禁止复制整个 `src/components/image-editor/` 到客户端。
|
||||
- 持久化决策:Tauri 草稿使用 `.agent/workbench/asset-canvas/` 下的 `game-creator-asset-canvas-draft.v1`,以 `expectedProjectId + expectedDraftRevision` 在 OS 句柄锁内 CAS;JSON 最大 `2 MiB`、最多 `4096` 层,媒体只保存受控引用。正式 `commit_local_project_asset` 同时绑定项目 `expectedRevision`、草稿 revision、`commitId/idempotencyKey` 和 staging 摘要,按 prepared journal、最终图片、manifest/revision 可恢复更新、回读、ledger/草稿、最后 Tauri event 推进;事件 `game-creator-local-asset-committed` 至少一次并按固定 eventId 去重。
|
||||
- 血缘与并发:refine 永远保留源文件/asset 并创建 `canvas-<commitId>`。源没有外部 resourceId 时补齐 `local-asset:<manifestAssetId>`,新资产只通过规范 `referenceResourceIds` 引用,禁止混用裸 manifest asset ID。两个窗口基于同一 project/draft revision 最多一个成功;相同提交返回 already-committed,同键不同请求失败关闭。恢复只依据 journal、before/after 摘要和 ledger,不按文件存在、mtime 或 PID 猜测。
|
||||
- 投影与焦点:提交返回完整当前 manifest;同项目仍活动时立即更新 manifest 投影、依赖图输入和两种布局,不要求刷新或重开。自动选中还必须复核 project/path、asset-canvas session/draft/intent、selection epoch 和 search/filter epoch;用户已切项目、切模式、选其它资源或新资源被条件隐藏时不得抢焦点,隐藏时保留条件并提供显式清除/定位动作。
|
||||
- 影响范围:下一阶段的共享画布包、网站 adapter、`apps/ai-game-creator-shell` 前端与 Tauri Rust 本地持久化;不修改 SpacetimeDB schema,不把草稿或资源布局 sidecar 变成 manifest 业务真相。
|
||||
- 验证方式:按权威专题的 28 项矩阵覆盖 Web/Tauri 共用源码、新增/精修、生成响应丢失、重复提交、两窗口并发、事务各崩溃点、草稿恢复、切项目/切状态/改选择/改筛选迟到结果和不刷新即时投影。
|
||||
- 关联文档:`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`、`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,12 +138,36 @@
|
||||
- 验证方式:覆盖路由后只启动 `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 与可重复界面合同收口
|
||||
|
||||
- 背景:飞书资源管理需求的阶段零至阶段六已经分别完成资源卡禁拖、固定资源投影、中央聚焦、安全文档 / 媒体预览、依赖深度与正式版本只读模型;最后需要统一复核需求边界并用当前主分支完整门禁排除集成回归。
|
||||
- 决策:阶段七不新增平行功能,只补齐视频原生控件和资源读取策略失败空态的 AppSurface 证据,运行完整前端 / Rust / 运维 CI,并以真实 `1280×800` 浏览器测量证明页面级无溢出。开发页的真实登录门禁不得为验收绕过,工作台内部以 AppSurface 与 CSS 合同测试复核;美术编辑继续等待画板回写、血缘登记、新资源自动选中与邻近布局闭环。
|
||||
- 决策:阶段七不新增平行功能,只补齐视频原生控件和资源读取策略失败空态的 AppSurface 证据;美术编辑继续等待画板回写、血缘登记、新资源自动选中与邻近布局闭环。
|
||||
- 影响范围:`apps/ai-game-creator-shell` 资源管理测试、跨平台 CI / 运维测试脚本、工作台 PRD、AI 游戏创作实施计划和共享项目记忆;不修改 SpacetimeDB schema、manifest 业务合同或资源布局 sidecar。
|
||||
- 验证方式:根目录全量 Vitest `171` 个文件、`2155 passed / 5 skipped`;追加 lint、build、Rust workspace test / check、schema、原生壳、内容 / 编码和生产运维门禁;应用内浏览器在 `1280×800` 下 document/body client 与 scroll 尺寸相等。
|
||||
- 验证方式:根目录全量 Vitest、lint、build、Rust workspace test / check、schema、原生壳、内容 / 编码和生产运维门禁均已完成。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
|
||||
## 2026-08-03 正式项目版本阶段六落在 manifest 追加不可变记录
|
||||
@@ -154,7 +308,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,卡片坐标和持久化布局不变。
|
||||
@@ -327,7 +481,7 @@
|
||||
|
||||
- 背景:项目开发工作台当前只在 React 会话内保存同分类资源的一维拖拽顺序,项目切换或客户端重启后重建默认排列;工作台 PRD 虽已给出二维位置字段,但缺少落盘路径、坐标系、Tauri API、CAS、异常与安全边界,仍不足以直接编码。
|
||||
- 决策:dependency 与 type 两套布局分别保存为项目内 `.agent/workbench/resource-layouts/dependency.json` 和 `type.json`,统一使用 `game-creator-resource-layout.v1`。`x / y` 是 section 内容 CSS 像素,revision 从缺文件时的 `0` 单调递增;新资源首次默认放置,任何已有坐标不因排序、筛选、模式切换或 resize 被自动覆盖。type 默认布局固定按 `subtype -> mediaType -> label -> id` 排序,manifest 资产使用 `asset.kind`,任务产物、附件与 Agent 文本成果使用稳定 fallback,subtype 同时进入资源协调签名。
|
||||
- 并发与失败:Tauri 用 `read_local_project_resource_canvas_layout` 和 `update_local_project_resource_canvas_layout` 暴露读写,以 `projectId + mode + expectedRevision` 在专用跨窗口布局锁内做 CAS。更新额外携带只读结果中的 `expectedProjectId` 身份栅栏,路径被重建为新项目时旧窗口在锁副作用前失败;Rust 内部 revision 保留 `u64`,但共享 serde、Tauri 输入和前端 IPC 统一限制为 `0..=Number.MAX_SAFE_INTEGER`,达到上限时保持原文件。锁入口文件持久存在,Unix 以 `flock` 文件描述符、Windows 以不共享句柄持有互斥;应用不按 mtime / PID 猜测 stale、不删除锁文件,进程退出由操作系统释放。更新在创建锁目录前只读验证 manifest,锁内复核 projectId;无效根保持零 workbench 副作用。前端以 project/path/mode epoch 丢弃旧 scope 迟到响应,资源变化不得取消首读或同 scope 在途写;同 scope 的手动拖动与资源协调进入单写者 FIFO,后一笔只使用前一笔权威响应的 revision。切换 scope 会释放旧活动槽,旧请求即使卡死也不能阻塞新 scope;同资源尚未发送的连续拖动折叠为最后坐标,已经在途的 CAS 不取消。冲突返回最新完整布局且零写入,前端载入最新值、丢弃基于旧快照排队的手动拖动并要求重新操作;资源协调最多追加两次冲突重试,普通失败恢复最近可信布局。写入复用项目安全路径、链接校验、容量上限、恢复副本与原子替换,损坏或身份冲突不能被空布局覆盖。
|
||||
- 并发与失败:Tauri 用 `read_local_project_resource_canvas_layout` 和 `update_local_project_resource_canvas_layout` 暴露读写,以 `projectId + mode + expectedRevision` 在专用跨窗口布局锁内做 CAS。更新额外携带只读结果中的 `expectedProjectId` 身份栅栏,路径被重建为新项目时旧窗口在锁副作用前失败;Rust 内部 revision 保留 `u64`,但共享 serde、Tauri 输入和前端 IPC 统一限制为 `0..=Number.MAX_SAFE_INTEGER`,达到上限时保持原文件。锁入口文件持久存在,Unix 以 `flock` 文件描述符、Windows 以不共享句柄持有互斥;应用不按 mtime / PID 猜测 stale、不删除锁文件,进程退出由操作系统释放。更新在创建锁目录前只读验证 manifest,锁内复核 projectId;无效根保持零 workbench 副作用。2026-08-05 阶段四把完整资源输入签名加入 `projectPath + projectId + mode` scope identity:资源投影或可信依赖深度变化立即建立新 epoch,旧读写继续由后端 CAS 收束,但不占用新 scope 的前端单写者槽且迟到响应被丢弃;同一签名 scope 内的资源协调仍使用 FIFO 和前一笔权威 revision。冲突返回最新完整布局且零写入,资源协调最多追加两次冲突重试,普通失败恢复最近可信布局。写入复用项目安全路径、链接校验、容量上限、恢复副本与原子替换,损坏或身份冲突不能被空布局覆盖。
|
||||
- 业务边界:布局是本地工作台 UI sidecar,不进入 manifest,不推进游戏项目 mutation revision,不使 Runtime verification 失效,不触发 Agent 权限,也不属于资产、Agent 产物、Git 或云端事实。本切片不包含关系线、资源替换、浮层位置、缩放 / 平移、搜索 / 筛选条件和当前 mode。
|
||||
- 影响范围:`packages/shared` 与 Rust `shared-contracts` 的跨边界 DTO、AI 游戏创作 Tauri 项目持久层与命令、项目开发资源画布、定向 Rust / React 测试、工作台 PRD 和客户端实施计划。
|
||||
- 验证方式:序列化与字段上限测试、缺文件 / 损坏 / 原子恢复 / 链接安全测试、同 revision 双写最多一个成功、两种 mode 跨重启独立恢复、新增资源不移动旧坐标、`1280×800` 横屏无页面级溢出,以及 `npm run agc:typecheck`、定向测试、`npm run check:encoding`、`git diff --check`。
|
||||
@@ -437,7 +591,7 @@
|
||||
- 决策:在现有 `apps/ai-game-creator-shell` 项目开发入口内扩展单一工作台,不新建平行客户端。首版从当前 manifest、导入附件和 Agent 状态派生界面,提供资源 / 运行切换、资源排列与聚焦、审批弹层和底部状态栏;真实游戏通过现有 localhost 预览 server 直接载入客户端内受限运行容器,不再调用系统外部浏览器。未具备正式写回契约的拖拽布局、版本资源替换、数值微调、泥点累计、Agent.md 和 Skill 管理不得在前端伪造成功。
|
||||
- 横屏窗口:当前独立 App 只交付横屏桌面工作台,`client` 默认与最小窗口固定为 `1280×800`。工作台按壳内剩余视口排布并收紧四周留白;消息区与 Runtime 区各自承担内部滚动,专业状态增长不得把输入区或底部 Agent 栏推到视口外。窄屏纵向布局不作为当前客户端验收目标。
|
||||
- 影响范围:`apps/ai-game-creator-shell` 正式项目开发页、项目工作台前端测试、AI 游戏创作智能体 App 实施计划和原生壳预览门禁。
|
||||
- 验证方式:运行 AI game creator shell 定向测试与 typecheck、`npm run ai-game-creator-shell:check`、`npm run check:encoding`、`git diff --check`,并用真实浏览器检查 `1280×800` 最小横屏与目标桌面视口布局。
|
||||
- 验证方式:运行 AI game creator shell 定向测试与 typecheck、`npm run ai-game-creator-shell:check`、`npm run check:encoding`、`git diff --check`。
|
||||
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-07-17 AI 游戏创作 V1.31 使用同一父 run 收束静态与隔离协作
|
||||
@@ -1460,6 +1614,7 @@
|
||||
- 2026-06-19 桌面壳外链打开 helper 共用:Tauri WebView 外域拦截和 HostBridge `app.openExternalUrl` 都必须复用 `open_normalized_desktop_external_url` 执行系统外链打开动作;HostBridge 分支仍先用 `normalize_external_url` 保留 payload 错误语义并把 opener 错误回传给 H5,WebView 拦截保持 best-effort 静默处理。桌面壳配置检查会拒绝 `dispatch.rs` 直接调用 `app.opener().open_url` 绕过该 helper,避免两条离壳路径漂移。
|
||||
|
||||
> 2026-07-18 覆盖说明:本段后续关于微信 `navigation.openNativePage`、生成结果订阅页、`[subscribe-message]` 日志和订阅页路由门禁的 2026-06 决策均已由旧创作模板退役决策废止,只作为历史记录。Expo / Tauri 的同源 H5 受控导航及微信登录、支付、分享能力继续有效。
|
||||
|
||||
- 2026-06-20 H5 原生导航预校验:`navigateHostNativePage()` 在 `native_app` 下发送 `navigation.openNativePage` 前必须先拒绝空值、控制字符、协议相对 URL、外域绝对 URL 和非 `http:` / `https:` 协议目标;同源绝对 URL、`/path` 和保留给桌面壳兼容的相对 route 继续交给 Expo / Tauri 壳二次归一并补写宿主上下文。微信小程序分支仍按小程序页面 URL 语义走 `wx.miniProgram.navigateTo`,不套原生 App 同源 H5 预校验。根级 `npm run check:native-shells` 会反查 H5 facade 仍使用 `normalizeNativeAppPageUrl(...)` 且发送归一后的 URL,避免明显不安全目标触达原生壳。
|
||||
- 2026-06-20 微信受控原生页能力声明:微信小程序壳真实 capability profile 声明 `navigation.openNativePage`,用于承接已经登记并测试的小程序原生页 flow;当前订阅生成结果通知页通过 H5 `requestGenerationResultSubscribePermission()` 调用 `navigateHostNativePage()` 打开 `/pages/subscribe-message/index`,小程序页再调用真实 `wx.requestSubscribeMessage` 并按既有结果协议回灌。根级 `npm run check:native-shells` 必须把该能力反查到共享 profile、微信 `WECHAT_HOST_CAPABILITIES` 镜像、订阅页协议常量、H5 入口、小程序 host-bridge / shell / page 文件和相关测试;该能力不代表开放任意小程序页面跳转。
|
||||
- 2026-06-18 能力声明收紧:`packages/shared/src/contracts/hostBridge.ts` 提供 HostBridge method / capability 白名单,H5 的 `getHostRuntime()` 会解析并过滤 `hostCapabilities`;`openHostShare`、`writeHostClipboardText`、`requestHostHapticsImpact`、`setHostAppTitle`、`exportHostTextFile` 等 native 能力只在宿主声明对应 capability 后调用。发布分享弹窗只有声明 `share.open` 时才显示受控分享动作,并按 `hostShell` 区分 Expo 系统分享面板和 Tauri 剪贴板复制表达,避免旧壳或裁剪壳露出不可用入口。
|
||||
@@ -6278,7 +6433,7 @@
|
||||
- 异步契约:External v1 的图片生成、图片编辑、图标图集、UI 素材提取、角色动画、视频、音效和背景音乐八类 POST 固定持久化入 `external_generation_job` 并返回 HTTP `202 + operationId/statusUrl/pollAfterMs`;不受站内 `GENARRATIVE_EXTERNAL_GENERATION_MODE=inline` 影响。每次逻辑生成必须携带稳定 `Idempotency-Key`;提交结果未知时复用原 endpoint、原始正文和原键恢复 POST,轮询超时时保留已有 `operationId` 并只继续 GET,不得换键重提。
|
||||
- 发布窗口兼容与幂等恢复:AI 游戏创作桌面客户端严格按 HTTP 状态分流生成首响应;旧服务 `200` 只作为已经完成且含可下载媒体的同步结果消费,旧图集允许从顶层 `spritesheetImageSrc` 换签下载且无效值不得遮蔽可用 `objectKey`;新服务 `202` 必须取得 `operationId` 后轮询,轮询间隔按 OpenAPI 限制在 `250..=5000ms`,其他 2xx 失败关闭。Runtime 在 POST 前原子持久化 endpoint、精确请求体字节、请求 SHA-256 与稳定幂等键,`202` 后先原子追加 `operationId` 并回读一致再查询;`accepted` 账本只恢复 GET,`prepared` 只允许在身份、配置指纹与请求 SHA 校验通过后使用账本中的原 endpoint、原始正文和同一键恢复 POST。该 POST 是 External v1 服务端幂等合同下的同一逻辑提交恢复,不放宽通用 ToolHost 未知副作用禁重放规则。恢复得到 `202` 后升级同一账本并继续 GET,旧 `200` 走兼容完成路径;再次 transport 失败仍保留同一账本。显式“继续/恢复”复用原 action、pending、provider batch 和生成账本身份,不创建 successor 请求或新键。生成 POST 使用独立三十五分钟等待预算;game-chat 的两次串行生成纳入父 run `4200` 秒软预算与从 root `bound_at` 起算的 `4500` 秒绝对硬截止,但截止时若 `canvas.asset_generate` 已进入 executing,客户端与预览照常退出,Runtime 保留 pending action、provider batch、生成账本与 `needs-reconciliation`。轮询超时保留 `accepted + operationId` 并在恢复时继续 GET;旧 `200` 结果损坏、`202` 缺 operationId、状态损坏、透明派生失败或外部完成后的本地提交失败仍进入对账边界。非阻断 general warning 继续消费结果并与 `sliceWarning` 分别展示。权威 External v1 OpenAPI 仍只声明新异步 `202`,不把部署过渡兼容公开成正式双协议。
|
||||
- 查询与结果:新增 owner-safe `GET /api/external/v1/generations/{operationId}`。`queued/running` 返回 phase/progress,`completed` 返回 compact 稳定 artifact 引用,`failed` 返回脱敏错误,跨 owner 按不存在处理。compact result 允许 objectKey、resource/asset ID、assetObjectId、尺寸、媒体类型、taskId 和告警;禁止完整 project/canvas、Data URL、Blob URL、临时 signed URL、内部 provider 原文和 lease/fencing 控制字段。
|
||||
- 客户端 durable 查询约束:私有生成账本同时绑定 base URL/API Key 配置指纹,指纹不一致不恢复 POST 或查询旧 operation。旧 `200` 兼容结果只持久恢复允许字段和安全媒体引用。operation 明确 failed 的账本保留到 pending observation 和 Provider batch 终态落盘后再清理。只有首次提交直接取得契约明确的 `400 / 401 / 403` 才可判定为入队前拒绝并清理 prepared 账本;首次结果已经未知后,恢复 POST 的临时 `401 / 403` 等响应不能证明原请求未入队,不得删除账本。其它非成功状态一律保留账本进入对账。账本路径解析、扫描和删除逐级拒绝符号链接,非法控制路径失败关闭。
|
||||
- 客户端 durable 查询约束:私有生成账本绑定归一化 base URL 的服务身份指纹,服务地址不一致时不恢复 POST 或查询旧 operation;Developer API Key 只负责授权,轮换不改变已有 operation 身份。旧 `200` 兼容结果只持久恢复允许字段和安全媒体引用。operation 明确 failed 的账本保留到 pending observation 和 Provider batch 终态落盘后再清理。只有首次提交直接取得契约明确的 `400 / 401 / 403` 才可判定为入队前拒绝并清理 prepared 账本;首次结果已经未知后,恢复 POST 的临时 `401 / 403` 等响应不能证明原请求未入队,不得删除账本。其它非成功状态一律保留账本进入对账。账本路径解析、扫描和删除逐级拒绝符号链接,非法控制路径失败关闭。
|
||||
- MCP:新增托管 `/api/external/v1/mcp`,使用现有 External API Key Bearer 鉴权和无协议 session 的 Streamable HTTP JSON direct 模式。MCP tools 从同一 OpenAPI operation 形成并复用 External REST router;生成 tool 显式要求 `idempotencyKey`,另有统一任务查询 tool。MCP resources 提供使用说明、OpenAPI、Skill 入口 `SKILL.md` 和 `references/capability-routing.md`、`references/api-operations.md`、`references/authentication-and-safety.md`、`references/requests-and-outputs.md` 四篇稳定 reference;日后新增 reference 时必须同步新增独立 resource。MCP Agent 直接调用托管 tools,不安装 CLI,也不将脚本、测试或 workflow 暴露为 MCP resources。禁止开放内部 SpacetimeDB MCP、worker procedure、controller 或队列控制面。
|
||||
- Agent 发现:新增公开 `agent-integration.json`、`skill/SKILL.md` 和 `skill.zip`。manifest 同时声明 MCP、OpenAPI、完整 Skill archive、SHA-256 和包内清单;archive 必须包含 `SKILL.md`、上述四篇 references、stdlib Python helper 和 `agents/openai.yaml` 七个声明文件,不能只提供 OpenAPI JSON,也不能包含 API Key、本机路径或个人配置。完整 `skill.zip` 只供不支持 MCP 或需要本地文件上传编排的 Agent 使用,不作为 MCP resource。
|
||||
- 兼容边界:这是基于「截至 2026-07-31 尚无外部第三方存量调用方」接受的 v1 原地 breaking change;一旦出现外部活跃 Key、公开契约或联调方,后续破坏性变更必须保留兼容、经过弃用期或升级 `/api/external/v2`。
|
||||
@@ -6629,6 +6784,19 @@
|
||||
- CSS `url(...)` 的资产路径保持原始大小写解析,stylesheet 证据必须同时命中实际可见元素;未命中 selector、元素自身或祖先 hidden、以及匹配隐藏规则的节点均不作证。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-06 客户端素材画布客户登录态生成与开发者 External 通道隔离
|
||||
|
||||
- 生成入口决策:客户端共享素材画布只实现首版图片生成与精修,通过共享 `ImageCanvasGenerationPort` 复用现有编辑器项目、统一生成队列、泥点预扣/退款和资产系统,不复制网站 `useImageCanvasGenerationWorkflow`,不建立第二套生成、扣费或发布系统。普通客户使用当前平台登录态调用 `/api/editor/*`、`/api/assets/*` 与 `/api/runtime/external-generation/jobs/*`;`/api/external/v1` 与开发者 API Key 继续只服务第三方 Agent、CLI 和受控内部验收。
|
||||
- 安全与计费决策:客户画布不显示或要求填写 Base URL / API Key,不读取仓库根目录 `.env*`,也不把供应商 Key 打包进客户端。后端从登录身份解析 owner 并归属泥点扣费;客户 Access Token 只作为单次 IPC 输入存在内存,不写账本、草稿、manifest、事件、日志或错误。
|
||||
- 确认决策:编辑 prompt、尺寸、比例、素材类型与参考图只改本地 UI;最终“确认生成”之前零远端请求。一次已确认意图固定 `intentId + generationId + 远端 idempotencyKey + commitId + commit idempotencyKey`,未知结果、重启和重复点击只能沿原身份恢复。
|
||||
- 持久化决策:prompt、精确请求快照、不依赖凭据的调用模式/API 身份指纹、远端 key、operationId 和远端稳定引用只写 `.agent/workbench/asset-canvas/generations/` 私有账本;参考图上传只持久化恢复所需的稳定 bucket/objectKey,direct-upload ticket 的 host、formFields、policy、signature 和临时 Authorization 只存在内存。登录 Token 刷新不得触发 `configuration-changed`。公开草稿、manifest 与事件只投影安全阶段和本地资产身份。远端 taskId 不得写入本地 manifest taskId。
|
||||
- 恢复决策:`accepted/running` 只 GET 原 operation,提交未知只用原请求字节和原 key 对账;远端完成后以固定 staging token 下载,再进入阶段三 journal/ledger。应用打开严格先恢复本地正式资产事务,再恢复 generation ledger,因此下载后提交中断只恢复原本地事务,不重新生成或下载。
|
||||
- 显现决策:`generation-accepted -> generation-running -> remote-completed -> media-downloaded -> asset-durable-committed -> manifest-projected -> layout-ready -> selected` 不可压成 completed;停止 UI 等待只失效聚焦意图,迟到完成仍可安全投影 manifest,但不能抢焦点。
|
||||
- 精修身份决策:正式本地资源通过 `referenceResourceIds` 引用原资源;External `sourceImageSrc` 只使用 objectKey、项目 resourceId 或素材 assetId,不能把 assetObjectId 当成媒体引用。`sourceResourceId` 只允许真实 External resourceId,不能使用 `local-asset:*` 或 objectKey 冒充。
|
||||
- 最终共享边界:Tauri 撤销/重做改为直接消费共享 `useCanvasHistory`;共享 history 统一恢复 viewport、selection、图层位置和缩放边界。Tauri Pointer 代码只负责把宿主事件接到共享 selection/viewport/transform/renderer 算法,不再维护第二套 history 栈。
|
||||
- 事务发布前恢复:首个快照、全部快照、journal 后但 ledger 前都是正式故障点。无 ledger 的 transaction 仅在受控目录内容、正式文件不存在且 manifest/revision 精确保持 before 时清理并返回 rolled-back;未知条目或权威状态变化必须失败关闭。清理后允许原 commit/idempotency 身份重放,最终仍只产生一个 manifest asset。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/asset_canvas/generation.rs`、`packages/image-canvas-core/src/ports.ts`、`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`。
|
||||
|
||||
## 2026-08-03 图片画布生成产物统一“改造”契约
|
||||
|
||||
- 背景:画布生成结果统一显示“改造”,但部分动画 / 音频结果无法恢复面板;另一些非生成型派生结果继承最近生成输入,产生错误可执行动作。中文标题和素材类别被同时当成显示文案、参数键和路由键,改名后容易漂移。
|
||||
@@ -6859,6 +7027,18 @@
|
||||
- 分类:内部稳定 reason code 固定为 `translation_invalid / translation_upstream_failed / translation_budget_exhausted / elevenlabs_http_failed / invalid_audio / duration_probe_failed / oss_failed / writeback_failed`。MIME、空 body 和大小归 `invalid_audio`;MP3 识别、帧读取和时长门禁归 `duration_probe_failed`。普通用户继续只读稳定短文案,不暴露 endpoint、上游正文或凭据。
|
||||
- 跨入口:画布 Agent `generate-sound-effect` 与站内 / External v1 共用 canonical Prompt、固定模型、nullable `0.5-30` 小数时长和 Loop;省略 duration 为手动 `5s`,显式 null 为自动。SFX 参数解析必须保留该 null,不能被通用 null-default 兼容层改写。最终仍进入相同 `editor_sound_effect_generation` queue payload,不新增 Agent 专属链路。
|
||||
- 发布边界:T6 工程实施和 mock / loopback 门禁不等于真实 provider 或生产验收。发布前关闭 SFX 入队,使用显式 `--server` / `--server-url` 只读查询 `external_generation_job` 中 pending / running 的 `editor_sound_effect_generation`,清零后按 api-server / Worker → Web 顺序部署并灰度;禁止 `--root-dir`、删除任务伪造 drain 或自动回退 Vidu。本次没有 SpacetimeDB schema、migration 或 bindings 变更。
|
||||
|
||||
## 2026-08-10 客户端全类型资源编辑恢复身份与交互门禁
|
||||
|
||||
- 资源编辑 operation 以私有账本中的原 endpoint、请求字节、幂等键和 `operationId` 为唯一恢复身份;账本同时冻结源 asset、媒体类型、资源 kind、生产任务和源版本快照。旧账本只从当前 manifest、已完成任务产物、版本数组及受控扩展名兼容恢复,不能信任 UI 路径或前端重新构造的来源。
|
||||
- Developer API Key 允许轮换,当前 `service-origin-v1` 配置身份绑定规范化 External base URL 的服务指纹,确认 UI 只展示服务 origin。升级前包含 Key 的生成指纹在当前 Key 可精确验证原服务时自动迁移;无法验证时保留原 operation 进入对账,不自动接管或重放。素材画布生成恢复另提供受挑战值保护的显式 origin 确认。operation 已建立后遇到 401/403/404、超时或临时网关错误都保留原 operation;恢复命令仅接收项目与 operation 身份,不接收 Key 或新请求正文。
|
||||
- pending 扫描上限覆盖目录全部实际条目。媒体提交前重新校验源摘要;视频后续派生使用 committed ledger 的稳定 `objectKey`,不把 `assetObjectId` 当远端输入;版本 journal 重放返回原子版本,禁止重复追加。
|
||||
- refine 草稿恢复以正式 sidecar 为权威,按项目、意图、源素材和 active 状态有界唯一发现;唯一命中保留 `draftId` 并换新 `sessionId`,多候选失败关闭。文本/SVG/Agent 回执在 Provider 调用前持久化 request-issued,成功正文先写 durable handoff 再解析和 staging;issued 后无可信 handoff 只对账,有 handoff 只消费原响应。
|
||||
- 旧 Key-bound 资源编辑账本采用快照绑定的显式服务 origin 确认,确认前零网络动作;已受理 operation 遇到 401/403 保留原 remote phase,换 Key 后只 GET。asset journal 已证明目标 asset/media 与 target revision 后,后续合法 manifest/revision 前进不阻止补齐 ledger。
|
||||
- 画布生成中冻结 wheel、指针、缩放和方向键状态改变并聚焦“停止等待”;普通 wheel 二维平移,Shift 纵向滚轮横移,Ctrl/Command wheel 缩放。Shift 指针序列只执行一次选择切换,草稿保存失败后退出仍必须提供保留/放弃确认。
|
||||
- 只有 `editing` 生命周期允许新生成;其它状态与 modal 同时锁闭背景交互,生成 handler 必须再次读取当前 lifecycle 失败关闭。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`、`apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx`、`packages/image-canvas-core/src/interaction.ts`。
|
||||
|
||||
## 2026-08-06 编辑器生成结果使用 durable receipt 与统一原子提交
|
||||
|
||||
- 背景:图片、改图、去背景、图集 / UI 多产物、角色动作、视频、音效和背景音乐在 OSS 结果可用后,仍分段 confirm object、创建 project resource / account asset、保存 canvas 和 complete job。任一中间失败都会留下部分业务事实;只把 `external_generation_job` 当 operation journal 又无法覆盖无 job 的 inline,也无法独立证明某批 resource/asset/canvas 已作为一笔提交完成。
|
||||
@@ -6954,4 +7134,12 @@
|
||||
- 元数据决策:首次导入只创建并保留项目根下的 `.agent/manifest.json`、`.agent/agent.db`、`.agent/logs/` 与 `.agent/runtime/`;不得创建默认 Web 原型的 `game/`、`assets/`、`memory/`、`exports/`。已有有效 `.agent` 项目只读复用身份,不覆盖 manifest。
|
||||
- Windows 锁文件决策:提升权限进程新建 `.agent/.manifest.json.lock` 时,Windows 可能把 owner 设为 `Administrators`。仅在固定锁路径已取得不共享独占句柄并确认是普通、非 reparse、单链接文件后,才初始化为当前 `TokenUser`;随后再次复核句柄并执行原有 owner/DACL 校验,不放宽既有异常对象的安全规则。
|
||||
- Provider Schema 决策:`agent.route_manifest.missingAssetSlots` 不再广告 OpenAI-compatible 代理拒绝的 `uniqueItems`;Runtime 继续排序去重,Schema 子集门禁新增该关键字,真实 Provider smoke 必须在发布前证明工具目录可被接受。
|
||||
- HTTP 传输边界:根 H5 包不得依赖 Tauri guest 插件;AGC 独立包保留 `@tauri-apps/plugin-http`。Rust 插件显式关闭默认特性,只启用 `charset`、`cookies`、`http2` 和 `rustls-tls`,避免 `reqwest/system-proxy` 通过 Cargo feature union 把画布、Provider、Runtime 与本地回环夹具统一接入 OS 自动系统代理;如未来产品要求正式客户端继承系统代理,必须按各客户端明确设计并单独完成跨平台验证。
|
||||
- 运行决策:Godot 项目提交给 Project Supervisor 时使用 `standard` Run Profile,避免触发 Web 专用 `game/index.html`、HTTP preview 与自主 Web 完成门。Godot 编辑器启动和内嵌运行预览不在本切片范围。
|
||||
|
||||
## 2026-08-11 Game Agent 通用 Goal Contract 与动态验收图
|
||||
|
||||
- 决策:固定规则只负责安全、事实完整性和完成协议。根 Project Supervisor 必须先用 `agent.goal_contract` 把自己对当前用户最终意图的理解冻结为结构化 outcome、硬约束、偏好、禁止假设、开放问题和 acceptance nodes;同一根 Run 不允许改写。该合同摘要进入共享黑板,权威 JSON 绑定可信 root Run Profile、source task SHA-256 与 fingerprint。
|
||||
- 决策:根合同和 Acceptance Graph 自动进入所有后代上下文,但不扩大局部工具、路径、写范围、expectedArtifacts 或委派权限。isolated child 必须同时看到其持久化的局部验收、产物和 writeScopes。专业 Agent 只交付局部结果与证据,不能更新根图或宣布总目标完成。
|
||||
- 决策:只有同一可信根 Supervisor 能调用 `agent.acceptance_update`,且该控制面动作必须独占一轮,不能夹带 `plan_update`、legacy plan 或回复。requiredEvidence 采用 `tool:<Runtime 工具名>`,合同冻结前必须命中 Runtime 允许的持久证据工具集合并拒绝拼错、控制面和纯协调工具;动态 MCP catalog 不得冻结为不可变 requiredEvidence。passed 节点必须引用当前根任务树中对应工具的真实成功动作回执;回执同时记录动作执行边界的 `projectRevisionBefore / projectRevisionAfter`,非 mutation evidence 必须满足 before、after 与验收时 current revision 完全相同,mutation evidence 的 after 必须等于 current revision,旧 revision 或执行后延迟落账的回执不可重放。failed、not-observed、缺失节点以及落后当前 project revision 的整图确认状态均阻断普通完成、finalization 与恢复。项目 revision 变化后由 Supervisor 只更新受影响节点并确认当前图,未提交的 passed 节点保持不变;Runtime 不替 Supervisor 推断影响范围、选择具体 Agent 或实现方式。
|
||||
- 决策:不可变 Goal Contract 的根 Run 收到 steer 时,必须按旧 rootRunId 串行化整个转换,并在持锁后重新确认该旧根仍是 Session 当前权威 Run,避免不同 steerId 并发创建多个 replacement。随后为绑定旧 rootRunId 的全部非终态静态、ready 和 isolated 后代写入取消栅栏、打断 Provider 并逐个收束,再终止旧根;所有项目修改入口在看到取消栅栏后立即失败关闭。Runtime 必须确认旧树所有成员都已进入终态或 `needs-reconciliation`,超时则保持等待并拒绝启动 replacement;只有旧树停稳后,才在同一 Session、source 和 Run Profile 启动新根 Run 重新理解完整目标。
|
||||
|
||||
@@ -611,6 +611,14 @@ npm run check:server-rs-ddd
|
||||
- 页面交互 smoke
|
||||
- 移动端视口检查
|
||||
|
||||
### 提交前 TypeScript 自动格式化
|
||||
|
||||
仓库级 Git `pre-commit` hook 通过 `lint-staged`,只对当前已暂存的 `*.ts`、`*.tsx` 文件运行 Prettier 自动格式化,并把格式化结果更新到本次提交的暂存区;未暂存的其他文件不进入格式化范围。格式化或暂存恢复失败时提交会中止,应先处理失败原因并重新检查 staged diff,不能等 CI 再暴露格式问题。
|
||||
|
||||
部分暂存同一 TS / TSX 文件时,`lint-staged` 会临时隐藏该文件未暂存的改动,以暂存快照执行格式化,随后恢复未暂存内容。因此提交前后都应分别检查 `git diff --cached` 和 `git diff`,确认格式化后的暂存内容属于本次提交,未暂存工作没有被误带入;若恢复产生冲突,先人工整理暂存边界再重新提交。
|
||||
|
||||
`git commit --no-verify` 会绕过该 hook,只允许在已明确原因的紧急场景使用。绕过时仍须对本次暂存的 TS / TSX 文件手动执行等价的 Prettier 格式化、重新暂存并核对 staged diff;`--no-verify` 不代表可以跳过格式化或其他提交门禁。
|
||||
|
||||
前端原则:
|
||||
|
||||
- 移动端优先,再兼容网页端。
|
||||
|
||||
@@ -14,6 +14,77 @@
|
||||
- 关联:相关文件、文档、提交或 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`。
|
||||
- 关联:`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`。
|
||||
## 正式保存不能等待 React state 才取得草稿 CAS 的新 revision
|
||||
|
||||
- 现象:用户刚完成编辑就点击“保存到项目”,自动草稿保存已经成功,但正式提交仍携带旧 `expectedDraftRevision`,于是单窗口也得到 draft revision conflict;快速连续保存时还可能使用不同 commitId 重复 staging。
|
||||
- 原因:`setDraft(result.value)` 的 React state 提交晚于当前 Promise 链,正式保存若从闭包或下一次 render 读取 revision,会把 UI 调度时序误当成持久化顺序。相同问题也会出现在选择变化未标脏、父组件每次 render 新建 scope 对象而重复恢复、项目切换后旧 generation 回调继续写 notice。
|
||||
- 处理:草稿保存队列在 CAS 成功后同步更新 `draftRef.current` 并直接返回权威 draft;正式提交继续使用该返回值的 revision。scope 按 project/draft/intent/source 原始字段稳定化,所有导入、保存、生成和事件回调捕获当前 epoch,选择变化属于草稿合同并必须标脏。首次正式保存冻结 commitId/idempotencyKey,未知结果只重放原请求。
|
||||
- 验证:用 deferred Promise 证明草稿 CAS 完成后正式 commit 使用新 revision;相同 scope 值重渲染不重复 recover/load;锁定图层选择进入草稿更新;项目切换后迟到 generation/commit/event 均不改变新会话。
|
||||
- 关联:`apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx`、`apps/ai-game-creator-shell/tests/assetCanvasSurface.test.tsx`。
|
||||
|
||||
## 共享画布不能用全局 DOM 查询或宿主整包 CSS 作为隐式依赖
|
||||
|
||||
- 现象:页面挂两个画布时,第二个画布点击小地图会移动第一个画布;反复挂载后 wheel 触发多次。Tauri 单独引入网站 `index.css` 时还会带入账号、项目页和历史业务样式,或因仓库外源码解析到第二份 React 而出现 Hook 错误。
|
||||
- 原因:`document.querySelector`、body 级 portal、未清理的 listener/observer/animation frame 和不受作用域约束的 CSS 都把组件实例与网站宿主当成全局单例;Tauri Vite 默认根目录又不等于仓库根,React 解析路径可能分叉。
|
||||
- 处理:共享小地图先从当前 viewport 查询,只有文档中唯一候选时才兼容旧单实例形态;wheel、ResizeObserver 和 animation frame 在 effect cleanup 中逐项释放。portal 支持实例 root,默认 body 只作兼容。共享 CSS 全部限定在 `.genarrative-image-canvas`,两宿主 alias 同一 `packages/` 源码并 dedupe React/ReactDOM,Tauri `fs.allow` 覆盖 repo root,禁止导入主站完整 `index.css`。
|
||||
- 验证:共享 React 测试重复 mount/unmount 后 wheel add/remove 数量相等、ResizeObserver 精确 disconnect,并挂两个含各自小地图的 viewport,确认只更新目标实例;网站与 Tauri 壳分别 typecheck/build。
|
||||
- 关联:`packages/image-canvas-react/src/useImageCanvasViewportControls.ts`、`packages/image-canvas-react/src/CanvasPortal.tsx`、根目录与 `apps/ai-game-creator-shell` 的 Vite 配置。
|
||||
|
||||
## 桌面工作台不要让 Supervisor 内容高度挤掉输入区和 Agent Dock
|
||||
|
||||
- 现象:`1280×800` 或更矮窗口中,Supervisor 的消息、Runtime 状态和错误正文共同按内容高度增长,聊天输入被推到栏外;底部 Dock 使用固定宽度卡片时还会在中等宽度造成页面级横向溢出。中央画布的生成卡和状态栏若同时固定高度并隐藏 overflow,进度、失败、重试、保存或取消动作会被裁掉。
|
||||
- 原因:四区工作台没有把“主区内部滚动”和“页面级滚动”分开;Supervisor 的长状态没有独立上限,Dock 卡片不能收缩,宿主又用全局按钮/固定行高样式覆盖共享 chrome。
|
||||
- 处理:桌面工作台使用 `100dvh` 两行网格,第一行 `minmax(0, 1fr)` 承载中央区与 Supervisor,第二行承载 Dock;消息和 Runtime 分别内部滚动,composer 保持最后一行并设置明确层级。Dock 卡片使用可收缩 flex 与文本省略。画布 dialog、进度/失败卡和状态栏设置 `min-height: 0`、受限最大高度与内部滚动;宿主不再覆盖所有按钮,只为主要动作和布局提供 token 化薄样式。
|
||||
- 验证:AppSurface CSS 合同检查 `100dvh`、Supervisor composer、Runtime 内滚动、Dock 常驻/可收缩;素材画布测试检查生成 dialog、operation card、状态栏和窄屏保存动作不会被裁剪。真实入口在 `1280×800` 测量 document/body client 与 scroll 一致;登录门禁不可为视觉测试绕过。
|
||||
- 关联:`apps/ai-game-creator-shell/src/styles.css`、`apps/ai-game-creator-shell/src/features/asset-canvas/assetCanvasSurface.css`、`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`、`apps/ai-game-creator-shell/tests/assetCanvasSurface.test.tsx`。
|
||||
|
||||
## 素材保存区不要把机器 subtype 当普通文本框(2026-08-06)
|
||||
|
||||
- 现象:保存区同时显示“画布素材”和裸 `asset` 文本框,普通用户无法判断两者用途;自由修改 kind 会形成无法稳定参与类型布局、Agent 合同和替换兼容性的 subtype。工具栏与保存设置挤在同一行时,主要保存按钮还会被压缩或裁切。
|
||||
- 原因:把 Host Port 的 `name / assetKind / mediaType` DTO 直接映射成同层输入控件,没有区分用户命名、机器分类和编码格式,也没有为中央区域的真实容器宽度保留主操作列。
|
||||
- 处理:名称保留编辑;create kind 使用 Runtime 权威四项目录和中文标签,refine 从源 manifest 继承并锁定,未知历史值只透传;格式继续使用有限枚举。工具动作和保存设置显式上下分行,保存列使用 `max-content + nowrap`,窄容器时按钮独占整行。普通工作区状态只显示项目名称,不把绝对路径作为默认辅助文案。
|
||||
- 验证:Surface 测试断言四项用途、默认值、精修未知 kind 锁定、最终 commit 参数和保存按钮 CSS;AppSurface 断言普通界面找不到绝对路径,内部 Tauri 调用仍使用原完整路径。
|
||||
- 关联:`apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx`、`apps/ai-game-creator-shell/src/features/asset-canvas/assetCanvasSurface.css`、`apps/ai-game-creator-shell/src/features/project-workspace/`。
|
||||
|
||||
## 正式素材提交不能把多文件写入或 Tauri 事件误当成一次原子动作
|
||||
|
||||
- 现象:图片已经落到 `assets/` 但 manifest 没有资产,或 manifest 已追加而 project revision/草稿仍是旧值;进程在 emit 前后退出后,用户重试又得到第二份图片、第二个 asset 或重复选中。
|
||||
- 原因:文件系统只保证单文件原子替换,不能让最终图片、manifest、`.agent/runtime/project-revision.json`、commit ledger 和草稿跨文件物理原子;Tauri event 也没有跨崩溃 exactly-once。若先写副作用再临时生成幂等身份,或只凭目标文件存在推断成功,就无法区分未提交、已提交未回包和部分提交。
|
||||
- 处理:第一次保存前冻结 `commitId + idempotencyKey + eventId + requestFingerprint`,在项目 write lock 内先写 prepared journal 和 before/after 摘要,再按最终图片、manifest/revision 逻辑原子更新、回读、ledger/草稿提交推进,释放锁后最后 emit。恢复只按 journal stage、精确字节摘要和 ledger 前向完成/安全回滚;矛盾状态进入 reconciliation-required。事件采用至少一次,监听方按 eventId 和 project revision 去重。
|
||||
- 验证:分别在 prepared、图片安装、manifest 安装、revision 安装、ledger 提交、emit 和投递标记后强杀;确认只有唯一 `canvas-<commitId>`、revision 最多推进一次、源资产与血缘正确,响应丢失后返回 already-committed,矛盾 fixture 不自动重试。
|
||||
- 关联:`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`。
|
||||
|
||||
## 素材保存成功不等于迟到结果仍有权抢占当前焦点
|
||||
|
||||
- 现象:用户等待生成/保存时切到另一个项目、run、另一份素材草稿,或主动选择其它资源、修改搜索条件;旧请求完成后界面却切回旧画布、清空筛选并自动选中新资源。
|
||||
- 原因:异步回调只检查“请求成功”或捕获的旧 `isMounted/projectId`,没有绑定中央状态 session、draft/intent、selection epoch 和 query epoch;manifest 投影这一数据事实又被错误地与“当前应自动聚焦”的用户意图合并处理。
|
||||
- 处理:保存开始捕获 `projectPath + projectId + centerKind + sessionId + draftId + intent + selectionEpoch + queryEpoch`,响应时从当前 ref/store 完整复核。manifest 可以按精确项目身份更新当前上下文或后台缓存,但自动切状态、选择、滚动和聚焦必须等当前 mode 布局 ready 且全部焦点守卫仍相等。新资源被搜索/筛选隐藏时保留条件与选择,提示“新资源已保存,当前筛选条件下不可见”,只提供显式清除/定位动作。
|
||||
- 验证:使用 deferred commit/layout Promise,依次在请求后切项目、切 run/overview、新开 session、改选择和改筛选;断言 manifest 只更新对应项目,新资源仍进入投影/布局,但所有失效守卫都不切中央状态、不改选择、不清查询。条件未变化且资源可见时才自动定位。
|
||||
- 关联:`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`、`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`。
|
||||
|
||||
## 派生 Debug 会让完整配置经应用状态递归进入日志
|
||||
|
||||
- 现象:配置和状态当前没有直接日志调用,但新增一行 `debug!(?state, ...)` 或 `format!("{config:?}")` 就能把 JWT、后台口令、支付私钥、OSS / provider key 与 SpacetimeDB token 一次性写入日志及 OTel 留存面。
|
||||
@@ -71,6 +142,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 +190,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 +222,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`。
|
||||
|
||||
## 依赖图未就绪时不能先初始化资源布局
|
||||
@@ -4202,15 +4313,13 @@
|
||||
- 处理:先以 CAS 单独 commit `queued -> executing`,成功后才调 ToolHost;调用返回后再 commit observation。恢复见到 executing 或 ToolHost 返回 Unknown 时只能进入 reconciliation,不得自动重执行。重复 resume 不得继续增 revision 或重复 event。
|
||||
- 验证:在“ToolHost 已调用、observation commit 失败”处注入故障,序列化快照并用新 engine 重载;断言重复 resume 后 ToolHost 计数仍为 1,且只有显式 reconcile observation 才恢复 running。
|
||||
|
||||
## Runtime pending 恢复不能让大型 async frame 共用默认 worker 栈(2026-08-03)
|
||||
## Runtime 后台执行不能让大型 async frame 共用默认 worker 栈(2026-08-03)
|
||||
|
||||
- 现象:Supervisor collaboration durable isolated spawn 恢复测试在默认 Tokio worker 栈下稳定 `stack overflow`;单独运行同样失败,提高 `RUST_MIN_STACK` 后通过。
|
||||
- 原因:不是业务递归。debug 构建中 pending action continuation、后台 task queue 和 Agent 主循环各自形成大型 async poll frame;恢复路径在同一次 poll 调用链直接进入下一层状态机,累计超过 worker 默认栈。
|
||||
- 处理:整个 pending continuation、它进入的后台主循环,以及完成、取消或失败后 drain 同 Agent 后续队列时,都必须跨越独立 Tokio task 轮询边界,使上层 poll 先退栈后再轮询下一层状态机。传入边界的 future 必须先装箱;若泛型 helper 直接持有大型 future,即使随后 `spawn`,调用方 async frame 仍会把它保留在默认 worker 栈上。边界必须保留结构化取消语义;当前使用 boxed future 与 `JoinSet`,父 continuation 被丢弃时同步 abort 子任务。不得只增大 CI 的 `RUST_MIN_STACK`,否则生产默认栈仍可能崩溃。
|
||||
- 验证:失败用例必须在未设置 `RUST_MIN_STACK` 时通过;同时覆盖 policy batch 全组、拒绝 pending 后重规划并 drain 下一任务,以及 pending/cancellation 回归,证明恢复不重复生成 isolated spawn、队列继续推进且父任务取消不遗留后台子任务。
|
||||
- 2026-08-10 补充:Provider、Codex CLI 与 Codex app-server 合并到同一个模式分发后,即使本轮实际选择普通 Provider,未装箱的组合 future 仍携带最大分支状态;委派子任务完成后回流父 Agent 的既有回归会在默认 Tokio worker 栈稳定溢出,单独运行同样失败,扩大 `RUST_MIN_STACK` 才通过。持久重试 helper 与非持久压缩路径都必须在构造完整物理 Provider request 后、进入下层泛型 control/lifecycle helper 前装箱;不要逐个扩大 queue worker 栈,也不要等到底层 helper 内部再装箱已经进入调用方 frame 的泛型 future。
|
||||
- 2026-08-10 验证:未设置 `RUST_MIN_STACK` 时运行 `background_agent_runtime_can_delegate_task_to_other_agent`,并追加 `provider_retry_`、`provider_handoff_`、`response_stream_` 与 Native shell 完整门禁;测试只能以默认 worker 栈通过,不能把 CI 环境变量当修复。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_retry.rs`。
|
||||
- 现象:Supervisor collaboration durable isolated spawn 恢复测试或普通 `agent.delegate` 后台委派测试在默认 Tokio worker 栈下稳定 `stack overflow`;单独运行同样失败,提高 `RUST_MIN_STACK` 后通过。
|
||||
- 原因:不是业务递归。debug 构建中 pending action continuation、后台 task queue、Agent 主循环,以及 Provider、Codex CLI、Codex app-server 组合模式分发的最大分支状态都会形成大型 async poll frame;恢复路径直接进入下一层状态机、普通后台任务把完整主循环放回默认 worker,或组合 future 进入泛型 helper,都会超过默认栈。
|
||||
- 处理:整个 pending continuation、它进入的后台主循环,以及完成、取消或失败后 drain 同 Agent 后续队列时,都必须跨越独立 Tokio task 轮询边界,使上层 poll 先退栈后再轮询下一层状态机。传入边界的 future 必须先装箱;若泛型 helper 直接持有大型 future,即使随后 `spawn`,调用方 async frame 仍会把它保留在默认 worker 栈上。普通后台任务、静态委派子任务和 manifest ready-task 的首次执行统一复用 16 MiB 专用 Runtime worker,并在 worker 已启动后交接 Agent 任务锁;worker 创建或交接失败要持久化当前 run 失败。Provider 物理请求必须在持久重试 helper 与非持久压缩路径构造完整请求后、进入下层泛型 control/lifecycle helper 前装箱,不能等到底层 helper 才装箱。pending 边界继续保留结构化取消语义,父 continuation 被丢弃时同步 abort 子任务。不得逐个扩大 queue worker 栈,也不得增大 CI 的 `RUST_MIN_STACK` 掩盖问题,否则生产路径仍可能崩溃。
|
||||
- 验证:失败用例必须在未设置 `RUST_MIN_STACK` 时通过;同时覆盖普通后台委派、policy batch 全组、拒绝 pending 后重规划并 drain 下一任务,以及 pending/cancellation 回归,证明任务锁只交接一次、恢复不重复生成 isolated spawn、队列继续推进且父任务取消不遗留后台子任务。另需运行 `background_agent_runtime_can_delegate_task_to_other_agent`、`provider_retry_`、`provider_handoff_`、`response_stream_` 与 Native shell 完整门禁,全部以默认 worker 栈通过。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_queue.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_retry.rs`。
|
||||
|
||||
## Provider 可扩展不能用一个全局 protocol 枚举代替实例隔离
|
||||
|
||||
@@ -4280,7 +4389,7 @@
|
||||
- 原因:把“客户端没有收到结果”误判为“服务端没有受理”,又没有持久保留逻辑请求的幂等键和服务端返回的 `operationId`。托管 MCP 若绕过 External REST router 直接调用 worker 或 SpacetimeDB,也会形成第二套去重与状态语义。
|
||||
- 处理:一次逻辑生成只分配一个稳定幂等键。桌面 Runtime 在 POST 前先把 endpoint、精确请求体字节、SHA-256 和幂等键原子写入私有生成账本并回读一致;收到 `202 + operationId` 后先把账本升级为 `accepted` 再轮询。`accepted` 只恢复 GET;`prepared` 或提交响应丢失时,只允许校验账本身份、配置指纹和请求 SHA 后,以账本保存的原 endpoint、原始正文与同一键恢复同一逻辑 POST,不得重建画布上下文、重组正文或换键。恢复 `202` 后继续 GET,恢复再次 transport 失败仍保留原账本;轮询超时只保留既有 operation 并恢复 GET。game-chat 的 4500 秒硬截止可以结束本轮、关闭预览和客户端,但 executing 的 `canvas.asset_generate` 必须保留 pending action、provider batch 与生成账本;旧 `200` 图集的 `spritesheetResource` 允许为空,此时只在顶层 `spritesheetImageSrc` 是有效下载引用时优先使用,否则回退可用 `objectKey`。`202` 缺 operationId、状态损坏与 `postprocess-failed-source-preserved` 仍进入对账边界;其它 non-blocking warning 继续消费成功结果并单独展示。旧 `200` 兼容不改变权威 External v1 的异步契约。MCP 生成工具必须把 `idempotencyKey` 映射到同一 REST header,并复用同一 External router、owner 和任务账本。这是 External v1 的专用幂等恢复,不是通用副作用自动重放。
|
||||
- 补充:不能把“accepted 分支里没有生成 POST”误当成 GET-only 恢复。若读取账本前仍重做项目/素材目录准备、输出路径预检或请求正文构造,恢复仍可能创建远端资源或在查询 operation 前失败。恢复必须直接使用 durable snapshot;清理必须最后删除 pending 身份锚点,活动 orphan 不得自动删除。完整恢复 future 还要在默认 Tokio worker 栈下验证,不能靠测试环境调大 `RUST_MIN_STACK` 掩盖栈溢出。
|
||||
- 加固:durable snapshot 必须绑定不含明文凭据的 base URL/API Key 配置指纹,配置漂移时恢复 POST 和 GET 都必须阻断。accepted operation 明确 failed 也不能在 observation 持久化前删账本。旧 `200` durable result 只保留允许字段与安全 objectKey/相对路径,签名 URL、query/fragment 和未知字段不落盘。只有首次提交直接返回契约明确的 `400 / 401 / 403` 才可证明未入队并清理 prepared 账本;首次结果已经未知后,恢复请求的临时鉴权错误、超时、冲突、限流、网关错误及其它意外状态均保留同一账本。账本根目录、扫描和删除必须通过受控路径解析逐级拒绝符号链接,不能让项目内链接把清理目标指向项目外。
|
||||
- 加固:durable snapshot 必须绑定不含明文凭据的规范 base URL 服务身份指纹;服务地址漂移时恢复 POST 和 GET 都必须阻断,Developer API Key 轮换则必须继续原 operation。accepted operation 明确 failed 也不能在 observation 持久化前删账本。旧 `200` durable result 只保留允许字段与安全 objectKey/相对路径,签名 URL、query/fragment 和未知字段不落盘。只有首次提交直接返回契约明确的 `400 / 401 / 403` 才可证明未入队并清理 prepared 账本;首次结果已经未知后,恢复请求的临时鉴权错误、超时、冲突、限流、网关错误及其它意外状态均保留同一账本。账本根目录、扫描和删除必须通过受控路径解析逐级拒绝符号链接,不能让项目内链接把清理目标指向项目外。
|
||||
- 代理 DNS:Clash 等透明代理可能把公网对象存储域名解析到 RFC 2544 的 `198.18.0.0/15` fake-IP。下载器只对已通过鉴权 `objectKey` 或受控 legacy path 换签得到的 URL 接受“全部地址均位于该 benchmark 段”的窄例外;直接 URL、其它本机/私网地址、公私混合解析和重定向仍必须失败关闭,不能为了兼容代理整体移除 SSRF 校验。
|
||||
- 验证:覆盖“服务端已入队但提交响应丢失”后两次 POST 的 endpoint、正文 bytes 与 `Idempotency-Key` 完全相同,原键重试仍返回同一 operation,最终只出现一份 completed result 和一次计费 / 写回;恢复再次 transport 失败或临时鉴权失败仍保留同一账本;换 owner 不可见;MCP 与 REST 对同一 owner、同一请求和同一键必须命中同一 operation。
|
||||
- 关联:`server-rs/crates/api-server/src/external_generation.rs`、`server-rs/crates/api-server/src/external_mcp.rs`、`docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md`。
|
||||
@@ -4353,6 +4462,20 @@
|
||||
- 处理:焦点状态机只比较稳定 `resourceId`:`null -> id` 与 `idA -> idB` 聚焦详情,`idA -> idA` 保持当前 active element。显式收起 / Escape 才恢复原卡片与滚动;后台删除清理 focused / matching selected ID 并聚焦搜索框;项目或运行视图切换清空 trigger / restore。媒体预览副作用依赖稳定 ID、路径和类别,不因同 ID 对象重建先卸载控件。
|
||||
- 验证:媒体控件获得焦点后用同 ID 新 manifest 重渲染并断言 active element 不变;删除资源后断言详情关闭、选中清理且搜索框获得焦点;既有收起、Escape、项目切换和运行切换测试继续通过。
|
||||
|
||||
## manifest 与 revision 必须作为同一一致快照发布(2026-08-05)
|
||||
|
||||
- 现象:旧 manifest 的 React effect 在正式素材提交后才读取项目 revision,可能把“旧内容 + 新 revision”发给父级;若它先到,真正的 commit manifest 会被误判为同 revision 分叉并失败关闭。
|
||||
- 原因:manifest 和 mutation revision 分开读取,却把其中任意时刻的两个值拼成一个权威快照;单独比较 callback 到达顺序无法修复这种身份错配。
|
||||
- 处理:普通 Supervisor 投影固定执行“revision 前读 -> manifest -> revision 后读”,两次 revision 相同才发布,漂移时有界重试。素材 command/event 直接使用事务返回的完整 manifest 与对应 revision。父级按 `projectPath + projectId` 单调接受更高 revision,同 revision 只允许内容一致的重复,低 revision 和分叉都不覆盖。
|
||||
- 验证:分别覆盖 command/event 两种先后、成功后旧轮询和同 revision 不同 manifest;不能只用 eventId 去重而跳过 revision 防倒灌。
|
||||
|
||||
## 新资源自动聚焦不能把投影、布局和 DOM 当成同一时刻(2026-08-05)
|
||||
|
||||
- 现象:保存回调已经带回 manifest,但新卡片可能尚无 dependency/type 坐标或尚未提交 DOM;立即选择会得到空画布、错误滚动,迟到回调还会抢走用户后来选择的资源。
|
||||
- 原因:把 durable commit、资源投影、关系图 ready、两份布局协调和 React DOM commit 压成一个“保存成功”布尔值,缺少保存尝试身份和用户意图 generation。
|
||||
- 处理:保存开始记录 `saveAttemptId + sessionId + draftId + commitId + focusGeneration`。自动定位依次等待资源投影存在、dependency/type 两份布局 settled 且都有位置、搜索条件可见和稳定 `data-resource-id` DOM 存在;按 commitId 只执行一次。切项目、切 mode、改选择/搜索、取消或开始新 flow 都推进 generation;迟到结果仍可合并权威 manifest,但不能改变选择。隐藏时保留搜索,只由显式“清除搜索并定位”建立新 generation。
|
||||
- 验证:覆盖 manifest 已更新但布局未完成、DOM 后只聚焦一次、搜索隐藏、保存中切项目/改选择和连续保存;测试不得用 reload 或重开项目绕过阶段边界。
|
||||
|
||||
## 不要用自然语言精确 `.replace()` 维护 Runtime Prompt
|
||||
|
||||
- 现象:Prompt 文案稍作改写、增删空格或调整段落后,替换静默失效,代码中出现难以审阅的链式 `.replace()`。
|
||||
@@ -4443,6 +4566,52 @@
|
||||
- 处理:全部全局 sink 测试共用一把 test-only 串行锁,并由 RAII guard 在 `Drop` 中无条件清空;测试统一使用 `manifest_invalidation_sink_isolation_` 前缀。relay fixture 对 accept 和 payload 分别使用非阻塞轮询与总 deadline,不使用固定 sleep;生产 loopback、token、连接 / 写入超时和 payload 大小校验保持不变。
|
||||
- 验证:用 `--test-threads=2` 重复运行统一 filter,覆盖正常 relay、无事件 accept 超时、不完整 payload 超时、panic 展开清理,以及 GUI owner attach 配置与 guard 清理。
|
||||
|
||||
## 远端图片 completed 不能冒充本地资源创建成功(2026-08-05)
|
||||
|
||||
- 现象:External operation 已返回 completed,但稳定引用缺失、下载失败、正式资产事务中断或 manifest 已提交而 UI 事件丢失时,界面仍可能提前显示“资源创建成功”,重复回调还可能再次下载、写文件或登记资源。
|
||||
- 原因:把远端生成、媒体传输、本地 durability、manifest 投影、布局和选择压成一个 completed 布尔值;同时把 External idempotencyKey、operationId 或 taskId 暴露到公开草稿,导致恢复逻辑从非权威状态重建请求或误绑本地 task graph。
|
||||
- 处理:使用私有 generation ledger 保存原请求、External 身份、稳定远端引用、固定 staging token 与本地 commit 身份;公开面只投影不可逆阶段。启动时先恢复阶段三事务,再恢复原 generation;重复 completed 先检查远端引用、staging 和 committed ledger,只有 `committed | already-committed` 才进入 manifest 投影。用户取消等待只推进 focus generation,不删除账本或伪装远端取消。
|
||||
- 精修补充:`sourceImageSrc` 是可下载的稳定媒体引用,`sourceResourceId` 是资源身份,二者不能因为都可表现为字符串就填同一个 objectKey。本地 `local-asset:*` 只保留在本地 manifest 血缘;没有真实 External resourceId 时省略 `sourceResourceId`。
|
||||
- 验证:覆盖确认前零调用、同 key 连点、accepted 重启 GET-only、重复 completed、取消后迟到、下载后本地事务恢复、事件丢失、切项目/改选择、旧轮询隔离、实时布局与选择、精修血缘及敏感字段零泄漏。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/asset_canvas/generation.rs`、`apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx`、`docs/technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md`。
|
||||
|
||||
## 可恢复生成账本不能持久化 direct-upload ticket(2026-08-05)
|
||||
|
||||
- 现象:为支持参考图上传中断恢复,把完整 upload ticket 放进 generation ledger;账本随之包含 Provider host、formFields、policy、signature 或临时 Authorization,项目目录泄露即可复用临时凭证。
|
||||
- 原因:把“恢复所需的稳定远端身份”和“仅供一次上传的临时授权材料”当成同一种持久状态。原子 sidecar 只能保证写入完整,不能让敏感字段变安全。
|
||||
- 处理:ticket 结构不实现 Serialize/Deserialize,host/formFields 只在本次内存调用中使用。账本在上传前只保存稳定 bucket/objectKey;重启先用这组身份调用 object confirm,确认成功后只保留 objectKey/assetObjectId 并清掉上传中间态。账本测试必须直接序列化完整 ledger,扫描 Provider URL、Authorization、policy、signature、API Key 和 ticket 字段名。
|
||||
- 验证:运行 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml private_generation_ledger_never_serializes_upload_credentials_or_provider_url`,并继续检查公开草稿、manifest、事件和普通错误不含 prompt、operationId、Key、绝对路径或媒体正文。
|
||||
|
||||
## 客户端内部用途目录不能直接作为 legacyPrefix(2026-08-10)
|
||||
|
||||
- 现象:本地图片精修或视频、音频等全类型资源编辑点击生成后立即提示参考资源上传失败;私有账本的 `uploadBucket/uploadObjectKey/operationId/requestBodyJson` 全为空,服务端也没有 OSS、confirm、生成或扣费记录。
|
||||
- 原因:direct-upload ticket 的 `legacyPrefix` 不是任意业务目录,而是 `platform-oss::LegacyAssetPrefix` 的权威白名单值。把 `asset-canvas-references` 或 `resource-editor-references` 直接放在该字段会被 api-server 在签名之前以 `400` 拒绝;客户端若把票据、OSS 和 confirm 全折叠成一个错误码,还会掩盖真正失败阶段。
|
||||
- 处理:客户端编辑器统一使用合法私有 `legacyPrefix=generated-character-drafts`,把业务用途放入 `pathSegments`:图片画布为 `editor/asset-canvas-references/<projectId>/<draftId>/<generationId>`,全类型资源编辑为 `editor/resource-editor-references/<projectId>/<operationId>`。仍严格执行 ticket → OSS form POST → object confirm,只有 confirm 返回自洽稳定 `objectKey/assetObjectId` 后才允许提交生成;不要为内部目录扩白名单或新建上传接口。图片路径按本地校验、票据、对象上传、对象确认分别使用安全错误码,票据材料继续只驻留内存。
|
||||
- 验证:客户端端到端测试必须断言 confirm 早于生成 POST、请求使用精确前缀与 pathSegments、账本只持久化稳定对象身份;票据失败时断言 `operationId/requestBodyJson` 为空且 manifest 只有源资产。api-server 测试应断言生成的 key 位于 `generated-character-drafts/editor/...` 且 access 为 private。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/asset_canvas/generation.rs`、`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`、`server-rs/crates/api-server/src/assets.rs`。
|
||||
|
||||
## Tauri 有平台登录 Token 不代表应调用主站画布 API(2026-08-10)
|
||||
|
||||
- 现象:客户端素材画布和全类型资源编辑从 WebView 读取平台 Access Token,把它传给 Tauri command,再调用 `/api/editor/*`、`/api/assets/*` 和 `/api/runtime/external-generation/jobs/*`;代码同时保留 External 分支,导致真实 UI、Runtime 和测试使用不同路径,发布客户端还错误依赖网页画布登录态。
|
||||
- 原因:把“客户端壳有账号登录能力”误当成“客户端画布属于主站网页宿主”。主站和 Tauri 虽复用相同请求 DTO 与后端生成服务,但对外边界不同:主站使用站内认证路由,Tauri 远端媒体能力使用 Developer API Key 和 External v1 路由。
|
||||
- 处理:Tauri 前端不读取、透传或持久化站内 Access Token;Rust 只从发布 AppData 私有 `editorApi.baseUrl/apiKey` 解析 External 凭据。图片、视频、音效、BGM 的项目/素材库、上传、确认、生成、轮询与换签全部留在 `/api/external/v1`,不访问内部 job 查询或账号/profile 接口。账本只绑定 External 配置身份指纹,升级前遗留的站内 endpoint 必须进入待对账状态,不能拿 External Key 自动重放。Key 缺失或无权限只返回安全配置错误,不打印 Key、Authorization、Provider 正文或私有路径。
|
||||
- 验证:前端测试断言 command input 不含 `accessToken/apiKey`;Rust mock 服务器拒绝任何 `/api/editor/*`、`/api/assets/*` 和 `/api/runtime/external-generation/jobs/*` 请求,并覆盖 External `202`、原 operation 轮询、换签、非破坏性本地提交和账本零凭据。主站路由与 OpenAPI 未发生契约变化时不得为了客户端切换修改后端接口。
|
||||
- 关联:`apps/ai-game-creator-shell/src/features/asset-canvas/tauriImageCanvasHostAdapter.ts`、`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/src-tauri/src/project/asset_canvas/generation.rs`、`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`。
|
||||
|
||||
## prepared journal 之前同样存在正式事务崩溃窗口(2026-08-05)
|
||||
|
||||
- 现象:事务依次安装 before/after 快照后才写 journal;若进程在首个快照、全部快照或 journal 已写但 ledger 未写时退出,重启扫描看到 transaction 目录却无法进入原先只覆盖 prepared 之后的恢复状态机,可能留下孤儿目录或阻塞项目后续提交。
|
||||
- 原因:把 `prepared` 当成事务的第一个可观察持久阶段,忽略了构造 prepared 证据本身也由多次原子文件安装组成。
|
||||
- 处理:把首个快照、全部快照和 journal 后/ledger 前加入故障矩阵。无 ledger 时只允许清理受控快照与本模块临时文件;若 journal 已存在,还必须证明正式目标不存在、manifest 和 project revision 精确等于 before。未知文件、正式文件存在或权威状态漂移全部失败关闭,不能递归猜测清理。清理后同步 transaction 父目录,并允许同 commit/idempotency 身份安全重放。
|
||||
- 验证:故障矩阵逐阶段恢复;额外用同一幂等身份在快照残留清理后提交两次,必须得到一次 committed、一次 already-committed,manifest 仍只有一个 canvas asset。
|
||||
|
||||
## 宿主事件接线不能顺手复制共享 history 栈(2026-08-05)
|
||||
|
||||
- 现象:Tauri Surface 已复用共享 viewport/transform/renderer 数学,却另外维护 undo/redo refs、快照克隆和恢复逻辑;网站共享 hook 后续增加内容安全或字段恢复时,两端会静默分叉。
|
||||
- 原因:把 Pointer 事件接线、宿主生命周期胶水和可复用 history 算法放在同一组件中,误以为没有复制整个画布目录就已经满足共享源码边界。
|
||||
- 处理:两宿主直接消费共享 `useCanvasHistory`;共享 snapshot 统一覆盖 viewport、selection、图层位置和 width/height,宿主只声明本地媒体是否允许安全移除/重做。Tauri 仍可保留 Pointer capture/epoch/host callback 接线,但选择、平移、缩放、变换、renderer 和 history 状态机不得在宿主重写。
|
||||
- 验证:主站 history 定向测试覆盖 resize undo/redo;Tauri 新建、导入、编辑、撤销重做和 durable commit 用例必须在同一共享 hook 下通过。
|
||||
|
||||
## 编辑器生成不能把传输重试、参考图截断和客户端 provenance 当成独立小问题(2026-08-05)
|
||||
|
||||
- 现象:生成 POST 首次已经入队但响应丢失时,客户端自动重试产生第二个任务;第 6 张或更多参考图仍显示在 UI / 元数据里,却没有送给 provider;直接构造请求还能把任意资源 ID 写成最终素材引用。
|
||||
@@ -4541,3 +4710,26 @@
|
||||
- 原因:pool 只按 LLM 凭据和路由复用进程,节点身份只用于进程内 thread map。任一 stdout framing、子进程退出或连接故障都会 drain 整个进程的 pending/turn router,使所有共享节点同时失去可信终态。
|
||||
- 处理:pool key 必须包含 `projectId + agentId + sessionId + runId`,每个权威节点直接持有独立 app-server 子进程;同节点 turn 还要串行,不能向同一 thread 并发 `turn/start`。只发送当前 CLI schema 定义的字段;stderr 使用有界内存尾部并先脱敏再进入 Runner 诊断。
|
||||
- 验证:至少两个节点并发各跑多轮,确认存在两个 app-server PID;终止其中一个后只有对应节点进入 reconciliation,另一个仍能收到 `turn/completed`。旧 AppData 缺 `agentMode` 且含非 Responses 路由时必须保留 `provider`,不能在项目自动恢复时批量失败。
|
||||
|
||||
## 模拟 Provider 的隔离 AppData 测试必须显式固定执行模式(2026-08-11)
|
||||
|
||||
- 现象:测试已经写入本地 mock `baseUrl / apiKey / model`,却收不到任何 HTTP 请求,日志反而显示 Codex app-server 启动或退出;Native shell 全量中多个后台 Agent 用例一起超时。
|
||||
- 原因:新安装和没有迁移上下文的隔离 AppData 默认使用 `codex_app_server`。只写 `agentLlm` 不能表达测试要走 HTTP Provider;直接切换 runtime config dir 的 fixture 也不会经过会自动补 `agentMode` 的测试 helper。
|
||||
- 处理:任何要断言模拟 HTTP Provider 请求的配置都必须显式写 `agentMode=provider`。测试 helper 可以统一补齐,但直接写隔离 AppData 的 fixture 仍须在自身 JSON 中声明,不能依赖仓库 `.env`、用户 AppData 或历史迁移。
|
||||
- 验证:先单跑失败用例确认请求命中 mock server,再执行完整 `npm run check:native-shells`;日志中不得出现该用例启动 Codex CLI/app-server,所有 Provider/MCP 请求数量和顺序按 fixture 闭合。
|
||||
|
||||
## Tauri 生成与资源编辑恢复不能依赖 UI 快照、旧 Key 指纹或队列首项(2026-08-11)
|
||||
|
||||
- 现象:应用重启后,任务视频、项目版本或 refine 草稿无法恢复;轮换 Developer API Key 后已有 operation 被误判为配置变化,已受理任务一次 401/403 还可能永久进入对账;文本 Provider 已成功但尚未 staging 时崩溃会重复调用。目录中放入大量无关文件还能绕过 pending 扫描上限。一条远端已明确失败的老 operation 会持续占据队列首项,挡住后续已受理或已下载任务;manifest 已写而 project revision 未写时,又可能被误标为 committed,或者 journal 已证明提交后因项目继续合法修改而无法补 ledger。durable committed 后遗留 staging 可能因一次删除失败而被误报为提交失败,也可能在正式媒体或 manifest 身份已经漂移时被直接删除;manifest 已有派生子版本而 journal 缺失,或旧 journal 没有 revision 身份时,也可能被猜成已经提交。派生视频再次编辑时若把 `assetObjectId` 当远端引用,生成会失败或指向错误身份。
|
||||
- 原因:早期账本只保存显示层资源 ID,恢复时又依赖当前页面资源对象;refine `draftId` 只在组件 Map;配置指纹混入 Key 并把认证错误写成状态机终态;Provider 正文从内存直接进入解析/staging;扫描计数只在识别出 pending JSON 后递增;本地登记 ID 与 External generation 接受的稳定 `objectKey` 没有分层。恢复 UI 只选排序后第一项,而账本又没有远端终态失败/归档阶段;资产提交恢复把整个历史 after manifest 当作永久相等条件,没有区分目标事务事实与后续合法提交;旧 version journal 只保存 base/target 数值,不能证明完整 project revision before/after 身份。
|
||||
- 处理:新账本冻结完整源快照,旧账本从权威 manifest、完成任务和版本记录有界恢复;refine 从正式 sidecar 按项目、意图、源素材和 active 状态唯一发现。服务身份用 `service-origin-v1` 哈希规范化 External base URL,确认 UI 只展示去除路径与凭据的服务 origin;旧 Key-bound 指纹由快照绑定的显式挑战迁移,确认前零网络动作,已受理任务换 Key 后只 GET 原 operation。Provider 调用前先持久化 request-issued,成功正文再写 durable handoff 后解析/staging;issued 无 handoff 只能对账。扫描在读取每个目录条目时先计数,任何文件都消耗预算。提交前复验源摘要,远端请求只使用账本已确认的稳定 `objectKey`,恢复始终复用原 operation 和请求字节。
|
||||
- 队列与事务:独立恢复面板必须展示后端权威队列的所有 operation,读取失败不能伪装为空。`remote-failed` 不再重放,只能显式标为 `archived` 并保留账本;`reconciliation-required` 不能归档。派生 asset 使用 `prepared -> media-installed -> manifest-written -> revision-written -> committed` journal,只对可证明状态前向恢复;尚未证明目标写入时严格核对 before/after,已证明目标 asset/media 与 target revision 后允许 manifest/revision 被后续合法提交继续推进,并补齐同一 ledger。committed 后只有 staging 与正式媒体摘要一致、manifest 按 ID 或路径唯一精确匹配 journal asset 时才尽力清理;删除 I/O 失败保持 durable committed,身份或媒体漂移保留 staging 并进入对账。version journal 同样冻结 project revision before/after 身份;manifest 已有子版本但 journal 缺失,或旧 journal 面对已推进 revision 无法补证时都失败关闭。
|
||||
- 验证:覆盖跨进程唯一 refine 草稿发现和多候选失败关闭、文本 Provider 成功到 staging 崩溃后零重复调用、任务视频/版本旧账本恢复、所有目录条目上限、Key 轮换与旧 Key 无法验证时的显式确认、Accepted 后 401/403 再换 Key 只 GET 原 operation、远端明确失败只归档且零新网络/扣费、三条乱序恢复队列、项目切换迟到结果、asset transaction 各崩溃阶段、revision 后项目继续合法修改仍补齐 ledger、committed 后 staging 清理成功/删除 I/O 失败/媒体或 manifest 漂移保留、源摘要漂移拒绝、committed 视频二次派生,以及 manifest 子版本缺 journal、旧 version journal 无法证明 revision 推进与 version journal exactly-once。
|
||||
|
||||
## 无限画布延迟草稿与零位移不能制造新状态(2026-08-11)
|
||||
|
||||
- 现象:r5 的 `loadDraft` 比 r6 更晚回包时会把草稿回退;pointerdown 后没有任何移动,pointerup 仍增加一条空 undo 并触发 CAS 保存;Tauri 自行维护 Shift toggle 后,Shift 单击唯一选中图层会意外清空选择。同一 `canvas.failed` 视图还可能把草稿保存或提交故障显示成生成重试。
|
||||
- 原因:延迟回包只与发起时 revision 比较,没有在落地时复核当前最高 revision;指针按下就 capture history,而不是等首次真实几何变化;宿主复制了共享 selection 规则;失败状态没有携带发生故障的 operation 类别。
|
||||
- 处理:generation progress、保存队列、生成/提交回包和延迟 `loadDraft` 统一用当前 scope、触发最低 revision 与回包当下草稿的单调门禁;同 revision 只允许完整相等回包。Tauri 指针和键盘选择复用 `resolveLayerPointerSelection`。pointerdown 只冻结快照,首次真实 move/resize/pan 才 capture 一次;零位移、未变选择和锁定图层不增加 undo、documentVersion 或草稿保存。
|
||||
- 失败边界:`canvas.failed` 必须携带 `generation / draft-save / asset-commit / recovery / cancellation`,只有 `generation` 失败显示“返回修改/重新确认”。保存/CAS 只重试或重载,提交/恢复只安全恢复或对账,取消故障只保留草稿继续编辑;初始恢复失败也不得进入生成重试。
|
||||
- 验证:用 deferred Promise 覆盖 r5/r6 逆序、保存与 progress 交错和 scope 切换;同时覆盖 Shift 单选自身、多选拖动、指针完整序列、零位移、首次有效移动只一条 history,以及五类失败的可访问名称与按钮集。
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -346,9 +346,13 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复
|
||||
- 源码:`server-rs/crates/spacetime-module/src/external_generation.rs`
|
||||
- 现役覆盖:worker claim 只允许 `source_module = editor-canvas`;下述逐玩法生成和写回描述均为退役前历史。历史 pending / running 行继续保留原状态,不得领取、失败收口或改写 payload。
|
||||
- 用途:外部生成 worker 的内部持久任务队列;`GENARRATIVE_EXTERNAL_GENERATION_MODE=queue` 时,`api-server` HTTP 角色只入队,`external-generation-worker` 角色通过 claim lease 领取、续租、执行,并用 `lease_token` 栅栏回写阶段、完成 / 失败。队列行继续保存 worker 执行、计费与滚动发布兼容所需字段,末尾可选 `phase` 只取 `generating / processing`;claim 写 `generating`,真实进入抠图处理时由受 `job_id + worker_id + lease_token` 保护的 procedure 写 `processing`。phase procedure 以结构化结果区分 `LeaseFencingRejected` 与 `OtherRejected`;`LeaseFencingRejected` 立即终止,`OtherRejected` 以及 SDK 的 `Procedure` / `Runtime` 错误不重试,只有 `Build` / `ConnectDropped` / `Timeout` 在同一个 job attempt 内重试一次。该重试只重新上报 phase,不把任务写回 `pending`,也不重新调用 provider;编辑器 job 入队固定 `max_attempts=1`,第二次传输失败后任务进入 `failed`,不会回到 `pending` 或从 provider 生成起点重跑。用户可见任务列表、价格、状态、阶段、未确认终态数量和通知确认时间的正式读取事实源已经迁到 `external_generation_job_summary`;BFF 不得再为列表 / 详情 / acknowledge 读取该大表。拼图 `compile_puzzle_draft` 的前置 `compile_puzzle_agent_draft`、`generate_puzzle_images` 与 `generate_puzzle_ui_background` 的业务写回也在对应 SpacetimeDB transaction 内校验 `job_id + worker_id + lease_token`、job kind、owner 和 source entity,避免过期 worker 写 session / work profile;图片画布编辑器的 `editor_image_generation`、`editor_image_edit`、`editor_background_removal`、`editor_icon_spritesheet_generation`、`editor_ui_design_asset_extraction`、`editor_character_animation_generation`、`editor_video_generation`、`editor_sound_effect_generation` 和 `editor_background_music_generation` 复用同一队列表。结构化 canvas 激活后,当前 worker completion 先以读取时 canvas revision 执行 CAS,并发冲突时拒绝覆盖并保留可诊断失败;目标是进一步收口为受 lease 栅栏保护的单事务幂等写入 `editor_project_resource`、结果 `editor_canvas_layer`、`editor_canvas_generation_dialog` 终态和 canvas revision。未激活 canvas 在 2 MiB 上限内继续走 legacy `editor_canvas.layers_json` 兼容写回。前端只通过 BFF job 状态轮询和项目快照读取恢复完成态。`GENARRATIVE_EXTERNAL_GENERATION_MODE=inline` 时不创建该队列行,三个 external generation guard 字段必须同时为空才允许 api-server 受控同步写回,半空 guard 仍会拒绝。worker 成功写回业务事实后才能 complete job;业务失败态写回成功后才能 fail job,失败态未写回时保留租约等待后续重领。
|
||||
- 素材写回:worker 成功后仍经 `api-server` facade 写入 `editor_project_resource` / `editor_asset`;结构化 canvas 的 layer / dialog / revision 与未激活 canvas 的 legacy `layers_json` 分流按上一条执行,前端不直接发明正式完成态。
|
||||
- 载荷约束:本次先对 `source_module = editor-canvas` 的 `request_payload_json` / `result_payload_json` 实施有限大小合法 JSON、任意层级禁止 `data:` / `blob:` 的双层门禁,只保存 worker 执行必需的普通参数和已登记媒体引用。画布 Agent 来源的任务可在 `result_payload_json.editor-agent-tool-call-result` 中保存有界的轻量结果和已登记媒体引用,供后端按已有 `externalJobId + owner_user_id` 定向懒回填;External v1 以及客户端素材画布通过登录态和稳定 `Idempotency-Key` 提交的图片生成/精修任务可在 `result_payload_json.result` 保存同一套 compact 稳定引用,供各自 owner-safe 单任务查询完成本地下载,其它编辑器任务保持元数据结果,并可保存有界的 `warning.code/reason`。其它玩法在完成各自参考图资源化之前不由本次门禁静默改变既有请求契约。该主表只供 worker claim / 执行、受控维护、画布 Agent 定向回填以及上述两类已完成任务的精确 `job_id + owner_user_id` 结果读取;任务列表、队列概览与 acknowledge 不得返回或解析 payload,普通单任务状态也不得返回完整 payload。客户端素材画布查询只能取有界 `result` 子对象;结果不存在时保持原元数据状态响应,不能把普通已完成任务改成 `502`。画布 Agent 懒回填必须经对应工具 formatter 归一为有界轻量媒体引用后写入 OSS 会话,不能把原始 payload 直接透传前端。
|
||||
- 非阻断告警:角色形象、图标图集和 UI 素材提取已保存 provider 原图、但透明背景处理最终失败时,以原图唯一主图完成任务;透明图和切片不写入画布。这个 source-only 降级只包住透明背景处理的最终失败,phase 上报、provider 原图持久化、透明处理图持久化或画布写回失败仍按任务错误传播。图标 / UI 透明图集成功但自动拆分降级时仍保留透明图集;通用 `warning` 与 `sliceWarning` 只在「透明背景最终失败」这一条上互斥,风格归一化或像素规整产生的通用 `warning` 可与 `sliceWarning` 并存。两类成功降级都以既有 `completed` 状态收口,不新增状态值:source-only 的 inline / external v1 响应使用结构化 `warning.code/reason`,仅拆分失败的 inline / external v1 响应继续使用既有 `sliceWarning.code/reason`,其 `reason` 保留原始诊断;queue worker 才把两者归一为有界的 `result_payload_json.warning`:只有一条时原样保留完整 `reason`,两条并存时按“通用在前、拆分在后”拼接且 `code` 收敛为 `multiple-generation-warnings`(两条 `code` 相同则沿用原 `code`),不允许任何一条被丢弃;`sliceWarning.reason` 无论是否并存都由 worker 添加“图集已生成,但自动拆分未完成:”前缀,拼接结果最后统一做长度上界收敛。除上述画布 Agent 定向回填、External v1 和客户端素材画布幂等任务所需的 compact 稳定引用外,队列结果不保存完整图片、切片列表、临时媒体 URL 或 provider 原始响应。
|
||||
- 2026-08-06 收口覆盖:上一条用途描述中“当前先 CAS、单事务仍是目标”的旧句已作废。现役编辑器生成不再组合调用 object confirm、resource create、asset create、canvas save 和 job complete。`api-server` 只准备稳定候选,再经 `spacetime-client` 调用 `persist_editor_generation_result_and_return`;procedure 在同一 `try_with_tx` 内写入可选 `asset_object`、全部 `editor_project_resource`、`editor_asset`、可选 `asset_entity_binding`、可选 canvas V2 CAS、queue job 终态和 `editor_generation_operation` receipt。结构化 canvas 的 layer / dialog / revision 与未激活 canvas 的 legacy `layers_json` 仍经既有 V2 布局验证分流,前端不直接发明正式完成态。queue 首次提交在同一快照验证 owner、job kind、request fingerprint 和有效 `job_id + worker_id + lease_token`;统一 procedure 已完成 job 后 worker 不得再单独 complete。
|
||||
- 载荷约束:本次先对 `source_module = editor-canvas` 的 `request_payload_json` / `result_payload_json` 实施有限大小合法 JSON、任意层级禁止 `data:` / `blob:` 的双层门禁,只保存 worker 执行必需的普通参数和已登记媒体引用。画布 Agent 来源的任务可在 `result_payload_json.editor-agent-tool-call-result` 中保存有界的轻量结果和已登记媒体引用,供后端按已有 `externalJobId + owner_user_id` 定向懒回填;其它编辑器任务保持元数据结果,并可保存有界的 `warning.code/reason`。其它玩法在完成各自参考图资源化之前不由本次门禁静默改变既有请求契约。该主表只供 worker claim / 执行、受控维护以及画布 Agent 的定向结果回填读取;正式用户任务列表、单任务状态、队列概览与 acknowledge 不得返回或解析这两个 payload。画布 Agent 懒回填必须经对应工具 formatter 归一为有界轻量媒体引用后写入 OSS 会话,不能把原始 payload 直接透传前端。
|
||||
- 非阻断告警:角色形象、图标图集和 UI 素材提取已保存 provider 原图、但透明背景处理最终失败时,以原图唯一主图完成任务;透明图和切片不写入画布。这个 source-only 降级只包住透明背景处理的最终失败,phase 上报、provider 原图持久化、透明处理图持久化或画布写回失败仍按任务错误传播。图标 / UI 透明图集成功但自动拆分降级时仍保留透明图集;通用 `warning` 与 `sliceWarning` 只在「透明背景最终失败」这一条上互斥,风格归一化或像素规整产生的通用 `warning` 可与 `sliceWarning` 并存。两类成功降级都以既有 `completed` 状态收口,不新增状态值:source-only 的 inline / external v1 响应使用结构化 `warning.code/reason`,仅拆分失败的 inline / external v1 响应继续使用既有 `sliceWarning.code/reason`,其 `reason` 保留原始诊断;queue worker 才把两者归一为有界的 `result_payload_json.warning`:只有一条时原样保留完整 `reason`,两条并存时按“通用在前、拆分在后”拼接且 `code` 收敛为 `multiple-generation-warnings`(两条 `code` 相同则沿用原 `code`),不允许任何一条被丢弃;`sliceWarning.reason` 无论是否并存都由 worker 添加“图集已生成,但自动拆分未完成:”前缀,拼接结果最后统一做长度上界收敛。除上述画布 Agent 定向回填的轻量结果外,队列结果不保存图片、切片列表或媒体 URL。
|
||||
- 客户端素材画布专项例外:通过登录态和稳定 `Idempotency-Key` 提交的图片生成 / 精修,与 External v1 一样,只允许 owner-safe 的精确单任务读取有界 `result` 子对象;普通编辑器单任务状态、列表、队列概览和 acknowledge 仍不得返回完整 payload。
|
||||
|
||||
- 2026-07-29 收口补充:上条 source-only 的“透明背景处理最终失败”同时包含 Alpha 比例漂移超过 `5%`、provider 原图修复性回读失败、Alpha 回贴失败和透明图完整解码失败;三条链路共用 helper,只写已保存 provider 原图画布层,图标 / UI 固定 `iconImageSrcs=[]`、`sliceWarning=null`,不得写透明图、派生资源或切片。provider 原图本身解码失败时在首次持久化前失败,不允许 `512×512` 元数据兜底。图标自动拆分、手动拆分与 UI 提取先在受 2 路 CPU semaphore、30 秒 / 请求 deadline 保护的 blocking prepare 中完成解码、透明化、连通域和 bounds 排序;platform 对全部原始连通域设置 `4096` 硬上限、用空间网格查询邻近辅助候选,并在首片 PNG 编码前同时执行 `maxOutputSlices=64` 与全部 padding crop 总像素预算。prepare 返回共享 RGBA + bounds 计划,api-server 再以容量 `2` 的有界管线按需编码、共享 HTTP client 并发 OSS `PUT + HEAD`,OSS 连接 / 单请求超时固定为 `10s / 60s`;手动入口在下载最大 `32 MiB` 来源对象前取得独立内存 admission,同一 admission 覆盖下载、prepare 到最后一片上传结束并在数据库调用前释放,CPU permit 只覆盖实际 CPU 阶段。全部对象上传验证成功后,切片的 `asset_object + editor_project_resource + editor_asset + editor_asset_group_cohort` 由单个受 editor generation runtime service identity 保护的 SpacetimeDB procedure 在一次 `try_with_tx` 中原子写入;resource / asset ID 由 owner + task + 序号稳定派生,已有同 ID 素材仅在内容完全一致时幂等复用,来源资源必须存在且与派生资源同 owner / project;上传中途失败不得写部分资源、素材或 cohort,不确定结果重放不得复制整批素材。自动超限只保留整张可信透明图并返回稳定 `sliceWarning`,不写切片;手动超限在首次持久化前返回 `422`。
|
||||
|
||||
@@ -358,7 +362,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复
|
||||
|
||||
- Rust 结构体:`ExternalGenerationJobSummary`
|
||||
- 源码:`server-rs/crates/spacetime-module/src/external_generation.rs`
|
||||
- 用途:外部生成正式任务列表的轻量投影,按 `job_id` 保存 owner、来源、状态、可选 `phase`、价格、有界错误摘要、通知确认时间、各阶段时间和入队时提取的 `request_prompt`,不包含 request/result payload、worker lease 或 dedupe 内部字段。错误摘要统一拒绝内联媒体并限制为 2048 字符;列表在单次 owner 扫描中同时计数并只保留请求 limit 的固定大小 top-N,不得先收集全量历史再截断。enqueue、claim、renew、phase update、complete、fail 事务同步投影;acknowledge 只更新该轻量表并写审计事件,后续主任务同步必须保留已有确认时间,禁止为了写确认时间加载 / 重写大 payload 行。BFF 的列表、状态和确认只调用 summary procedure;`running + processing` 映射为“正在处理”,其它 running(含旧行 `phase=None`)映射为“正在生成”。历史终态任务由迁移操作员的游标分批 maintenance procedure 在压缩 payload 时同步回填摘要,正式列表不得为兼容旧数据回扫完整主表。
|
||||
- 用途:外部生成正式任务列表的轻量投影,按 `job_id` 保存 owner、来源、状态、可选 `phase`、价格、有界错误摘要、通知确认时间、各阶段时间和入队时提取的 `request_prompt`,不包含 request/result payload、worker lease 或 dedupe 内部字段。错误摘要统一拒绝内联媒体并限制为 2048 字符;列表在单次 owner 扫描中同时计数并只保留请求 limit 的固定大小 top-N,不得先收集全量历史再截断。enqueue、claim、renew、phase update、complete、fail 事务同步投影;acknowledge 只更新该轻量表并写审计事件,后续主任务同步必须保留已有确认时间,禁止为了写确认时间加载 / 重写大 payload 行。BFF 的列表、概览、确认和非终态单任务状态只调用 summary procedure;客户端素材画布的 owner-safe 单任务查询仅在 summary 已是 `completed` 时,按同一 `job_id + owner_user_id` 额外读取 artifacts 并最多返回 compact `result`,不存在则省略。`running + processing` 映射为“正在处理”,其它 running(含旧行 `phase=None`)映射为“正在生成”。历史终态任务由迁移操作员的游标分批 maintenance procedure 在压缩 payload 时同步回填摘要,正式列表不得为兼容旧数据回扫完整主表。
|
||||
- 非阻断告警:摘要字段 `warning_message` 是展示投影,由完成任务的轻量 `result_payload_json.warning.reason` 原样提取,不等同于公开 inline / external v1 的原始结构化诊断字段。complete 和历史 backfill 共用同一构建路径;历史任务按其结果载荷中已写入的 `reason` 快照投影,不为格式升级重写或补前缀。单 job 状态和任务列表 BFF 以 `warning: string` 返回该可直接展示的完整文案,不再返回结构化 code,Web 不得再次补前缀或按字符串推断告警类型。错误与告警摘要都不复制内联媒体并限制为 2048 字符。`phase` 与 `warning_message` 分别表示当前执行阶段和成功降级提示,不得混用;worker / BFF / Web 必须同版本协调发布,不保证滚动混部或旧 Web 缓存下的字符串语义兼容。
|
||||
- 正式读取 procedure 为 `get_external_generation_job_summary_and_return`、`list_external_generation_job_summaries_and_return` 和 `acknowledge_external_generation_job_summaries_and_return`。历史维护 procedure 为 `compact_external_generation_job_payloads_and_return` 与 `backfill_external_generation_job_summaries_and_return`,仅 migration operator 可调用;运维入口统一使用 `npm run spacetime:external-generation:maintain -- ...`,默认 dry-run、单批最多 25 条。B-tree cursor 选择阶段最多反序列化 `limit + 1` 行,apply 再按主键逐条读取选中行;怀疑存在单行异常巨型 JSON 时必须先使用 `--limit 1`。payload 压缩额外固定使用 `source_module = editor-canvas` 的复合 cursor 索引,不得静默改写其它玩法历史任务。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user