修复无限画布恢复与幂等链路
Project CI / Repository checks (pull_request) Successful in 1m0s
Project CI / Frontend tests (pull_request) Successful in 2m57s
Project CI / Backend tests (pull_request) Successful in 3m48s
Project CI / Native shell tests (pull_request) Failing after 11m11s

补齐 refine 草稿跨重启唯一恢复与生命周期门禁

完善 Provider durable handoff、认证恢复和旧密钥服务确认

修复资源事务前向恢复与异步导入迟到写入

补充前后端回归测试并同步权威文档与项目记忆
This commit is contained in:
2026-08-12 10:21:18 +08:00
parent 912df59c2b
commit daaef0917f
13 changed files with 2115 additions and 230 deletions
@@ -978,8 +978,10 @@ confirmation-required
- 资源总览的“新增资源”保留为明确禁用态,入口处理函数也必须拒绝 create,不能只依赖按钮外观阻止进入空白画布。
- 资源聚焦态的所有现役资源均提供“编辑资源”,覆盖 manifest asset、已完成任务产物、已导入附件、Agent 文本回执和项目版本。后端必须按 manifest、任务完成态、上传登记或回执身份重新核验来源;没有唯一来源身份的本地媒体不得仅凭前端路径进入编辑。
- 静态 PNG / JPEG / WebP 继续使用 `AssetCanvasSurface + intent=refine`,自动加载唯一源图片,并把源资源身份作为图片编辑请求的必选引用。任务产物或附件中的静态图片必须先正规化为正式 manifest asset,再进入现有图片画布。
- refine 入口不能只在 WebView 进程内缓存 `draftId`。每次打开先在正式 draft sidecar 中按 `projectId + intent=refine + sourceAssetId + active status` 有界发现:唯一命中沿用原 `draftId` 并生成新 `sessionId`,零命中才创建,多命中进入 `reconciliation-required``committed/cancelled` 不属于 active 候选。
- 客户端确认编辑后固定调用 `POST /api/external/v1/editor/images/edits`;提示词、比例、尺寸、资源用途、泥点计费和原 operation 恢复继续复用现有生成合同,凭据只来自发布 AppData 私有 `editorApi.apiKey`
- SVG、UTF-8 文档、代码和 Agent 文本回执使用文本差异派生:把源内容当作不可信数据交给当前客户端 LLM,响应必须是完整、唯一的结构化内容 envelope;JSON、SVG 等可校验格式必须在落盘前重新校验。结果写入新的本地路径和 manifest asset,不能直接写回源文件。Agent 回执原记录不转写、不删除,新 asset 以回执资源身份登记血缘。
- 文本、SVG 与 Agent 回执在 Provider 调用前必须先持久化 request-issued;成功响应必须先原子安装到与原 operation、请求指纹和内容摘要绑定的私有 durable handoff,再做 envelope 解析、格式校验和 staging。issued 后缺少可信 handoff 只能对账;handoff 已存在且校验通过时恢复只消费该正文,两种情况都禁止再次调用 Provider。
- 视频使用 `POST /api/external/v1/editor/videos/generations`。有稳定远端引用时直接作为 `referenceVideoSrcs`,只有本地文件时先走 External direct-upload ticket、OSS 表单上传和 confirm,再提交同一逻辑生成;结果必须下载到新的本地文件并登记远端稳定身份。
- 音效和背景音乐分别使用 `POST /api/external/v1/editor/audios/sound-effects/generations``POST /api/external/v1/editor/audios/background-music/generations`。当前接口没有源音频引用字段,因此产品语义固定为“基于原资源语义的派生重制”,界面不得描述为对源波形的裁剪、变声或局部修改;结果仍必须引用源资源身份并保留原音频。
- 项目版本编辑固定追加 `parentVersionId` 指向源版本的子版本,继承源版本资源绑定并记录本轮编辑提示;已有版本数组元素不可修改、删除或重排。
@@ -987,13 +989,15 @@ confirmation-required
- 各类型统一使用稳定 `operationId / Idempotency-Key`。远端 `202` 只表示受理,必须轮询原 operation;提交结果未知或登录失效时保留原身份供恢复,不能换键重提。签名 URL 不得写入 manifestmanifest 只保存稳定 objectKey 对应的资源 / 资产身份。
- External 图片 refine、视频、音效和背景音乐请求统一在 `generationInputs.source` 写入专用消费身份 `game-creator-resource-editor`;所有 External 路由必须实际读取并传递同一稳定 `Idempotency-Key`。队列完成态只向该消费身份返回经过裁剪的稳定 `objectKey / resource / asset` 引用和必要媒体元数据,不暴露 provider、worker、队列内部字段或临时签名 URL。
- 生成公开状态每次写入草稿都必须推进草稿 revision,并把最新 revision 同步到私有生成账本、进度事件、staging 与正式 commit;旧 UI 快照不得覆盖 accepted/running/reconciliation 状态。
- operation 已受理或首次提交结果未知后遇到 401/403,不得写成 terminal failed。私有账本保留原 operation、原请求字节和原幂等身份,公开状态进入可恢复对账态;修正 External Editor API Key 配置后只继续同一 operation
- operation 已受理或首次提交结果未知后遇到 401/403,不得写成 terminal failed 或不可逆 `reconciliation-required`。私有账本保留原 remote phase、operation、原请求字节和原幂等身份;修正 External Editor API Key 配置后,已有 `operationId` 只继续 GET 同一 operation,未受理请求才允许用冻结正文和原幂等键重试 POST
- 全类型资源编辑私有账本必须冻结 `sourceAssetId / sourceMediaType / sourceAssetKind / producerTaskId / sourceVersionId` 源快照;新账本不得依赖 UI 仍停留在原资源,旧账本则只允许从当前 manifest、已完成任务产物、版本数组和受控扩展名兼容恢复。恢复结果仍要重新校验唯一来源、项目归属与媒体类型,不能把账本快照当成越权凭据。
- 私有账本的 External 配置身份只绑定规范服务地址,不绑定可轮换的 Developer API Key。升级前仍使用“服务地址 + Key”指纹的 Agent 生成账本,在原 Key 尚可用时首次恢复必须原子迁移为仅服务地址指纹;迁移后 Key 轮换不影响原 operation,无法证明服务地址未漂移的旧账本仍失败关闭。Key 轮换后,401/403/404、轮询超时或网关暂时失败都必须保留原 endpoint、请求字节、幂等键和 operation恢复命令仅凭持久 `operationId` 查找账本,不允许前端重新拼请求或携带 Key。
- 私有账本的 External 配置身份只绑定规范服务地址,不绑定可轮换的 Developer API Key。升级前仍使用“服务地址 + Key”指纹的 Agent 生成账本,在原 Key 尚可用时首次恢复必须原子迁移为仅服务地址指纹;无法用当前 Key 验证时先返回与账本快照绑定的显式确认挑战,确认前零网络动作,确认时复核 operation、挑战值与当前规范服务地址,成功后原子迁移。迁移后 Key 轮换不影响原 operation401/403/404、轮询超时或网关暂时失败都必须保留原 endpoint、请求字节、幂等键和 operation恢复命令不允许前端重新拼请求或携带 Key。
- pending 恢复扫描的目录上限统计所有实际目录条目,包括未知文件、非 pending 账本和损坏条目;达到上限后停止扫描并返回安全错误,不能通过堆放无关文件绕过边界。已 committed 的账本不得出现在 pending 列表中。
- 派生媒体提交必须以账本冻结的源摘要再次校验源文件;摘要、媒体身份或版本父子关系变化时失败关闭。远端派生视频二次编辑必须复用已提交账本中的稳定 `objectKey`,不能把仅用于本地登记的 `assetObjectId` 当成 generation reference。版本 journal 重放只能返回既有子版本,不得重复追加。
- 返回资源总览时,clean 草稿直接保留并退出;dirty 草稿必须使用独立确认面板提供“保留草稿并退出”和“放弃草稿”,默认保留。保留前必须等待当前保存或主动 flush,放弃才允许调用 discard。
- 图层选择与图层缩放必须同时提供指针和键盘路径;不得嵌套 button/role=button。Enter/Space 可选择图层,缩放手柄使用原生 button 并提供方向键离散缩放。
- Tauri 画布普通滚轮按二维 delta 平移,Shift 加纵向滚轮作为横向平移回退,Ctrl/Command 加滚轮才围绕指针缩放;一次 Shift 指针事件序列只能切换一次选择。生成中必须把焦点移到“停止等待”,并阻止 wheel、指针拖拽、缩放快捷键和方向键继续改变画布,避免生成恢复期间写入新的 UI 快照。
- `editing` 是唯一允许发起新生成的生命周期。`generating/committing/recovering/failed` 及独立 modal 打开时,画布与顶部工具栏必须同时 disabled/inert;生成事件 handler 还要读取当前权威 lifecycle 再次失败关闭,不能依赖遮罩层或渲染时闭包。
- asset transaction journal 已精确证明目标 asset、正式媒体和 target revision 写入时,恢复应允许当前 manifest 包含该目标的同时已有后续合法追加,且只要求 `currentRevision >= targetRevision`;补齐同一 ledger/draft 后仍复用原 commit/event 身份,不能因为整个 manifest 不再等于历史 after 快照而永久对账。
当前阶段不主动扩展 create 专属空画布导入、生成或保存体验;图片继续复用现有画布,其他类型使用同一资源编辑壳按能力分流,不能把视频、音频或版本强塞进图片图层模型。共享持久化、安全、幂等和可访问性缺陷仍必须修复,因为它们直接影响编辑链路。