合并 feat/agc-resource-replace:把「替换素材」接进资源工作台
- 解冲突 project/manifest.rs:保留版本写入公共体 write_manifest_locked_with_version_guard(版本只追加 + 绑定改写放行),并把 schema 校验并入该公共体,两侧语义都不丢 - 解冲突 shared-contracts:取当前分支的读显示口径 game_creation_app_asset_effective_category(含与 TS 交叉钉住的 EFFECTIVE_CATEGORY_CONTRACT 矩阵),同步改写 version_resource_replacement 对旧函数名的调用 - 解冲突 index.tsx:两处均为纯 import 追加,两侧都保留 - 解冲突 decision-log 与技术方案文档:两侧条目都是追加,全部保留 - 随之进入本分支:版本绑定改写窄放行通道、直接替换命令与其兼容性判据、资源卡工具条「替换素材」入口
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
- 文本、SVG 与 Agent 回执在 Provider 调用前必须先持久化 request-issued;成功响应必须先原子安装到与原 operation、请求指纹和内容摘要绑定的私有 durable handoff,再做 envelope 解析、格式校验和 staging。issued 后缺少可信 handoff 只能对账;handoff 已存在且校验通过时恢复只消费该正文,两种情况都禁止再次调用 Provider。
|
||||
- 普通客户端视频使用 `POST /api/editor/videos/generations`。有稳定远端引用时直接作为 `referenceVideoSrcs`,只有本地文件时先走 `/api/assets/direct-upload-tickets`、OSS 表单上传和 `/api/assets/objects/confirm`,再提交同一逻辑生成;结果必须下载到新的本地文件并登记远端稳定身份。
|
||||
- 普通客户端音效和背景音乐分别使用 `POST /api/editor/audios/sound-effects/generations` 与 `POST /api/editor/audios/background-music/generations`。当前接口没有源音频引用字段,因此产品语义固定为"基于原资源语义的派生重制",界面不得描述为对源波形的裁剪、变声或局部修改;结果仍必须引用源资源身份并保留原音频。
|
||||
- 项目版本编辑固定追加 `parentVersionId` 指向源版本的子版本,继承源版本资源绑定并记录本轮编辑提示;已有版本数组元素不可修改、删除或重排。
|
||||
- 项目版本编辑固定追加 `parentVersionId` 指向源版本的子版本,继承源版本资源绑定并记录本轮编辑提示;已有版本数组元素不可修改、删除或重排(两个例外见 PRD §5.4 的存储边界两条:删除素材时连带删版本、资源直接替换时改写该版本的 `resourceBindings`)。
|
||||
- 角色动画派生结果的 manifest `kind` 固定写 `character-animation`,`source.generationKind` 同步保留 `character-animation`,媒体仍以预览视频加正式序列帧登记。图片编辑等普通派生继续继承源资源语义 kind;后续如需改写为 canonical kind,只能在读取投影或显式迁移中完成,不重写历史 manifest。
|
||||
- 图片编辑、视频、音效和背景音乐请求统一在 `generationInputs.source` 写入专用消费身份 `game-creator-resource-editor`;普通内部路由和高级 External 路由都必须实际读取并传递同一稳定 `Idempotency-Key`。队列完成态只向该消费身份返回经过裁剪的稳定 `objectKey / resource / asset` 引用和必要媒体元数据,不暴露 provider、worker、队列内部字段或临时签名 URL。
|
||||
|
||||
|
||||
@@ -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 按「直接替换」口径独立实施**(见本文末「资源替换」一节),同样不改布局 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。
|
||||
|
||||
@@ -1340,3 +1340,15 @@ DirectProject 使用 `approvalPolicy=never`,避免每次原生调用再经过
|
||||
|
||||
- AGC 前端请求客户端配套后端的鉴权 API(包括 `/api/llm/models`)收到 `401` 时,共享进行中的 refresh 请求;确认当前用户并安装 Rust / Runner 会话后,用新 access token 最多重试原请求一次。`403` 权限拒绝不触发续期;续期失败保留原鉴权错误,账号切换或登出后不重发旧请求。
|
||||
- DirectProject 的 Rust/app-server 对话调用返回鉴权失效时,前端先刷新客户端平台会话并重新提交同一 `clientTurnId`;平台会话代次变化后由 app-server pool 使用新 access token 建立连接,避免长时间运行后必须重新登录。
|
||||
## 2026-09-11 AGC 资源工作台 V3:资源替换(**直接替换**:只改该版本的绑定,不建新版本)
|
||||
|
||||
- **口径**:用户当日 DDL 口径「替换这块先做成直接替换」→ 改 `.agent/manifest.json` 里该版本的绑定指向另一个已登记资源:**不创建新版本、不删除版本、不动资源文件、不建文件副本**。PRD §3.2 的「创建下一迭代版本」与 §5.3 的 `ProjectVersionResourceReplacement` /「三项兼容性必须同时为 true 才能创建下一版本」**保留为未来合同**,今天不实现;Issue #309 的 C6(候选素材 / 替换关系 / 替换队列 / Agent 审核 / 批量提交)仍然不做。
|
||||
- **写入通道(新增的第二条、也是更窄的版本放行口)**:`project/manifest.rs` 的 `mutate_manifest_allowing_version_binding_rewrites` + `validate_version_records_allow_binding_rewrites`。不变式:**不增不删**(候选与磁盘版本数量必须相等)、**不重排**(版本 ID 序列逐项相同)、**只有放行清单里的版本**允许 `resourceBindings` 不同、其余字段(`versionId / parentVersionId / projectRevision / createdReason / createdAt / editPrompt`)逐字段相等、**未放行版本整条相等**、放行集合取自**写入前** manifest、与删除放行集合**互斥**。既有 `validate_version_records_are_append_only` 与既有 `mutate_manifest_at_allowing_version_removals` 语义一行未改;唯一结构性改动是把写盘公共体抽成 `write_manifest_locked_with_version_guard`(校验器由调用方注入,`write_manifest_locked` 变成一行委托),装盘 / 回读 / 原子替换仍只有一份实现。
|
||||
- **命令**:`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`;出参 `{versionId, committedProjectRevision, replacement:{versionId, sourceResourceId, replacementResourceId, compatibility, warning}}`(**没有** `parentVersionId`,因为没有新版本)。这两个 DTO 是 Tauri 本地 DTO,**不进跨端契约**。
|
||||
- **写入语义**:持项目写锁(与删除 / 重命名 / 标签同一把)→ 锁外与锁内各复核一次 `projectId`,锁内复核 durable revision(CAS 失败报 `project-identity-conflict` / `project-revision-conflict` 且零写入)→ 走上面的绑定改写通道,放行集合固定为 `[sourceVersionId]` → 成功后推进一次项目 revision(改绑定属于 `versions` 变化,跨面快照门禁要求 revision 前进)→ 追加一条 `asset.version_binding.replace` 审计(复用既有 `append_agent_db_record`)。审计写失败会报错但不回滚,与 `asset.register` 同口径。
|
||||
- **绑定改写语义**(恒等绑定口径的硬约束):`resourceBindings` 是"该版本使用的素材集合"而不是槽位表,因此改写 = **源素材从集合里消失 + 保证替换素材在集合里**;替换素材是源版本创建之后才登记时按源素材原来的位置插回(顺序稳定),早已登记时只做摘除 —— 不能把源素材那条槽位改写成替换素材,那会撞「资源槽位重复」。
|
||||
- **准入与提示(后端权威,前端只呈现)**:**硬门禁**只有 `categoryEqual`(用 PRD §5.3 的**读时自愈**口径,Rust 侧 `game_creation_app_asset_category_with_read_time_healing` 与 `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` / `assetHints` / `renderAssetMedia` / `selectionNoun` / `errorMessage`,**默认值保持网页端美术画布行为逐字不变**。候选行渲染类型占位而不挂 `<img>`(AGC 的预览要经带 scope 的原生调度器拿 Blob URL,弹窗内没有同步 `src`)。
|
||||
- **成功后行为**:重读 manifest,**不切换版本**(没有新版本可切),**不自动重载 / 重启运行中的预览**(PRD §3.2 末条),不做运行时资源重映射。可见变化只有资源卡「当前使用」高亮移到替换素材、`@` 面板「当前版本素材」更新。
|
||||
- **已知代价(用户已确认接受)**:**替换历史不可回溯**——替换前身份只剩那条审计与 manifest 的 `.previous` 副本;需要"某版本历史上换过什么"时要另立切片(PRD §3.2 / §5.3 保留为未来合同正是为此)。
|
||||
- **验证**:绑定改写通道定向 7 条(`project/manifest/version_binding_rewrite_tests.rs`)+ 替换定向 8 条 + `shared-contracts` 20 条。**变异验证**:① 去掉「未放行版本整条相等」→ 两周转红;② 放行集合改成整个版本数组 → 「未放行版本」转红;③ 去掉长度检查 → 「不增不删」转红;④ 准入删掉 category → 硬门禁与候选两条转红;另有 ⑤ 改回"原地改既有版本但绕过放行口"→ 被「项目版本记录写入后不可修改、删除或重排」拦下。前端 13 条(模型 9 + 真链路 4)。AGC 全量 1231 passed / 4 skipped / 0 failed;共享美术画布组件 1385 passed;`npm run ai-game-creator-shell:typecheck`(含 check-config)、`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,19 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
|
||||
### 7.3 已知未做 / 已取消(不要报成缺陷)
|
||||
|
||||
1. **C6 替换关系与替换队列整条取消**(#309)。PRD L387 的 `ProjectVersionResourceReplacement` 兼容性三项属该取消范围。
|
||||
1. **C6 的候选素材 / 替换关系 / 替换队列 / Agent 审核 / 批量提交整条取消**(#309)。PRD §5.3 的 `ProjectVersionResourceReplacement` 与 §3.2 的「创建下一迭代版本」**当前不实现**(2026-09-11 用户 DDL 口径「替换这块先做成直接替换」),但**保留为未来合同**:今天的实现是「改该版本的绑定,不建新版本」,验收见 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>` 喂空串、不挂破图)。
|
||||
12. **尺寸规格差异只提示、不拒绝**:`sizeSpecEqual` 的完整判据今天不存在(manifest 无 `width / height / durationMs`,实际只等于"媒体格式相等"),所以同分类同类型的 `png ↔ webp` 替换是**允许**的,弹窗里只给「格式与源素材不同」的提示。
|
||||
13. **替换历史不可回溯**:直接替换不产生版本记录,替换前身份只剩一条 `asset.version_binding.replace` 审计与 manifest 的 `.previous` 副本。这是当前口径的已知代价,不要报成"缺少替换历史功能"的缺陷(版本级替换属未来合同)。
|
||||
|
||||
### 7.4 文档与代码的偏差(需明确按哪个判)
|
||||
|
||||
@@ -253,5 +257,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)。资源替换的后端矩阵(版本绑定改写放行的六条不变式与两组互斥、绑定改写两条路径、硬门禁与提示、CAS、四条拒绝路径、读时自愈口径、审计留痕)见 `apps/ai-game-creator-shell/src-tauri/src/project/manifest/version_binding_rewrite_tests.rs` 与 `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