校准资源替换的 PRD、验收文档、技术方案与项目记忆
- PRD §5.3 补实现口径:替换落盘 = 改绑定 + 追加新版本、子版本绑定 = 源绑定去掉源素材并保证替换素材在集合里、前后身份用推导记录、三项兼容性逐条判据与 sizeSpecEqual 的降级声明、失败即拒绝、入口与可见效果;同时标注 :364-373 的 ProjectResourceDescriptor 是历史四分类形状(其 width/height/durationMs 在 manifest 里并不存在) - PRD §5.4 前的版本章节与 §6 P1、§7.4 第 4 条按当前状态校准:替换已实施,但版本聚焦态仍不提供创建/替换/切换/回滚按钮,资源替换入口在资源卡工具条 - PRD 新增 §7.8 P1 资源替换验收六条(入口放行判据、候选禁用与原因、只追加写入形状、两条绑定路径、拒绝零副作用、成功后切版本但不重载预览) - 【测试用例】AGC资源工作台V3端到端验收:删掉「PRD L387 兼容性三项属 C6 取消范围」那句(改为明确不属于),更新 §7.3 工具条 opt-in 清单,补三条已知边界(运行画面不因改绑定而变、候选弹窗不加载缩略图、picker 在 AGC 首次使用),§8 覆盖与不覆盖范围补资源替换 - 【技术方案】AI游戏创作智能体App实施计划::669 的「本阶段不提供版本创建、替换、切换」与 :641 的布局切片范围按当前状态校准,并在文末新增「2026-09-11 AGC 资源工作台 V3:版本级资源替换」一节,写全命令、写入语义、前后身份推导口径、三项兼容性判据与降级声明、入口、成功后行为与验证数字 - decision-log 新增一条(背景 / 决策 / 恒等绑定硬约束 / 判据 / 降级 / 边界 / 影响范围 / 验证方式 / 关联文档):说明本次按 PRD §3.2 / §5.3 恢复「替换后创建下一迭代版本」,与 2026-09-10 那条「不创建新版本、替换功能整条取消」的口径关系,以及 C6 的候选/队列/审核仍不做 - Issue #309 的三处口径(C1 决策、贯穿性决策 6、验收总纲的「不创建新版本 / 不产生新版本」)按远程写确认规则**未擅自改动**,需要在用户确认后再改
This commit is contained in:
@@ -187,10 +187,10 @@ idle -> focused(document|art|audio|version) -> idle
|
||||
- 文档:合法 Agent 文本回执直接使用对话投影内容;项目文件只允许读取当前 manifest 已登记资产或已完成任务产物中的 Markdown、文本、JSON、YAML、TOML,必须经过 `file.read` auto 权限、相对路径、项目边界、普通文件、符号链接 / 硬链接、读取漂移、2 MiB、UTF-8 与扩展名白名单校验。正文使用不执行 HTML、不加载远程图片、不产生可点击外链的安全 Markdown 渲染,并在中央画布内独立滚动;读取失败显示错误空态。聊天侧 `/read` 回执中的文件正文必须作为代码块渲染为 `<pre><code>`,以便用户审阅源码字面量但不执行其中的 HTML;Markdown 渲染使用 `react-markdown` 的 `skipHtml`,依赖库对代码 span / fenced code 的文本转义;不得在整段 Markdown 上预转义 HTML,否则会把代码中的 `<tag>` 双重转义为字面量 `<tag>`。
|
||||
- 美术:PNG、JPEG、WEBP、GIF、SVG、AVIF、BMP、MP4、WebM、MOV 只在资源卡本体中按既有受控读取、文件签名与解码门禁展示;中央详情不重复加载或放大图片 / 视频本体。SVG 继续拒绝脚本、事件处理器、外部资源引用和实体声明。
|
||||
- 音频:只读取 manifest 已登记音频或已成功导入且登记到 manifest 的附件,按文件签名接受 MP3、WAV、OGG / Opus、M4A、AAC、FLAC;聚焦态展示实际格式、浏览器解码后的时长以及带播放进度和暂停能力的内置播放器。音频任务声明中的未登记路径继续不得读取或播放。
|
||||
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源替换仍留给后续切片。
|
||||
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源替换(2026-09-11 实施)不在版本聚焦态提供,入口在资源卡选中工具条:选中被**当前版本**绑定的素材后替换成另一已登记素材,落盘为「追加下一迭代版本 + 新绑定」,详见 §3.2 / §5.3。
|
||||
- mentor 最新决定:资源聚焦不提供工具栏,也不提供工具侧边栏。
|
||||
- 音频:只读取 manifest 已登记音频或已成功导入且登记到 manifest 的附件,按文件签名接受 MP3、WAV、OGG / Opus、M4A、AAC、FLAC;聚焦态展示实际格式、浏览器解码后的时长以及带播放进度和暂停能力的内置播放器。音频任务声明中的未登记路径继续不得读取或播放。
|
||||
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源编辑只允许追加继承源绑定并记录提示词的子版本,不允许原地替换或修改源版本。
|
||||
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源编辑只允许追加继承源绑定并记录提示词的子版本,不允许原地替换或修改源版本;资源替换同样不改既有版本,而是追加一条 `resource-replacement` 子版本(§5.3)。
|
||||
- 资源聚焦不提供通用工具栏或工具侧边栏;图片聚焦态允许一个明确的“精修资源”业务动作进入素材创作无限画布,该动作不是在聚焦容器中内嵌编辑器或恢复通用工具栏。
|
||||
- 点击资源后,中央主视窗从 `resource-overview.list` 切换为 `resource-overview.focused.document / art / audio / version`,左侧平台导航、右侧 Supervisor 对话和底部 Agent 状态栏保持原位;聚焦容器以路径、类型、来源任务、依赖层级、同类型上下游和版本字段为首屏主体,不使用页面级浮层或可拖动标题栏。文档正文与按意图加载的音频控制位于元数据之后。
|
||||
- 焦点转换以稳定资源 ID 为准。只有从资源列表进入详情或从一个资源 ID 切换到另一个 ID 时聚焦详情 region;同一资源 ID 因 manifest 更新而重新投影时,不得抢走详情内音频 / 视频控件、文档链接或收起按钮的当前焦点。
|
||||
@@ -384,8 +384,21 @@ type ProjectVersionResourceReplacement = {
|
||||
};
|
||||
```
|
||||
|
||||
> `ProjectResourceDescriptor.category` 是**历史形状**(旧四分类轴),已被本节上方 2026-09-10 / 2026-09-11 的六分类收口取代;实现以 manifest `assets[].category` 的六分类与读时自愈口径为准,本 DTO 只保留作名词参照。`width / height / durationMs` 也只在该历史形状里存在,manifest 资产表今天并没有这些字段(见下方 `sizeSpecEqual` 的降级声明)。
|
||||
|
||||
三项兼容性必须同时为 true 才能创建下一版本。
|
||||
|
||||
替换实现口径(2026-09-11):
|
||||
|
||||
- **落盘 = 改绑定 + 追加新版本**:新版本 `createdReason='resource-replacement'`、`parentVersionId` 指向被替换的源版本;既有版本记录一个字节不改,走 §5.4 的只追加写入边界。子版本的 `resourceBindings` = 源版本绑定集合**去掉源素材**,并保证**替换素材在集合里**(替换素材是源版本创建之后才登记时,按源素材原来的位置插回);恒等绑定口径下不能把源素材那条槽位改写成替换素材,替换素材若在源版本创建时就已登记、本来就在集合里,改写会撞「资源槽位重复」。
|
||||
- **替换前后资源身份用推导记录**(不新增 manifest / 跨端契约字段,依据 §5.4 的版本字段表):父版本里有 `asset:{sourceResourceId}` 绑定是「替换前」,子版本里有 `asset:{replacementResourceId}` 绑定是「替换后」,配对由 `parentVersionId` + `createdReason` 确定;差异 `父 − 子 = {源素材}`,`子 − 父 = {替换素材}`(替换素材在源版本创建时就已登记时 `子 − 父` 为空集,此时版本记录无法单独反推配对)。
|
||||
- **三项兼容性判据**(后端权威,前端只呈现,不重算):
|
||||
- `categoryEqual`:两侧资产的功能分类相等,用**读时自愈**口径(见本节 2026-09-11 收口)。Rust 侧与之逐分支一致的实现是 `game_creation_app_asset_category_with_read_time_healing`。
|
||||
- `subtypeEqual`:两侧 `kind` 的 canonical 值相等(别名表见 `GAME_CREATION_APP_CANONICAL_ASSET_KINDS` / `canonical_game_creation_app_asset_kind`)。
|
||||
- `sizeSpecEqual`:**规范化媒体格式相等,且「任一方有事实的维度必须相等」**(`imageSequenceFrames[0]` 的宽高、`imageSequenceDurationMs`;两个维度都无事实时不阻断)。**降级声明**:manifest 资产表没有 `width / height / durationMs` 字段,且现役写入侧(上传、派生、画板回传、生成回流)几乎全部写 `imageSequenceFrames: None`,因此该项**在真实数据上退化为「媒体格式相等」**(`png ↔ webp` 会判为不同而被拒绝)。这是刻意接受的最小实现;要支持跨图片格式替换,必须先给 manifest asset 增尺寸字段并在写入侧回填(跨端契约变更),届时把「缺失即未知」的口径写进契约。
|
||||
- **失败即拒绝**:任一项不为 true、源版本不存在、源版本未绑定该素材、替换素材未登记、替换素材与源素材相同、`expectedProjectId` / `expectedProjectRevision` CAS 冲突,都必须拒绝并说明原因;拒绝时 manifest 与项目 revision 都不变,界面不得出现成功态。
|
||||
- **入口与可见效果**:入口在资源卡选中工具条,只对「manifest 资产 + 被当前版本绑定」的素材放行;候选弹窗列出全部候选、不兼容项渲染但禁用并给出原因。替换成功后当前版本选择切到新版本,但**不自动重载 / 重启运行中的预览**(§3.2 末条);按 §7.4 的口径,改绑定不做运行时资源重映射,运行画面本身不会因改绑定而变化。
|
||||
|
||||
### 5.4 游戏迭代版本(P1)
|
||||
|
||||
阶段六实现状态(2026-08-13 更新):正式版本业务真相扩展在本地项目 `.agent/manifest.json` 的可选 `versions` 字段中;旧项目字段缺失时等价于空列表,不根据 checkpoint、布局 sidecar、静态检查、失败试玩或单独的 `game-creator-project-revision.v1` 自动伪造版本。版本数组只允许追加,已有记录不得删除、重排或修改;首轮没有版本创建按钮。自主首板只有在当前 revision 的 `preview.validate` 已成功形成持久试玩回执后,才幂等追加首条 `initial` 版本,并绑定当时 manifest 中全部已登记资源;同一完成态恢复不得重复创建。已有正式版本时,后续试玩通过不自动追加版本,仍由明确的资源派生事务创建子版本。
|
||||
@@ -473,7 +486,7 @@ type ProjectAgentMudPointAttribution = {
|
||||
|
||||
- 已实施依赖/类型两套坐标持久化、首次默认不重叠布局、历史坐标跨重启恢复、自动协调 CAS 冲突处理与资源卡手动拖动。
|
||||
- 资源关系线在布局持久化验收通过后单独实施,不与本切片捆绑伪造完成。
|
||||
- 已实施正式版本不可变模型、版本卡、父子关系与引用资源高亮;“编辑资源”可追加继承源绑定并记录提示词的子版本,资源直接替换、运行版本切换和兼容性迁移仍待后续切片。
|
||||
- 已实施正式版本不可变模型、版本卡、父子关系与引用资源高亮;“编辑资源”可追加继承源绑定并记录提示词的子版本;资源替换(2026-09-11)已按 §3.2 / §5.3 实施为「改绑定 + 追加 `resource-replacement` 子版本」,入口在资源卡选中工具条;运行版本切换(C7)已完成;兼容性迁移仍待后续切片。
|
||||
- 素材创作无限画布阶段一按权威专题一次交付图片导入、编辑、生成、导出、草稿恢复、正式本地回写、即时投影和焦点竞态闭环。
|
||||
- 高级抠图、图集、角色动画、视频时间线编辑和音频波形级编辑按后续切片实施;当前视频走源引用派生,音频只做语义重制。
|
||||
|
||||
@@ -530,7 +543,7 @@ type ProjectAgentMudPointAttribution = {
|
||||
1. manifest 缺少 `versions` 时旧项目正常打开且不显示伪造版本;存在合法记录时,固定“项目版本”分区按追加顺序显示稳定版本卡。
|
||||
2. 根版本、父版本和直接子版本关系在卡片或聚焦态可见;悬空父版本、自引用、重复 ID、非递增修订、倒退时间、重复 slot 和超限数字均失败关闭。
|
||||
3. 点击版本卡后,当前 manifest 中仍存在的绑定资产卡被高亮;历史已删除资产只在版本详情保留 ID,不创建幽灵卡,也不把 External Editor resource ID 猜成 manifest asset ID。
|
||||
4. 版本聚焦态展示身份、修订、创建原因、父子关系、创建时间和 slot 绑定;“编辑资源”只追加继承源绑定并记录提示词的子版本,不提供原地替换、切换、回滚或运行按钮。
|
||||
4. 版本聚焦态展示身份、修订、创建原因、父子关系、创建时间和 slot 绑定;“编辑资源”只追加继承源绑定并记录提示词的子版本,不提供原地替换、切换、回滚或运行按钮。资源替换**也不在版本聚焦态提供**:它的入口是资源卡选中工具条(§5.3),并且同样不原地改既有版本,而是追加一条新版本;本条限制的是版本聚焦态这个容器,不限制资源卡入口。
|
||||
5. 任意现有 manifest 写入只能保留磁盘版本前缀并追加新记录;存储边界以跨进程专用锁串行覆盖旧状态读取、前缀校验、安装和回读,修改、删除、重排或并发旧快照覆盖已有版本时写入失败。
|
||||
6. 版本选择和高亮不写 manifest、布局 sidecar 或 project revision;dependency / type 两种布局都可显示绑定高亮,既有依赖关系 SVG 语义不变。
|
||||
|
||||
@@ -582,6 +595,15 @@ type ProjectAgentMudPointAttribution = {
|
||||
8. 右侧正式钱包入口在普通工作台和 UI Editor 子路由都始终可见、键盘可达并能打开余额、充值与使用详情;路由切换不得使入口消失或失去交互。
|
||||
9. “资源依赖 / 资源类型”以连通分段按钮呈现,点击与键盘操作均只保留一个选中项;使用 Tab 定位和键盘切换时焦点指示清晰、完整,不被容器边界或 `overflow` 裁切。
|
||||
|
||||
### 7.8 P1 资源替换验收
|
||||
|
||||
1. 入口只对「manifest 资产 + 被当前版本绑定」的素材渲染;未绑定素材(版本创建后才登记、或不属于当前版本绑定集合)一律不出现入口,也不出现"点了没反应"的假按钮。
|
||||
2. 候选弹窗列出后端返回的全部候选,不兼容项**渲染但禁用**并显示不等维度(分类 / 类型 / 尺寸规格);不得隐藏不兼容候选,不得合成非候选素材,不得在前端重算兼容性判据。
|
||||
3. 写入后的 manifest:既有版本记录逐项不变(含 `projectRevision` / `resourceBindings` / `createdAt`),新版本追加在末尾,`createdReason='resource-replacement'`、`parentVersionId` 指向源版本、`projectRevision` 严格大于父版本且等于推进后的项目 revision。
|
||||
4. 子版本绑定 = 源版本绑定去掉源素材并保证替换素材在集合里;不得出现重复 `slotId`。替换素材在源版本创建后才登记时,子父差异恰好"一减一增"且顺序稳定。
|
||||
5. 三项兼容性任一为 false、源版本不存在、源版本未绑定该素材、替换素材未登记、替换素材与源素材相同、`expectedProjectId` / `expectedProjectRevision` 冲突,都必须在写入前拒绝;拒绝时 manifest、`versions` 与项目 revision 都不变,投影里不得出现新的版本卡或新的"当前使用"高亮。
|
||||
6. 成功后重读 manifest 并把当前版本选择切到新版本;**不**自动重载或重启运行中的预览(§3.2 末条),也不做运行时资源重映射。可见变化只有:版本下拉多一条「资源替换 · 时间」、资源卡"当前使用"高亮移到替换素材、`@` 面板"当前版本素材"更新。
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
- 当前不开放“新增资源”产品入口。图片精修候选保持私有且不污染资源总览;“设为最终图”只允许保持原 asset ID、以新正式文件和事务方式切换 manifest 指针,不原地覆写旧文件。其他资源编辑继续追加派生 asset 或子版本。
|
||||
|
||||
@@ -8364,3 +8364,16 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 不做什么:不新增替代导航、不做栏目下拉/分段控件、不改 `resourceBookModel` 的 `main / child + category` 目标模型、不改每「排序模式 + 栏目」独立 viewport 的既有约定、不动 sidecar schema 与跨端契约。
|
||||
- 验证方式:新增反向守卫用例(`资源栏目大纲` 标签、`.game-resource-outline` 类名、对应 `<nav>` 都不再渲染)与切栏目链路用例(总览卡片进空栏目 → 下一页切下一个 → 标题栏「资源总览」返回 → 「所有资源」入口往返);变异验证:把 `<nav aria-label="资源栏目大纲">` 加回去 → 守卫红灯,只加回类名 → 守卫同样红灯(均已实测)。既有断言未放宽:栏目顺序断言改钉在总览缩略卡片的 `aria-label` 序列上(第 0 张「所有资源」+ 固定七栏目),只服务导航的断言按「四不写」直接删除。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`(:56 / :512)、`docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md`(:5 / :33 / :53 / :54 / :57 / :102 / :103)、`docs/project-memory/shared-memory/pitfalls.md`。
|
||||
|
||||
## 2026-09-11 AGC 资源替换按 PRD 恢复为「版本级替换」:改绑定落在追加的新版本上
|
||||
|
||||
- 背景:PRD §3.2(`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md:36-41`)要求「替换版本引用资源时创建下一迭代版本,不原地修改既有版本」,并要求新版本记录 `parentVersionId`、替换前后资源身份与创建原因;§5.3(:356-387)给出 `ProjectVersionResourceReplacement` 与「三项兼容性必须同时为 true 才能创建下一版本」。本文件 2026-09-10 那条(`decision-log.md:8263`)与 Issue #309 的 C1 决策 / 贯穿性决策 6 / 验收总纲当时写的是相反口径:「改 manifest 绑定……**不创建新版本**」「替换功能(候选素材 / 替换关系 / 替换队列 / Agent 审核 / 单点替换)整条取消,不实现」。用户裁决:**按 PRD 来**,实际机制仍是「改 manifest 绑定」。
|
||||
- 决策:资源替换 = **改绑定 + 追加下一迭代版本**。新命令 `replace_local_project_version_resource`(写,`asset.register`)在持项目写锁与 `expectedProjectId + expectedProjectRevision` CAS 下,一次 `mutate_manifest_at` 写入里追加 `createdReason='resource-replacement'` 的子版本(`parentVersionId` 指向被替换的源版本),子版本绑定 = 源版本绑定**去掉源素材**并保证**替换素材在集合里**;只读命令 `read_local_project_version_replacement_candidates`(`asset.list`)返回候选与后端权威的三项兼容性。既有版本记录一个字节不改,**继续走默认空放行集合**,不新增/放宽 `mutate_manifest_at_allowing_version_removals`。
|
||||
- 恒等绑定口径的硬约束(实测得出):每个版本的 `resourceBindings` 是「该版本使用的素材集合」而不是槽位表,替换素材若在源版本创建时就已登记,它本来就在集合里,因此**不能**把源素材那条槽位改写成替换素材(会撞「资源槽位重复」);正确落盘是「源素材从集合里消失 + 替换素材在集合里」,替换素材是后来才登记时按源素材原位置插回。
|
||||
- 三项兼容性判据:`categoryEqual` 用 PRD §5.3 的**读时自愈**口径(为此在 `server-rs/crates/shared-contracts` 新增 `game_creation_app_asset_category_with_read_time_healing`,与 `packages/shared` 的 `gameCreationAppAssetCategory` 逐分支一致并配用例锁定——此前 Rust 反序列化只有「缺失/非法 → 按 kind 派生」,没有自愈,两侧口径并不一致);`subtypeEqual` 比较 canonical `kind`;`sizeSpecEqual` 比较规范化媒体格式 + 已知帧宽高 + 已知时长,任一方有事实的维度必须相等。
|
||||
- 已知降级(刻意接受,不得当成完整实现):manifest 资产表没有 `width / height / durationMs`,现役写入侧几乎全写 `imageSequenceFrames: None`,所以 `sizeSpecEqual` 在真实数据上退化为**媒体格式相等**(`png ↔ webp` 会被拒)。要支持跨图片格式替换,必须先给 manifest asset 增尺寸字段并在写入侧回填(跨端契约变更)。
|
||||
- 替换前后资源身份:按 PRD §5.4 的版本字段表口径**用推导记录**,不新增 manifest / 跨端契约字段(`父 − 子 = {源素材}`、`子 − 父 = {替换素材}`,配对由 `parentVersionId` + `createdReason` 确定)。已知限制:替换素材在源版本创建时就已登记时 `子 − 父` 为空集,版本记录无法单独反推配对;要无歧义持久化配对须先给 `GameIterationVersion` 增字段。
|
||||
- 边界:C6 的候选素材 / 替换关系图 / 替换队列 / Agent 审核 / 批量提交**仍然不做**(#309 该部分不变);§3.2 末条「当前运行中的版本不消费尚未生成的新版本变更」保持——成功后只切记录层当前版本,不自动重载/重启预览,也不做运行时资源重映射。入口只在资源卡选中工具条、且只对「manifest 资产 + 被当前版本绑定」的素材渲染;候选弹窗复用美术画布的 `ImageCanvasProjectAssetPickerDialog`(AGC 首次使用),以可选 prop 扩展且默认值保持网页端行为逐字不变。
|
||||
- 影响范围:`server-rs/crates/shared-contracts/src/game_creation_app.rs`、`src-tauri/src/project/version_resource_replacement.rs`(新增)、`commands.rs`、`main.rs`、`tests/version_resource_replacement.rs`(新增)、`src/features/resource-canvas/resourceVersionReplacement{Model,Transport}.ts`(新增)、`view/project-development/index.tsx`、`src/components/image-editor/ImageCanvasProjectAssetPickerDialog.tsx`、`apps/ai-game-creator-shell/src/styles.css`。
|
||||
- 验证方式:Rust 定向 8 条 + `shared-contracts` 20 条;变异验证三条(去掉媒体格式维度 → 2 条转红;绕开读时自愈 → 自愈用例转红;改成原地改既有版本 → 4 条转红并被「项目版本记录写入后不可修改、删除或重排」拦下,证明只追加守卫真的在挡)。前端新增 12 条,变异验证两条(放宽入口判据 → 「不给假按钮」转红;失败路径静默关弹窗 → 「保留弹窗显示原因」转红)。AGC 全量 1231 passed / 4 skipped / 0 failed;共享美术画布组件 1385 passed;typecheck / `cargo check --locked --all-targets` / `check:encoding` / `git diff --check` 全绿。跨端契约、SpacetimeDB schema、manifest 结构与布局 sidecar schema 均未改动。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`(§3.2 保持,§5.3 补实现口径与降级声明,§7.8 新增替换验收)、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`(新增同章节)、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md`(§7.3 第 1 条已按本次口径改写)、Issue #309。
|
||||
|
||||
@@ -638,7 +638,7 @@ game-project/
|
||||
- 五栏目页签的未读红点由 `ProjectDevelopmentView` 在会话内比较各栏目稳定资源 ID 派生:首次快照只建立基线,只有非当前栏目后续出现新 ID 才加入未读集合;所有栏目切换路径统一以当前栏目变化清除对应未读,项目 scope 变化时重建基线并清空。该状态以 `projectPath + projectId` 隔离,不进入 manifest、布局 sidecar、localStorage、sessionStorage 或后端契约。
|
||||
- type 默认布局固定按 `subtype -> mediaType -> label -> id` 排序。manifest 资产的 subtype 使用 `asset.kind`,任务产物、导入附件和 Agent 文本成果使用稳定的来源 fallback;subtype 必须进入资源协调签名,不能因 MIME 相同而退化成按名称混排。
|
||||
- 自动协调保存失败时保留当前会话布局;CAS 冲突载入对方最新布局,需要继续协调时最多追加两次重试,持续跨窗口竞争时停止自旋。用户提示只说明“布局已在其他窗口更新”,不要求重新拖动。损坏、未知 schema、身份冲突、超限与链接文件失败关闭,不能用空布局覆盖原文件。
|
||||
- 本布局持久化切片不包含资源关系线、资源替换、聚焦态持久化、分区高度、分区内容倍率、整个画布平移、搜索 / 筛选条件、当前 mode,也不修改 `api-server` 或 SpacetimeDB。资源关系线、当前会话内中央聚焦、分区独立高度与内容倍率已在后续独立前端切片接入,都不改变本段 sidecar 合同;其余 P1 能力继续独立实施。
|
||||
- 本布局持久化切片不包含资源关系线、资源替换、聚焦态持久化、分区高度、分区内容倍率、整个画布平移、搜索 / 筛选条件、当前 mode,也不修改 `api-server` 或 SpacetimeDB。资源关系线、当前会话内中央聚焦、分区独立高度与内容倍率已在后续独立前端切片接入,都不改变本段 sidecar 合同;其余 P1 能力继续独立实施。**资源替换已在 2026-09-11 按 PRD §3.2 / §5.3 独立实施**(见本文末「版本级资源替换」一节),同样不改布局 sidecar 合同。
|
||||
- 本资源总览布局 sidecar 不包含资源关系线、资源替换、聚焦态持久化、资源总览缩放 / 平移、搜索 / 筛选条件、当前 mode,也不修改 `api-server` 或 SpacetimeDB。资源关系线与当前会话内中央聚焦已在后续独立前端切片接入;素材创作 viewport 和图层使用独立草稿 schema,不能写入 `game-creator-resource-layout.v1`。
|
||||
|
||||
历史实施顺序已完成 TypeScript / Rust DTO、Tauri sidecar/CAS、前端纯模型与持久 Hook。二维手动拖动接线现已暂缓;重新开放前必须先更新 PRD 与验收合同。任何后续步骤不得用 `localStorage`、manifest 字段或只在当前 React 会话有效的状态冒充项目持久化。
|
||||
@@ -666,7 +666,7 @@ game-project/
|
||||
|
||||
2026-08-03 阶段六:正式迭代版本直接扩展本地 `.agent/manifest.json`,不新增 checkpoint / layout sidecar / SpacetimeDB 平行业务真相。共享 Rust / TypeScript 合同新增可选 `versions: GameIterationVersion[]`;旧项目缺失字段时只读为空,不回填。Rust 在 manifest 读写边界校验版本唯一性、父先于子、根/原因一致、父子修订与时间单调、slot 唯一和 JavaScript 安全整数,并在覆盖已有 manifest 前要求磁盘版本数组是新数组的逐项相等前缀,从存储边界保证历史记录不可修改、删除或重排。2026-08-13 起,自主首板在当前 revision 的 `preview.validate` 成功结果和持久试玩回执均落盘后,幂等追加唯一首条 `initial` 版本,并以稳定 `asset:<manifest asset id>` 槽位绑定当时全部已登记资源;失败试玩、静态 smoke、checkpoint 和普通预览状态不得触发版本创建,恢复重放和已有版本项目也不得重复追加。
|
||||
|
||||
工作台资源投影只从 `manifest.versions` 构建版本卡,按数组追加顺序生成稳定“版本 N”标题;不再接收前端独立 `projectVersions` 注入。`resourceBindings.resourceId` 只解释为 manifest asset ID,并映射到现有 `asset:<id>` 卡片。选中版本后在 dependency / type 两种布局中高亮当前仍存在的绑定资产;缺失历史资产只留在版本聚焦详情,不能合成幽灵卡或猜测 External Editor resource ID。版本聚焦复用中央只读容器,展示身份、修订、原因、父版本、直接子版本、创建时间与 slot 绑定。本阶段不提供版本创建、替换、切换、回滚、测试切片或运行态消费入口。
|
||||
工作台资源投影只从 `manifest.versions` 构建版本卡,按数组追加顺序生成稳定“版本 N”标题;不再接收前端独立 `projectVersions` 注入。`resourceBindings.resourceId` 只解释为 manifest asset ID,并映射到现有 `asset:<id>` 卡片。选中版本后在 dependency / type 两种布局中高亮当前仍存在的绑定资产;缺失历史资产只留在版本聚焦详情,不能合成幽灵卡或猜测 External Editor resource ID。版本聚焦复用中央只读容器,展示身份、修订、原因、父版本、直接子版本、创建时间与 slot 绑定。**本阶段**不提供版本创建、替换、切换、回滚、测试切片或运行态消费入口;其中**运行版本切换、资源替换与版本创建**已由 2026-09-10 / 2026-09-11 的后续切片补齐(见下方命令段),版本聚焦态本身仍不提供创建、替换、切换、回滚按钮。
|
||||
|
||||
历史命令式 drag preview 句柄与局部连接索引可以保留,但项目工作台不再向资源卡传入该入口。拖动热路径、4096 张真实卡片拖动重渲染和 Chromium p95 门槛统一暂缓;当前回归只要求 Pointer Move 不改变卡片坐标、SVG path 或布局 revision。`ResizeObserver` 仍保持单图层单实例,任何实时 DOM 几何都不得通过 Tauri IPC 往返 Rust。
|
||||
|
||||
@@ -1335,3 +1335,14 @@ DirectProject 使用 `approvalPolicy=never`,避免每次原生调用再经过
|
||||
- 等待预算耗尽时按 `project.write_lock.wait_exhausted` 记录 `commandId`、尝试次数、等待毫秒数、`projection=`(contention / permission_denied)与持锁方身份,Unix 上明确判定的权限拒绝按 `project.write_lock.permission_denied` 记录;争用不在零等待入口里逐次记账,避免有界等待的上千次重试淹没日志。这条日志正是 Issue #318 现场缺的“谁在持锁、是不是自己人”。**这条日志与终态改判都只在真的等过(`max_attempts > 1`)时发生**:单次试探(hydrate 的 `try_acquire_*`)不写 `wait_exhausted`(`waitedMs≈0` 会让“耗尽”失去意义,而 hydrate 每次状态变化都会撞一次锁,写成日志就是噪声),也不做终态改判。
|
||||
- 定向验收覆盖:同进程重叠写等待后成功、同一轮并行写多个文件、有界等待不占 runtime worker(`current_thread` + 心跳任务)、活外部进程持锁(错误带 `ownerIsSelf=false` 且锁文件不被回收)、ACL 拒绝不投影成争用,外加两条平台无关判据用例(重试性只由错误码决定、终态改判三条件)——后两条让 Linux CI 也能盯住 Windows 分支。对应 `project_lock_recovery`、`direct_tool_bridge` 与 `project/write_lock` 定向测试;`tests/project_tools.rs` 既有的 `runtime_project_write_lock_waits_for_delete_pending_target` 继续覆盖“带句柄的 delete-pending 必须等到成功”。
|
||||
- 仍待收口(后续事项):① 其余仍用零等待取锁的入口(`command.exec / project.verify / memory / conversation / task / checkpoint / 预览 / UI 编辑器 / 资源编辑器 / Tauri 命令`)本批不改,遇到同类争用仍会立刻失败;零等待入口无法区分“拆链窗口 / ACL 拒绝”,因此在前缀不变的前提下补一句“锁文件此刻不存在,可能是删除挂起、删除拆链窗口或权限 / ACL 拒绝”。② 锁策略已按“单一职责”收口到 `project/write_lock.rs`(887 行:取锁、等待分类、持锁方诊断、残留回收),`project/filesystem.rs` 回到项目文件 IO(680 行);仍待收口的是 Direct 锁用例,它们还留在 `direct_tool_bridge.rs`(3135 行,锁用例与桥实现混在一起),后续移到 `tests/project_lock_recovery.rs` 或独立测试文件。③ 行为级 Windows 用例(delete-pending 等)仍只在 Windows 本地执行,CI 没有 Windows runner;关键判据已参数化到 Linux 可覆盖,行为级覆盖仍需本地执行或后续补 runner。
|
||||
|
||||
## 2026-09-11 AGC 资源工作台 V3:版本级资源替换(改绑定 + 追加下一迭代版本)
|
||||
|
||||
- **口径**:按 PRD §3.2 / §5.3 实施「替换某个版本引用的资源」——**不原地修改既有版本、不动资源文件、不建文件副本**,落盘为「追加一条 `createdReason='resource-replacement'` 的新版本 + 新绑定」。Issue #309 的 C6(候选素材 / 替换关系 / 替换队列 / Agent 审核 / 批量提交)仍然不做;C6 原设计里被一并取消的“替换后生成新版本”按 PRD 口径恢复。
|
||||
- **命令**:`read_local_project_version_resource_replacement_candidates`(只读,`asset.list`)与 `replace_local_project_version_resource`(写,`asset.register`),实现落在 `src-tauri/src/project/version_resource_replacement.rs`。入参都是单个 `input` 对象、`deny_unknown_fields`:读命令 `{projectPath, sourceVersionId, sourceResourceId}`,写命令额外要求 `expectedProjectId + expectedProjectRevision + replacementResourceId`。出参的 `replacement` 形状就是 PRD §5.3 的 `ProjectVersionResourceReplacement`;这两个 DTO 是 Tauri 本地 DTO,**不进跨端契约**。
|
||||
- **写入语义**:持项目写锁(与删除 / 重命名 / 标签同一把)→ 锁内复核 `projectId` 与 durable revision(CAS 失败报 `project-identity-conflict` / `project-revision-conflict` 且零写入)→ `mutate_manifest_at`(**从不**调用 `mutate_manifest_at_allowing_version_removals`)追加子版本 → 成功后推进一次项目 revision,且子版本 `projectRevision` 必须等于推进后的值。子版本 `resourceBindings` = 源版本绑定**去掉源素材**,并保证**替换素材在集合里**(替换素材是源版本创建之后才登记时按源素材原位置插回)。恒等绑定口径下不能把源素材那条槽位改写成替换素材:替换素材若在源版本创建时就已登记,它本来就在集合里,改写会撞「资源槽位重复」。
|
||||
- **替换前后资源身份**:按 PRD §5.4 的版本字段表口径**用推导记录**,不新增 manifest / 跨端契约字段。父版本里有 `asset:{sourceResourceId}` 是「替换前」,子版本里有 `asset:{replacementResourceId}` 是「替换后」,配对由 `parentVersionId` + `createdReason` 确定;差异 `父 − 子 = {源素材}`、`子 − 父 = {替换素材}`。已知限制:替换素材在源版本创建时就已登记时 `子 − 父` 为空集,版本记录无法单独反推配对;要无歧义持久化配对必须先给 `GameIterationVersion` 增字段(跨端契约变更)。
|
||||
- **三项兼容性判据(后端权威,前端只呈现)**:`categoryEqual` 用**读时自愈**口径——Rust 侧新增 `game_creation_app_asset_category_with_read_time_healing`(`server-rs/crates/shared-contracts`),与 `packages/shared` 的 `gameCreationAppAssetCategory` 逐分支一致并配定向用例锁定;`subtypeEqual` 比较 canonical `kind`;`sizeSpecEqual` 比较「规范化媒体格式 + 已知帧宽高 + 已知时长」,任一方有事实的维度必须相等。**降级声明**:manifest 资产表没有 `width / height / durationMs`,现役写入侧几乎全写 `imageSequenceFrames: None`,因此该项在真实数据上退化为「媒体格式相等」(`png ↔ webp` 会被拒);要支持跨格式替换须先给 manifest asset 增尺寸字段(跨端契约变更)。
|
||||
- **入口**:资源卡选中工具条的宿主 `extraActions` 新增「替换素材」,判据复用现役 `isResourceUsedByCurrentVersion`(manifest 身份 + 被当前版本绑定),未绑定素材不渲染入口,避免假按钮;不动 `ImageCanvasSelectedLayerToolbarAction` 共享 union、不改 `resourceCanvasToolbarModel` 的 `supportedActions`。候选弹窗复用 `ImageCanvasProjectAssetPickerDialog`(该弹窗在 AGC 侧首次使用),以可选 prop 扩展:`singleSelect` / `assetBlockedReasons` / `renderAssetMedia` / `selectionNoun` / `errorMessage`,**默认值保持网页端美术画布行为逐字不变**。候选行渲染类型占位而不挂 `<img>`:AGC 的预览读取要经带 scope 的原生调度器拿 Blob URL,弹窗内没有同步 `src`。
|
||||
- **成功后行为**:重读 manifest 并把当前版本选择切到新版本(复用 `selectActiveVersion`,已改为 `useCallback` 固定身份),**不自动重载 / 重启运行中的预览**(PRD §3.2 末条),不做运行时资源重映射。可见变化只有版本下拉多一条「资源替换 · 时间」、资源卡「当前使用」高亮移动、`@` 面板「当前版本素材」更新。
|
||||
- **验证**:Rust 定向 8 条(`tests/version_resource_replacement.rs`)+ `shared-contracts` 20 条;变异验证三条(去掉格式维度 → 2 条转红;绕开读时自愈 → 自愈用例转红;改成原地改既有版本 → 4 条转红并被「项目版本记录写入后不可修改、删除或重排」拦下,证明只追加守卫真的在挡)。前端新增 12 条(`tests/resourceVersionReplacement.test.tsx` 真链路 4 条、`tests/resourceVersionReplacementModel.test.ts` 8 条),变异验证两条(放宽入口判据 → 「不给假按钮」转红;失败路径静默关弹窗 → 「保留弹窗显示原因」转红)。AGC 全量 1231 passed / 4 skipped / 0 failed;共享美术画布组件 1385 passed;`npm run ai-game-creator-shell:typecheck`、`cargo check --locked --all-targets`、`npm run check:encoding`、`git diff --check` 全绿。`/api/external/v1`、SpacetimeDB schema、`packages/shared` 与 `shared-contracts` 的 wire DTO、manifest 结构、布局 sidecar schema 均未改动。
|
||||
|
||||
@@ -231,15 +231,17 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
|
||||
### 7.3 已知未做 / 已取消(不要报成缺陷)
|
||||
|
||||
1. **C6 替换关系与替换队列整条取消**(#309)。PRD L387 的 `ProjectVersionResourceReplacement` 兼容性三项属该取消范围。
|
||||
1. **C6 的候选素材 / 替换关系 / 替换队列 / Agent 审核 / 批量提交整条取消**(#309)。PRD §5.3(L387 附近)的 `ProjectVersionResourceReplacement` 兼容性三项**不属于**该取消范围:2026-09-11 已按「改绑定 + 追加下一迭代版本」实施,入口在资源卡选中工具条,验收见 PRD §7.8。
|
||||
2. **画布生成入口只覆盖 3 类**(视频 / 音效 / 背景音乐);图片等类型会直接报"当前资源类型不支持无源生成",这是设计而非缺陷。
|
||||
3. **工具条 7 个动作按 opt-in 不渲染**:重绘 / 裁剪扩图 / 去背景 / 像素完美 / 切图集 / 提取 UI 素材 / 角色动画;只有 `快速编辑` 与 `下载` 真渲染(宿主另附加 `UI 编辑器` / `编辑标签` / `重命名`)。
|
||||
4. **C7 只做记录层 + UI 层**:切换版本即重载当前预览;版本化资源解析机制未实现。
|
||||
3. **工具条 7 个动作按 opt-in 不渲染**:重绘 / 裁剪扩图 / 去背景 / 像素完美 / 切图集 / 提取 UI 素材 / 角色动画;只有 `快速编辑` 与 `下载` 真渲染(宿主另附加 `UI 编辑器` / `编辑标签` / `重命名`,以及**只在素材被当前版本绑定时**出现的 `替换素材`)。
|
||||
4. **C7 只做记录层 + UI 层**:切换版本即重载当前预览;版本化资源解析机制未实现。资源替换同样不做运行时资源重映射:改绑定只改「这个版本用哪些素材」的记录,运行画面要按游戏自身引用的资源路径渲染。
|
||||
5. **「首轮进度投影」无编码级判据**,可见性由底部 Agent 状态栏承载。
|
||||
6. **浮出工具条的视觉位置未做真机核验**(jsdom 不执行 Web Animations,只覆盖结构与 class)。
|
||||
7. **重命名不改游戏源码中的旧 `assets/<name>` 引用**。
|
||||
8. **C9 参考图弹窗在 AGC 侧入口未确认**:全仓检索 `添加参考图` 只命中共享 `src/components/image-editor/*`,AGC 的 `ResourceCanvasGenerationPanelView` 未引用它,可能处于"组件已实现、AGC 入口未接"状态。建议真机点一次生成 / 编辑面板确认;若无入口,C9 不计入本轮主线。
|
||||
8. **C9 参考图弹窗在 AGC 侧入口未确认**:全仓检索 `添加参考图` 只命中共享 `src/components/image-editor/*`,AGC 的 `ResourceCanvasGenerationPanelView` 未引用它,可能处于"组件已实现、AGC 入口未接"状态。建议真机点一次生成 / 编辑面板确认;若无入口,C9 不计入本轮主线。**注**:资源替换复用同一个 `ImageCanvasProjectAssetPickerDialog`,它在 AGC 侧是首次使用(同一个弹窗、不同的 opt-in 参数)。
|
||||
9. **运行视图存在「点选素材」按钮**,但 #309 的"不做"清单包含运行画面点选,口径冲突待定。
|
||||
10. **替换成功后运行画面不会立刻变**:按 C7 与本轮口径只做记录层 + UI 层,可见变化是版本下拉多一条「资源替换」、资源卡"当前使用"高亮与 `@` 面板当前版本素材更新;这不等于替换失败。
|
||||
11. **替换候选弹窗不加载缩略图**:AGC 的素材预览要经带 scope 的原生读取器拿 Blob URL,弹窗内没有同步 `src`,因此候选行只渲染类型占位(不给 `<img>` 喂空串、不挂破图)。
|
||||
|
||||
### 7.4 文档与代码的偏差(需明确按哪个判)
|
||||
|
||||
@@ -253,5 +255,5 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
|
||||
## 8. 验收边界
|
||||
|
||||
- 本用例覆盖:资源总览 / 栏目分页画布、资源卡预览与本地受控读取、资源卡选择与多选、快速编辑派生、画布生成入口(3 类)、编辑标签、重命名、下载、删除三分支、版本切换与当前使用高亮、聊天 @ 引用与原子 chip、AI 润色与发送前提醒、本地预览启动与退出收尾、两份布局 sidecar 台账。
|
||||
- 本用例不覆盖(另走专项或定向测试):双窗口 CAS 冲突、大规模 fixture 性能、素材创作无限画布阶段一至五的草稿 / 事务 / 恢复矩阵(见 `【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 与配套专题)、UI 编辑器子路由、主站图片编辑器回归(见 PRD §7.7)。
|
||||
- 本用例覆盖:资源总览 / 栏目分页画布、资源卡预览与本地受控读取、资源卡选择与多选、快速编辑派生、画布生成入口(3 类)、编辑标签、重命名、下载、删除三分支、版本切换与当前使用高亮、**资源替换(入口放行判据 / 候选禁用与原因 / 写入载荷 / 拒绝零副作用 / 成功后切版本不重载预览)**、聊天 @ 引用与原子 chip、AI 润色与发送前提醒、本地预览启动与退出收尾、两份布局 sidecar 台账。
|
||||
- 本用例不覆盖(另走专项或定向测试):双窗口 CAS 冲突、大规模 fixture 性能、素材创作无限画布阶段一至五的草稿 / 事务 / 恢复矩阵(见 `【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 与配套专题)、UI 编辑器子路由、主站图片编辑器回归(见 PRD §7.7)。资源替换的 Rust 定向矩阵(只追加守卫、两条绑定路径、CAS、四条拒绝路径、读时自愈口径)见 `apps/ai-game-creator-shell/src-tauri/src/tests/version_resource_replacement.rs`,前端矩阵见 `apps/ai-game-creator-shell/tests/resourceVersionReplacement*.test.ts(x)`。
|
||||
|
||||
Reference in New Issue
Block a user