{ "generatedAt": "2026-09-06T10:36:39.541Z", "overall": "PASS", "finalRun": true, "finalArtifactsVerified": true, "featureCount": { "total": 119, "pass": 119, "fail": 0, "pending": 0 }, "featureKinds": { "browser": 112, "focused": 7 }, "e2eCount": { "total": 33, "pass": 33, "fail": 0, "pending": 0 }, "validationTotals": [ { "kind": "构建与类型检查", "checks": 1, "passedChecks": 1, "tests": 0, "passedTests": 0 }, { "kind": "后端构建与测试", "checks": 1, "passedChecks": 1, "tests": 373, "passedTests": 373 }, { "kind": "Node 合同与单元", "checks": 2, "passedChecks": 2, "tests": 138, "passedTests": 138 } ], "cleanupCount": { "projects": 167, "projectsRemoved": 167, "originals": 4, "originalsUnchanged": 4, "users": 28, "usersRemoved": 28, "roles": 34, "rolesRemoved": 34 }, "unknownIds": [], "missing": [], "malformed": [], "features": [ { "id": "project.load", "group": "工程入口与顶部命令", "name": "按真实项目 ID 打开已存场景", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "async function loadProject", "line": 547 }, "implementation": "本轮修复已完成验证", "boundary": "服务端 content 与版本是真值;不得以模板种子覆盖有效旧工程。", "acceptance": "独立新会话打开,真实模型可见、固定版本及对象字段一致。", "fix": "新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。", "fixIds": [ "SCENE-F18" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "successful parallel restores retain document order after delayed GLBs and split-object batches finish out of order", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "project.load", "selection.tree" ], "file": "validation.json", "title": "successful parallel restores retain document order after delayed GLBs and split-object batches finish out of order", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "project.retry", "group": "工程入口与顶部命令", "name": "项目或挂载加载失败后重新加载", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "mountError", "line": 76 }, "implementation": "本轮修复已完成验证", "boundary": "错误详情自动消退,保留中性失败状态与重试按钮。", "acceptance": "注入加载失败;无可写半场景;恢复网络后按钮重试成功。", "fix": "前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。", "fixIds": [ "SCENE-F12" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载", "recordedAt": "2026-09-06T10:24:14.518Z", "kind": "browser", "ids": [ "project.retry", "glb.pending", "leave.busy" ], "file": "evidence/loading-retry.json", "title": "加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载", "details": "GET503只留中性失败和重试;延迟受控下载时禁用写入/导出/预览并阻止关闭,释放后真实狐狸加载。", "screenshots": [] }, { "status": "PASS", "testTitle": "a failed fixed-version lookup is evicted and the same reference can be retried", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "project.retry" ], "file": "validation.json", "title": "a failed fixed-version lookup is evicted and the same reference can be retried", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "project.title", "group": "工程入口与顶部命令", "name": "当前场景名称与项目说明按钮", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "project: () => this.toast", "line": 599 }, "implementation": "已有实现已完成验证", "boundary": "现有标题显示服务端名称;说明按钮不是重命名输入框。", "acceptance": "长名称完整 title 正确;点击说明反映内容 API 管理。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] } ] }, { "id": "project.new", "group": "工程入口与顶部命令", "name": "顶部新建独立工程", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "new: () =>", "line": 575 }, "implementation": "本轮修复已完成验证", "boundary": "基线代码重置当前模板;本轮由宿主接真实创建 API,必须保留原项目。", "acceptance": "取消/失败不清原工程;成功新 ID;保存重开与列表对应。", "fix": "顶部新建委托宿主命名与创建 API,先确认未保存内容。选择放弃也要等创建及导航成功后才清除原 dirty/恢复稿;创建503仍保留原稿。成功后刷新原缓存页,避免再次打开时将已放弃修改冒称已保存。取消、失败不清原场景,成功导航到新项目 ID,并有创建并发守卫。", "fixIds": [ "SCENE-F02" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "新建独立场景:取消、失败、保护原工程以及成功新 ID", "recordedAt": "2026-09-06T10:30:58.245Z", "kind": "browser", "ids": [ "project.new" ], "file": "evidence/new-project.json", "title": "新建独立场景:取消、失败、保护原工程以及成功新 ID", "details": "取消命名、创建503保留原工程、创建成功新ID、保存刷新与原工程内容核对。", "screenshots": [] } ] }, { "id": "project.back", "group": "工程入口与顶部命令", "name": "返回场景制作列表并保留场景标签", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "back: () =>", "line": 598 }, "implementation": "已有实现已完成验证", "boundary": "平台约定返回列表等同切换标签,保留场景缓存和未存稿,不弹关闭确认;真正关闭标签才询问。", "acceptance": "未存状态返回列表不写服务器、不丢场景;重新点原标签恢复输入和选择。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "recordedAt": "2026-09-06T10:27:04.782Z", "kind": "browser", "ids": [ "validation.complete", "validation.bindings", "panel.validation", "publish.saved", "publish.failure", "publish.fixed-model", "cover.atomic", "project.back" ], "file": "evidence/publish-fixed-version.json", "title": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "details": "实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。", "screenshots": [] } ] }, { "id": "project.save", "group": "工程入口与顶部命令", "name": "保存草稿按钮", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async saveProject()", "line": 1874 }, "implementation": "本轮修复已完成验证", "boundary": "只有服务器保存完成才显示成功;本地恢复缓冲不是自动入库。", "acceptance": "编辑后真实 PUT,刷新值一致,version 前进一次。", "fix": "完整 PUT 回写原有相同 SCENE_MODEL 三元组时沿用场景授权边界;新增或改版依赖仍要求来源模型可见及范围权限,不扩大模型页面访问权。", "fixIds": [ "SCENE-F14" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "recordedAt": "2026-09-06T10:24:27.147Z", "kind": "browser", "ids": [ "project.concurrent-save", "project.save", "project.save-key", "recovery.autobuffer", "recovery.same-version" ], "file": "evidence/save-concurrency.json", "title": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "details": "延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。", "screenshots": [] }, { "status": "PASS", "testTitle": "双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿", "recordedAt": "2026-09-06T10:25:56.822Z", "kind": "browser", "ids": [ "recovery.multi-session", "recovery.autobuffer", "recovery.discard", "project.save" ], "file": "evidence/scene-multi-session.json", "title": "双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿", "details": "", "screenshots": [] } ] }, { "id": "project.save-key", "group": "工程入口与顶部命令", "name": "Ctrl/Meta+S 保存快捷键", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleKeyboard(event)", "line": 469 }, "implementation": "已有实现已完成验证", "boundary": "键盘与按钮走相同保存/权限/忙碌守卫。", "acceptance": "实际按键触发一次保存;表单内与非表单焦点分别核查。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "recordedAt": "2026-09-06T10:24:27.147Z", "kind": "browser", "ids": [ "project.concurrent-save", "project.save", "project.save-key", "recovery.autobuffer", "recovery.same-version" ], "file": "evidence/save-concurrency.json", "title": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "details": "延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。", "screenshots": [] }, { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] } ] }, { "id": "project.save-failure", "group": "工程入口与顶部命令", "name": "保存失败保留修改和恢复稿", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async saveProject()", "line": 1874 }, "implementation": "已有实现已完成验证", "boundary": "网络失败或业务拒绝不能清 dirty;不弹虚假保存成功。", "acceptance": "定向 503 后对象/字段保留,恢复后重试与刷新成功。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "保存失败与封面降级:503 保留草稿,封面上传失败保留旧图,恢复后可重试", "recordedAt": "2026-09-06T10:26:33.388Z", "kind": "browser", "ids": [ "project.save-failure", "cover.failure" ], "file": "evidence/save-cover-failure.json", "title": "保存失败与封面降级:503 保留草稿,封面上传失败保留旧图,恢复后可重试", "details": "正文PUT503不清草稿;封面上传503保留旧图但正文可保存;恢复服务后保存刷新。截图异常和409另由聚焦合同验证。", "screenshots": [] } ] }, { "id": "project.conflict", "group": "工程入口与顶部命令", "name": "409 乐观锁冲突保护", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "function describeSaveError", "line": 432 }, "implementation": "已有实现已完成验证", "boundary": "服务器最新版本不覆盖;恢复副本可下载/隔离。", "acceptance": "独立副本制造版本冲突;验证失败提示、旧版本不覆盖与本地稿保留。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载", "recordedAt": "2026-09-06T10:26:49.585Z", "kind": "browser", "ids": [ "project.conflict", "recovery.autobuffer", "recovery.same-version", "recovery.stale-version" ], "file": "evidence/real-recovery-conflict.json", "title": "同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载", "details": "真实未存输入产生恢复稿,同版本恢复不自动入库;另一版本先保存后409拒绝覆盖,旧稿隔离下载。", "screenshots": [] }, { "status": "PASS", "testTitle": "upload conflict propagates without any content PUT", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "project.conflict", "cover.failure" ], "file": "validation.json", "title": "upload conflict propagates without any content PUT", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "project.concurrent-save", "group": "工程入口与顶部命令", "name": "重复保存与保存中继续编辑", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "const saveRevision = this.changeRevision", "line": 1887 }, "implementation": "本轮修复已完成验证", "boundary": "提交快照与后续编辑分开;后续修改不能随早先请求完成清掉。", "acceptance": "延迟 PUT、连点保存并继续改值;一次旧提交、新修改仍 dirty。", "fix": "开始保存时取消之前安排的恢复稿定时器,避免它覆盖宿主保存快照 nonce;保存中的后续编辑仍单独保留。", "fixIds": [ "SCENE-F08" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "recordedAt": "2026-09-06T10:24:27.147Z", "kind": "browser", "ids": [ "project.concurrent-save", "project.save", "project.save-key", "recovery.autobuffer", "recovery.same-version" ], "file": "evidence/save-concurrency.json", "title": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "details": "延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。", "screenshots": [] } ] }, { "id": "json.export", "group": "工程文件与资源导入", "name": "导出当前场景 JSON", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "exportProject()", "line": 578 }, "implementation": "已有实现已完成验证", "boundary": "导出 edit3dv4.scene、对象/部件覆盖、渲染和稳定资源引用;不冒称自包含 GLB 包。", "acceptance": "打开下载 JSON 核对内容、结构和无临时 blob 依赖。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] }, { "status": "PASS", "testTitle": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "recordedAt": "2026-09-06T10:31:05.221Z", "kind": "browser", "ids": [ "permission.readonly", "permission.update", "permission.publish", "preview.enter", "preview.exit", "preview.lock", "json.export" ], "file": "evidence/readonly.json", "title": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "details": "只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。", "screenshots": [] } ] }, { "id": "json.import", "group": "工程文件与资源导入", "name": "导入有效场景工程 JSON", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "importProject(event)", "line": 462 }, "implementation": "本轮修复已完成验证", "boundary": "替换当前编辑文档后仍需保存;不得写入另一个项目身份。", "acceptance": "导出后实际文件导入、保存、重开;对象与渲染参数往返一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] } ] }, { "id": "json.invalid", "group": "工程文件与资源导入", "name": "非法 JSON / 错误 schema 拒绝", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "importProject(event)", "line": 462 }, "implementation": "本轮修复已完成验证", "boundary": "解析或结构错误不得清空当前场景。", "acceptance": "非法 JSON 与非场景文档各一次,原工程和模型仍可编辑。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] } ] }, { "id": "json.missing-resource", "group": "工程文件与资源导入", "name": "JSON 缺失/错误受控引用拒绝与回滚", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async restoreImportedObject", "line": 2016 }, "implementation": "本轮修复已完成验证", "boundary": "错误资产编码不能回退为另一个模型;恢复失败不半替换。", "acceptance": "导入显式不存在编码/版本;原对象保留或明确阻断,零错误发布。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。;前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。", "fixIds": [ "SCENE-F03", "SCENE-F12" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] }, { "status": "PASS", "testTitle": "a failed replacement GLB keeps the previous live scene rather than clearing an unsaved draft", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "json.missing-resource" ], "file": "validation.json", "title": "a failed replacement GLB keeps the previous live scene rather than clearing an unsaved draft", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "an explicit missing code cannot fall back to the first READY asset", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "validation.references", "json.missing-resource" ], "file": "validation.json", "title": "an explicit missing code cannot fall back to the first READY asset", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "json.rendering", "group": "工程文件与资源导入", "name": "相机、灯光、雾和环境序列化往返", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "serializeRendering()", "line": 857 }, "implementation": "本轮修复已完成验证", "boundary": "当前默认 UI 不提供全部渲染编辑器;导入/保存仍须保留其文档参数。", "acceptance": "合法带渲染参数文档导入、实际画面比对、保存重开字段一致。", "fix": "透视按钮实际恢复先前观察位置;导入、加载和历史恢复存在已保存相机时不再被车辆自动聚焦覆盖。", "fixIds": [ "SCENE-F04" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] }, { "status": "PASS", "testTitle": "loading and history restoration preserve an authored camera rather than auto-focusing a vehicle", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "json.rendering", "history.undo" ], "file": "validation.json", "title": "loading and history restoration preserve an authored camera rather than auto-focusing a vehicle", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "glb.import", "group": "工程文件与资源导入", "name": "本地 GLB 上传并加入当前场景", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async importGLB(event)", "line": 2122 }, "implementation": "本轮修复已完成验证", "boundary": "上传到内容服务,消费票据后解析;对象加入不等于工程已保存。", "acceptance": "真实选择小 GLB,上传/登记/可见/保存重开均完成。", "fix": "恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。", "fixIds": [ "SCENE-F06" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] } ] }, { "id": "glb.invalid", "group": "工程文件与资源导入", "name": "非 GLB / 损坏 GLB / 上传失败处理", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async importGLB(event)", "line": 2122 }, "implementation": "已有实现已完成验证", "boundary": "上传或解析失败不插入占位对象;原场景应保留,提示可重试。", "acceptance": "错误格式及损坏文件、上传 503;验证对象数/旧资产/后续成功导入。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] } ] }, { "id": "glb.pending", "group": "工程文件与资源导入", "name": "资源加载/上传期间操作互斥", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "setModelLoading(loading", "line": 1411 }, "implementation": "已有实现已完成验证", "boundary": "加载中的模型不能被半保存、导出或发布;不能离开造成旧请求污染新项目。", "acceptance": "延迟下载/上传,操作禁用与待完成状态一致,完成后恢复。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载", "recordedAt": "2026-09-06T10:24:14.518Z", "kind": "browser", "ids": [ "project.retry", "glb.pending", "leave.busy" ], "file": "evidence/loading-retry.json", "title": "加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载", "details": "GET503只留中性失败和重试;延迟受控下载时禁用写入/导出/预览并阻止关闭,释放后真实狐狸加载。", "screenshots": [] }, { "status": "PASS", "testTitle": "replacement failure waits for other started loads before restoring the original scene", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.failure", "glb.pending" ], "file": "validation.json", "title": "replacement failure waits for other started loads before restoring the original scene", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "an upload finishing after a project switch cannot register into the new session", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "glb.pending", "lifecycle.destroy" ], "file": "validation.json", "title": "an upload finishing after a project switch cannot register into the new session", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "glb.references", "group": "工程文件与资源导入", "name": "本项目 GLB 稳定资产引用", "source": { "file": "../../../unreal_tran_web/src/features/scene-legacy/serverAssetStore.ts", "symbol": "async put(blob", "line": 113 }, "implementation": "本轮修复已完成验证", "boundary": "自导入使用 content://asset/scene/{projectId}/{assetCode} 和 READY 资产;blob 只供运行。", "acceptance": "重新会话受控下载成功,主对象/资产身份一致,无本地缓存依赖。", "fix": "前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。;新增 SceneResourceReferenceValidator 检查 objects 和递归 splitParts;自有/固定版本资源的所有显式引用必须指向同一 READY 模型文件,错误引用在发布/提交前阻断,保留受控的内置资源和旧引用兼容。", "fixIds": [ "SCENE-F12", "SCENE-F13" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] }, { "status": "PASS", "testTitle": "copied scene keeps its original pseudo URI but reads its own copied asset by stable code", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "glb.references" ], "file": "validation.json", "title": "copied scene keeps its original pseudo URI but reads its own copied asset by stable code", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "panel.left-tabs", "group": "左侧目录与资源放置", "name": "场景大纲 / 资源库 / 场景模板切换", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "const leftTab =", "line": 540 }, "implementation": "已有实现已完成验证", "boundary": "切面板不写业务文档;筛选状态行为需一致。", "acceptance": "三页签均可达,无 DOM 异常或资源目录中断。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "模板搜索与未保存切换确认保留和放弃", "recordedAt": "2026-09-06T10:28:42.077Z", "kind": "e2e", "ids": [ "panel.left-tabs", "search.templates", "template.switch-cancel" ], "file": "evidence/template-confirm.json", "title": "模板搜索与未保存切换确认保留和放弃", "details": "中文/无匹配/清空搜索;真实取消保留对象,真实确认放弃并切换空白模板后保存。", "screenshots": [ "29-template-confirm-empty.png" ] } ] }, { "id": "search.tree", "group": "左侧目录与资源放置", "name": "对象/部件名称搜索与清空", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "renderTree()", "line": 627 }, "implementation": "已有实现已完成验证", "boundary": "根名与部件名可命中;无匹配状态应清楚,非删除对象。", "acceptance": "根名/部件名/无结果/清空分别核对行和真实对象数。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "recordedAt": "2026-09-06T10:29:25.167Z", "kind": "e2e", "ids": [ "assets.catalog", "assets.model-double-click", "assets.model-drop", "search.tree", "part.source", "property.visible" ], "file": "evidence/published-model-assets.json", "title": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "details": "API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。", "screenshots": [ "33-fox-placed-visible.png", "34-truck-placed-part-restored.png" ] } ] }, { "id": "search.assets", "group": "左侧目录与资源放置", "name": "资源搜索与异步刷新保留筛选", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "applyLeftFilter()", "line": 742 }, "implementation": "已有实现已完成验证", "boundary": "只筛当前目录;异步重绘不复活不匹配卡片。", "acceptance": "命中、无结果、清空,目录重绘后过滤仍正确。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "search.templates", "group": "左侧目录与资源放置", "name": "模板搜索与清空", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "filterLeft(query)", "line": 2278 }, "implementation": "已有实现已完成验证", "boundary": "模板卡片与同一搜索框联动。", "acceptance": "中文模板关键字过滤、无结果和清空。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "模板搜索与未保存切换确认保留和放弃", "recordedAt": "2026-09-06T10:28:42.077Z", "kind": "e2e", "ids": [ "panel.left-tabs", "search.templates", "template.switch-cancel" ], "file": "evidence/template-confirm.json", "title": "模板搜索与未保存切换确认保留和放弃", "details": "中文/无匹配/清空搜索;真实取消保留对象,真实确认放弃并切换空白模板后保存。", "screenshots": [ "29-template-confirm-empty.png" ] } ] }, { "id": "assets.catalog", "group": "左侧目录与资源放置", "name": "真实已发布模型成果目录", "source": { "file": "../../../unreal_tran_web/src/features/scene-legacy/contentApiClient.ts", "symbol": "async listPublishedModels", "line": 783 }, "implementation": "本轮修复已完成验证", "boundary": "目录由 API 预加载,当前默认不以 localStorage 发布清单为真值。", "acceptance": "显示发布模型名称/版本,草稿不冒充已发布,空态可操作。", "fix": "移除 runtime 旧 content.update 门槛,独立计算新建、更新和发布;无模型页面权限时不加载新模型目录,但既有固定依赖仍可按场景授权读取。", "fixIds": [ "SCENE-F01" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "recordedAt": "2026-09-06T10:29:25.167Z", "kind": "e2e", "ids": [ "assets.catalog", "assets.model-double-click", "assets.model-drop", "search.tree", "part.source", "property.visible" ], "file": "evidence/published-model-assets.json", "title": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "details": "API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。", "screenshots": [ "33-fox-placed-visible.png", "34-truck-placed-part-restored.png" ] } ] }, { "id": "assets.model-double-click", "group": "左侧目录与资源放置", "name": "双击已发布模型放置", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async placeEditorAsset", "line": 1357 }, "implementation": "已有实现已完成验证", "boundary": "按固定 project/version/assetCode 导入当前实例,继承发布部件和材质。", "acceptance": "狐狸和卡车分别双击加入、真实可见、保存重开固定版本。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "recordedAt": "2026-09-06T10:29:25.167Z", "kind": "e2e", "ids": [ "assets.catalog", "assets.model-double-click", "assets.model-drop", "search.tree", "part.source", "property.visible" ], "file": "evidence/published-model-assets.json", "title": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "details": "API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。", "screenshots": [ "33-fox-placed-visible.png", "34-truck-placed-part-restored.png" ] }, { "status": "PASS", "testTitle": "fresh skinned placement reaches the requested size and floor after authored scale/pose replay", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "assets.model-double-click", "assets.model-drop" ], "file": "validation.json", "title": "fresh skinned placement reaches the requested size and floor after authored scale/pose replay", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "assets.model-drop", "group": "左侧目录与资源放置", "name": "拖入已发布模型放置", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleDrop(event)", "line": 461 }, "implementation": "已有实现已完成验证", "boundary": "拖放实际命中地平面;与双击共用受控资产链路。", "acceptance": "真实指针拖放到不同位置,坐标/实例数/固定引用准确。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "recordedAt": "2026-09-06T10:29:25.167Z", "kind": "e2e", "ids": [ "assets.catalog", "assets.model-double-click", "assets.model-drop", "search.tree", "part.source", "property.visible" ], "file": "evidence/published-model-assets.json", "title": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "details": "API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。", "screenshots": [ "33-fox-placed-visible.png", "34-truck-placed-part-restored.png" ] }, { "status": "PASS", "testTitle": "fresh skinned placement reaches the requested size and floor after authored scale/pose replay", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "assets.model-double-click", "assets.model-drop" ], "file": "validation.json", "title": "fresh skinned placement reaches the requested size and floor after authored scale/pose replay", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "assets.basic-double-click", "group": "左侧目录与资源放置", "name": "双击基础资源放置", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "onRootDblClick", "line": 429 }, "implementation": "本轮修复已完成验证", "boundary": "以当前轨道目标落地;每次生成独立对象身份。", "acceptance": "双击生成对象,变换、保存后仍可见。", "fix": "恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。", "fixIds": [ "SCENE-F06" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "assets.basic-drop", "group": "左侧目录与资源放置", "name": "基础资源拖入与拖放提示", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleDrop(event)", "line": 461 }, "implementation": "已有实现已完成验证", "boundary": "MIME 和 text/plain 回退协议一致;离开视口清提示。", "acceptance": "真实拖放命中位置与 0.5m 吸附、dragleave/drop 提示复位。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "assets.model-link", "group": "左侧目录与资源放置", "name": "打开/返回模型编辑器入口", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "open-model-editor", "line": 590 }, "implementation": "本轮修复已完成验证", "boundary": "当前基线只有跳模型列表,来源按钮不得冒称直达正确源项目。", "acceptance": "空资源库和选中模型来源两入口,跳转/未保存保护正确。", "fix": "模型来源按钮有来源权限时导航正确项目;无来源页面权限回到可访问列表,不将模型列表误称为来源工程编辑。", "fixIds": [ "SCENE-F11" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "model source navigation locates the published owner and falls back without source permission", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "assets.model-link" ], "file": "validation.json", "title": "model source navigation locates the published owner and falls back without source permission", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "asset.diagnostic", "group": "左侧目录与资源放置", "name": "基础资源:液压诊断工位", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'diagnostic'", "line": 123 }, "implementation": "已有实现已完成验证", "boundary": "现有程序化场景资源;名称不代表已连接物理设备或真实人员。", "acceptance": "实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "asset.equipment", "group": "左侧目录与资源放置", "name": "基础资源:维修装备主体", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'equipment'", "line": 124 }, "implementation": "已有实现已完成验证", "boundary": "现有程序化场景资源;名称不代表已连接物理设备或真实人员。", "acceptance": "实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "asset.workbench", "group": "左侧目录与资源放置", "name": "基础资源:检修工作台", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'workbench'", "line": 125 }, "implementation": "已有实现已完成验证", "boundary": "现有程序化场景资源;名称不代表已连接物理设备或真实人员。", "acceptance": "实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "asset.shelf", "group": "左侧目录与资源放置", "name": "基础资源:器材存放架", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'shelf'", "line": 126 }, "implementation": "已有实现已完成验证", "boundary": "现有程序化场景资源;名称不代表已连接物理设备或真实人员。", "acceptance": "实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "asset.safety-zone", "group": "左侧目录与资源放置", "name": "基础资源:安全作业区域", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'safety-zone'", "line": 127 }, "implementation": "已有实现已完成验证", "boundary": "现有程序化场景资源;名称不代表已连接物理设备或真实人员。", "acceptance": "实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "asset.camera", "group": "左侧目录与资源放置", "name": "基础资源:视觉采集设备", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'camera'", "line": 128 }, "implementation": "已有实现已完成验证", "boundary": "现有程序化场景资源;名称不代表已连接物理设备或真实人员。", "acceptance": "实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "asset.worker", "group": "左侧目录与资源放置", "name": "基础资源:训练人员占位", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'worker'", "line": 129 }, "implementation": "已有实现已完成验证", "boundary": "现有程序化场景资源;名称不代表已连接物理设备或真实人员。", "acceptance": "实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "asset.light", "group": "左侧目录与资源放置", "name": "基础资源:区域照明", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'light'", "line": 130 }, "implementation": "已有实现已完成验证", "boundary": "现有程序化场景资源;名称不代表已连接物理设备或真实人员。", "acceptance": "实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "asset.vehicle-depot", "group": "左侧目录与资源放置", "name": "场地资源:真实装备车库", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'vehicle-depot'", "line": 121 }, "implementation": "已有实现已完成验证", "boundary": "整体场地 GLB 与维修车间分开;重复放置按既有规则处理。", "acceptance": "资源拖放或双击加载、比例/落地正常、重复放置不产生错误重叠。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "两种整体场地加载、重复放置阻止与显式删除后替换", "recordedAt": "2026-09-06T10:29:06.315Z", "kind": "e2e", "ids": [ "asset.vehicle-depot", "asset.field-terrain", "object.delete" ], "file": "evidence/venue-assets.json", "title": "两种整体场地加载、重复放置阻止与显式删除后替换", "details": "车库和起伏地形真实加载并分别保存重开;已有整体场地时阻止叠加,显式删除旧场地后才能替换。", "screenshots": [ "31-venue-depot-reopened.png", "32-venue-terrain-reopened.png" ] } ] }, { "id": "asset.field-terrain", "group": "左侧目录与资源放置", "name": "场地资源:真实野外地形", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'field-terrain'", "line": 122 }, "implementation": "已有实现已完成验证", "boundary": "真实地形资源和指定落地基准;不能以程序地板代替。", "acceptance": "加载后真实起伏地形可见,保存重开资源、地面高度一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "两种整体场地加载、重复放置阻止与显式删除后替换", "recordedAt": "2026-09-06T10:29:06.315Z", "kind": "e2e", "ids": [ "asset.vehicle-depot", "asset.field-terrain", "object.delete" ], "file": "evidence/venue-assets.json", "title": "两种整体场地加载、重复放置阻止与显式删除后替换", "details": "车库和起伏地形真实加载并分别保存重开;已有整体场地时阻止叠加,显式删除旧场地后才能替换。", "screenshots": [ "31-venue-depot-reopened.png", "32-venue-terrain-reopened.png" ] } ] }, { "id": "template.depot-1", "group": "全部可达场景模板", "name": "真实装备车库", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'depot-1'", "line": 86 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:真实装备车库", "recordedAt": "2026-09-06T10:27:14.151Z", "kind": "e2e", "ids": [ "template.depot-1" ], "file": "evidence/template-depot-1.json", "title": "场景模板实际加载、保存重开:真实装备车库", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "20-template-depot-1.png" ] } ] }, { "id": "template.terrain-1", "group": "全部可达场景模板", "name": "真实野外训练场", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'terrain-1'", "line": 87 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:真实野外训练场", "recordedAt": "2026-09-06T10:27:24.419Z", "kind": "e2e", "ids": [ "template.terrain-1" ], "file": "evidence/template-terrain-1.json", "title": "场景模板实际加载、保存重开:真实野外训练场", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "21-template-terrain-1.png" ] } ] }, { "id": "template.workshop-1", "group": "全部可达场景模板", "name": "综合检验车间", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'workshop-1'", "line": 88 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:综合检验车间", "recordedAt": "2026-09-06T10:27:33.434Z", "kind": "e2e", "ids": [ "template.workshop-1" ], "file": "evidence/template-workshop-1.json", "title": "场景模板实际加载、保存重开:综合检验车间", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "22-template-workshop-1.png" ] } ] }, { "id": "template.workshop-real", "group": "全部可达场景模板", "name": "演示维修车间(可拆分)", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'workshop-real'", "line": 89 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:演示维修车间(可拆分)", "recordedAt": "2026-09-06T10:27:50.202Z", "kind": "e2e", "ids": [ "template.workshop-real" ], "file": "evidence/template-workshop-real.json", "title": "场景模板实际加载、保存重开:演示维修车间(可拆分)", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "23-template-workshop-real.png" ] } ] }, { "id": "template.workshop-2", "group": "全部可达场景模板", "name": "总成拆卸车间", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'workshop-2'", "line": 90 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:总成拆卸车间", "recordedAt": "2026-09-06T10:27:59.059Z", "kind": "e2e", "ids": [ "template.workshop-2" ], "file": "evidence/template-workshop-2.json", "title": "场景模板实际加载、保存重开:总成拆卸车间", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "24-template-workshop-2.png" ] } ] }, { "id": "template.workshop-3", "group": "全部可达场景模板", "name": "部件分解车间", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'workshop-3'", "line": 91 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:部件分解车间", "recordedAt": "2026-09-06T10:28:07.821Z", "kind": "e2e", "ids": [ "template.workshop-3" ], "file": "evidence/template-workshop-3.json", "title": "场景模板实际加载、保存重开:部件分解车间", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "25-template-workshop-3.png" ] } ] }, { "id": "template.workshop-4", "group": "全部可达场景模板", "name": "清洗检测车间", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'workshop-4'", "line": 92 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:清洗检测车间", "recordedAt": "2026-09-06T10:28:16.504Z", "kind": "e2e", "ids": [ "template.workshop-4" ], "file": "evidence/template-workshop-4.json", "title": "场景模板实际加载、保存重开:清洗检测车间", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "26-template-workshop-4.png" ] } ] }, { "id": "template.workshop-5", "group": "全部可达场景模板", "name": "复装调试车间", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'workshop-5'", "line": 93 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:复装调试车间", "recordedAt": "2026-09-06T10:28:25.537Z", "kind": "e2e", "ids": [ "template.workshop-5" ], "file": "evidence/template-workshop-5.json", "title": "场景模板实际加载、保存重开:复装调试车间", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "27-template-workshop-5.png" ] } ] }, { "id": "template.outdoor-1", "group": "全部可达场景模板", "name": "室外综合训练场", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "id: 'outdoor-1'", "line": 94 }, "implementation": "本轮修复已完成验证", "boundary": "模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。", "acceptance": "独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "场景模板实际加载、保存重开:室外综合训练场", "recordedAt": "2026-09-06T10:28:34.148Z", "kind": "e2e", "ids": [ "template.outdoor-1" ], "file": "evidence/template-outdoor-1.json", "title": "场景模板实际加载、保存重开:室外综合训练场", "details": "真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。", "screenshots": [ "28-template-outdoor-1.png" ] } ] }, { "id": "template.switch-cancel", "group": "全部可达场景模板", "name": "未保存模板切换取消", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async loadPreset", "line": 765 }, "implementation": "本轮修复已完成验证", "boundary": "取消保留原场景、选择与 dirty;请求中不能切换半场景。", "acceptance": "有未存修改时取消/确认分别验证,取消零对象和服务端变更。", "fix": "模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。", "fixIds": [ "SCENE-F03" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "模板搜索与未保存切换确认保留和放弃", "recordedAt": "2026-09-06T10:28:42.077Z", "kind": "e2e", "ids": [ "panel.left-tabs", "search.templates", "template.switch-cancel" ], "file": "evidence/template-confirm.json", "title": "模板搜索与未保存切换确认保留和放弃", "details": "中文/无匹配/清空搜索;真实取消保留对象,真实确认放弃并切换空白模板后保存。", "screenshots": [ "29-template-confirm-empty.png" ] } ] }, { "id": "view.orbit", "group": "三维视口、选择与变换", "name": "鼠标旋转相机", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "new OrbitControls", "line": 379 }, "implementation": "已有实现已完成验证", "boundary": "相机轨道操作不应误选、拖动物体。", "acceptance": "实际鼠标拖动,相机变化且对象姿态不变。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] }, { "id": "view.pan", "group": "三维视口、选择与变换", "name": "鼠标平移相机", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "new OrbitControls", "line": 379 }, "implementation": "已有实现已完成验证", "boundary": "轨道目标平移与物体位置分开。", "acceptance": "实际右键/约定鼠标平移,目标变化且对象数据不变。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] }, { "id": "view.zoom", "group": "三维视口、选择与变换", "name": "滚轮缩放相机", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "new OrbitControls", "line": 379 }, "implementation": "已有实现已完成验证", "boundary": "改变观察距离,不改对象 scale。", "acceptance": "滚轮前后模型可见且对象 scale 不变。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] }, { "id": "view.perspective", "group": "三维视口、选择与变换", "name": "透视按钮", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "data-camera=\"perspective\"", "line": 259 }, "implementation": "本轮修复已完成验证", "boundary": "基线按钮无处理分支;需明确恢复相机视角,不静默无效。", "acceptance": "顶视切回透视,实际相机位置与激活样式变化。", "fix": "透视按钮实际恢复先前观察位置;导入、加载和历史恢复存在已保存相机时不再被车辆自动聚焦覆盖。", "fixIds": [ "SCENE-F04" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] }, { "id": "view.top", "group": "三维视口、选择与变换", "name": "顶视图按钮", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "setTopView()", "line": 585 }, "implementation": "本轮修复已完成验证", "boundary": "仍为透视相机顶视位置,不冒称正交投影。", "acceptance": "实际点击后顶视方向与目标一致。", "fix": "透视按钮实际恢复先前观察位置;导入、加载和历史恢复存在已保存相机时不再被车辆自动聚焦覆盖。", "fixIds": [ "SCENE-F04" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] }, { "id": "view.focus", "group": "三维视口、选择与变换", "name": "聚焦选中对象", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "focusSelected()", "line": 586 }, "implementation": "已有实现已完成验证", "boundary": "按对象真实 bounds 调整相机;无选中应提示。", "acceptance": "狐狸/卡车/环境分别聚焦可见;无选中提示不异常。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] }, { "id": "view.grid", "group": "三维视口、选择与变换", "name": "网格显隐", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "toggleGrid(button)", "line": 1643 }, "implementation": "已有实现已完成验证", "boundary": "当前视图辅助选项;不默认要求跨重开持久化。", "acceptance": "真实网格出现/消失、状态同步、截图封面不含网格。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] }, { "status": "PASS", "testTitle": "copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "cover.capture", "selection.highlight", "view.grid" ], "file": "validation.json", "title": "copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "view.enclosure-full", "group": "三维视口、选择与变换", "name": "车库外壳完整显示", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/garage-enclosure.js", "symbol": "createGarageEnclosureControls", "line": 377 }, "implementation": "已有实现已完成验证", "boundary": "完整显示屋顶/墙体/玻璃;仅车库资源存在时出现控制。", "acceptance": "实际点击、对象材料和显示恢复,状态与画面一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] } ] }, { "id": "view.enclosure-xray", "group": "三维视口、选择与变换", "name": "车库外壳透明查看", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/garage-enclosure.js", "symbol": "createGarageEnclosureControls", "line": 377 }, "implementation": "已有实现已完成验证", "boundary": "车库多种围护材料联动;不等于普通相机透视。", "acceptance": "实际外壳透明且内部模型可辨,材质无串改。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] } ] }, { "id": "view.enclosure-hidden", "group": "三维视口、选择与变换", "name": "隐藏车库外壳", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/garage-enclosure.js", "symbol": "createGarageEnclosureControls", "line": 377 }, "implementation": "已有实现已完成验证", "boundary": "隐藏围护后保留内部及地面;序列化 enclosureMode 与 dirty 契约须核对。", "acceptance": "隐藏/还原、保存重开和只读浏览行为准确。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] } ] }, { "id": "selection.viewport", "group": "三维视口、选择与变换", "name": "点击视口选中根对象/部件", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "selectFromViewport(event)", "line": 456 }, "implementation": "本轮修复已完成验证", "boundary": "只命中真实可选择可见对象;拖相机超过阈值不误选。", "acceptance": "真实像素点击目标、树/属性/高亮一致,点击空白清选择。", "fix": "新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。", "fixIds": [ "SCENE-F18" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] }, { "id": "selection.tree", "group": "三维视口、选择与变换", "name": "大纲根对象与部件选中", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "const treeItem =", "line": 546 }, "implementation": "本轮修复已完成验证", "boundary": "大纲显示所有可编辑实例和部件;环境锁定提示不是折叠控件。", "acceptance": "树选中与视口高亮/属性联动、筛选后仍可选。", "fix": "新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。", "fixIds": [ "SCENE-F18" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "two published instances have unique tree part IDs and editing the second persists independently after reopen", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "part.transform", "selection.tree" ], "file": "validation.json", "title": "two published instances have unique tree part IDs and editing the second persists independently after reopen", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "successful parallel restores retain document order after delayed GLBs and split-object batches finish out of order", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "project.load", "selection.tree" ], "file": "validation.json", "title": "successful parallel restores retain document order after delayed GLBs and split-object batches finish out of order", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "selection.hidden", "group": "三维视口、选择与变换", "name": "隐藏祖先与软删除部件不可误拾取", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-selection.js", "symbol": "chooseSceneSelection", "line": 41 }, "implementation": "本轮修复已完成验证", "boundary": "父隐藏/子隐藏/软删除状态不能被射线重新命中。", "acceptance": "用重叠模型/部件实测,命中应落可见有效对象。", "fix": "射线候选逐级检查父级可见性和软删除;删除根对象先恢复高亮材质再释放几何/材质,避免错误拾取及 GPU 资源残留。", "fixIds": [ "SCENE-F09" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "real Three ray hits on hidden descendants are excluded before selecting a root or part", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "selection.hidden" ], "file": "validation.json", "title": "real Three ray hits on hidden descendants are excluded before selecting a root or part", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "selection.highlight", "group": "三维视口、选择与变换", "name": "选中高亮与恢复原材质", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "updateSelectionHighlight()", "line": 627 }, "implementation": "本轮修复已完成验证", "boundary": "高亮是暂态,不写入模型/场景材质,不进入封面。", "acceptance": "切换对象/清选择/预览/保存截图后材质原值恢复。", "fix": "射线候选逐级检查父级可见性和软删除;删除根对象先恢复高亮材质再释放几何/材质,避免错误拾取及 GPU 资源残留。", "fixIds": [ "SCENE-F09" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] }, { "status": "PASS", "testTitle": "selection highlight cleanup restores source materials and releases its own GPU helper", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "selection.highlight", "lifecycle.destroy" ], "file": "validation.json", "title": "selection highlight cleanup restores source materials and releases its own GPU helper", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "cover.capture", "selection.highlight", "view.grid" ], "file": "validation.json", "title": "copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "transform.translate", "group": "三维视口、选择与变换", "name": "移动工具及真实 XYZ 手柄", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "setTransform(mode)", "line": 1627 }, "implementation": "已有实现已完成验证", "boundary": "对象实例位置;部件按发布能力限制。", "acceptance": "真实拖动平移手柄,属性联动、撤销重做、保存重开一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实变换手柄移动旋转缩放、吸附与保存重开", "recordedAt": "2026-09-06T10:29:55.869Z", "kind": "真实UI手柄+只读几何投影定位", "ids": [ "transform.translate", "transform.rotate", "transform.scale", "transform.keys", "transform.snap" ], "file": "evidence/viewport-transform.json", "title": "真实变换手柄移动旋转缩放、吸附与保存重开", "details": "", "screenshots": [ "43-real-transform-gizmo" ] } ] }, { "id": "transform.rotate", "group": "三维视口、选择与变换", "name": "旋转工具及真实旋转手柄", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "setTransform(mode)", "line": 1627 }, "implementation": "已有实现已完成验证", "boundary": "UI 度数与保存弧度转换;不能仅用脚本改姿态代替拖动。", "acceptance": "实际旋转手柄改变姿态并重开一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实变换手柄移动旋转缩放、吸附与保存重开", "recordedAt": "2026-09-06T10:29:55.869Z", "kind": "真实UI手柄+只读几何投影定位", "ids": [ "transform.translate", "transform.rotate", "transform.scale", "transform.keys", "transform.snap" ], "file": "evidence/viewport-transform.json", "title": "真实变换手柄移动旋转缩放、吸附与保存重开", "details": "", "screenshots": [ "43-real-transform-gizmo" ] } ] }, { "id": "transform.scale", "group": "三维视口、选择与变换", "name": "缩放工具及真实缩放手柄", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "setTransform(mode)", "line": 1627 }, "implementation": "本轮修复已完成验证", "boundary": "归一化模型已有 scale 必须保留,负/零/小值合同明确。", "acceptance": "真实缩放与属性数值一致;狐狸不突然放成超大或消失。", "fix": "实际手柄跨越枢轴时按既有严格正尺度合同限制,避免产生负或零尺度导致模型翻转或消失;未新增镜像建模功能。", "fixIds": [ "SCENE-F17" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实变换手柄移动旋转缩放、吸附与保存重开", "recordedAt": "2026-09-06T10:29:55.869Z", "kind": "真实UI手柄+只读几何投影定位", "ids": [ "transform.translate", "transform.rotate", "transform.scale", "transform.keys", "transform.snap" ], "file": "evidence/viewport-transform.json", "title": "真实变换手柄移动旋转缩放、吸附与保存重开", "details": "", "screenshots": [ "43-real-transform-gizmo" ] }, { "status": "PASS", "testTitle": "gizmo scaling across its pivot follows the same strictly positive scale contract as inspector edits", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "transform.scale", "property.scale" ], "file": "validation.json", "title": "gizmo scaling across its pivot follows the same strictly positive scale contract as inspector edits", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "transform.keys", "group": "三维视口、选择与变换", "name": "W/E/R 快捷键", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleKeyboard(event)", "line": 469 }, "implementation": "本轮修复已完成验证", "boundary": "键盘焦点在表单时不切工具,失活缓存页不能抢键。", "acceptance": "视口按键切工具;名称输入包含 w/e/r 正常;只读不改。", "fix": "预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。", "fixIds": [ "SCENE-F05" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实变换手柄移动旋转缩放、吸附与保存重开", "recordedAt": "2026-09-06T10:29:55.869Z", "kind": "真实UI手柄+只读几何投影定位", "ids": [ "transform.translate", "transform.rotate", "transform.scale", "transform.keys", "transform.snap" ], "file": "evidence/viewport-transform.json", "title": "真实变换手柄移动旋转缩放、吸附与保存重开", "details": "", "screenshots": [ "43-real-transform-gizmo" ] }, { "status": "PASS", "testTitle": "typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.undo", "history.redo", "transform.keys", "lifecycle.cache" ], "file": "validation.json", "title": "typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "transform.snap", "group": "三维视口、选择与变换", "name": "吸附 0.5m 与旋转 15° 开关", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "toggleSnap()", "line": 581 }, "implementation": "已有实现已完成验证", "boundary": "开关同时控制拖放/变换吸附;当前视图工具参数与业务值区分。", "acceptance": "开/关实际拖动位置、旋转角度,按钮/页脚状态一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实变换手柄移动旋转缩放、吸附与保存重开", "recordedAt": "2026-09-06T10:29:55.869Z", "kind": "真实UI手柄+只读几何投影定位", "ids": [ "transform.translate", "transform.rotate", "transform.scale", "transform.keys", "transform.snap" ], "file": "evidence/viewport-transform.json", "title": "真实变换手柄移动旋转缩放、吸附与保存重开", "details": "", "screenshots": [ "43-real-transform-gizmo" ] } ] }, { "id": "property.name", "group": "对象属性、部件与底部面板", "name": "显示名称", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleInput(event)", "line": 437 }, "implementation": "本轮修复已完成验证", "boundary": "对象/部件 displayName 与项目名称分开。", "acceptance": "修改名称,树/对象条/属性同步,保存重开保留。", "fix": "恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。", "fixIds": [ "SCENE-F06" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] } ] }, { "id": "property.visible", "group": "对象属性、部件与底部面板", "name": "在场景中可见", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleChange(event)", "line": 438 }, "implementation": "已有实现已完成验证", "boundary": "实例显隐可保存;受保护蒙皮部件不得独立隐藏。", "acceptance": "开/关可见性、恢复、保存重开,视口与树状态准确。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "recordedAt": "2026-09-06T10:29:25.167Z", "kind": "e2e", "ids": [ "assets.catalog", "assets.model-double-click", "assets.model-drop", "search.tree", "part.source", "property.visible" ], "file": "evidence/published-model-assets.json", "title": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "details": "API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。", "screenshots": [ "33-fox-placed-visible.png", "34-truck-placed-part-restored.png" ] } ] }, { "id": "property.operable", "group": "对象属性、部件与底部面板", "name": "可作为实训目标", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleChange(event)", "line": 438 }, "implementation": "已有实现已完成验证", "boundary": "operable/capabilities 进入场景定义;不能扩大平台操作权限。", "acceptance": "开/关并保存重开,发布数据的目标能力一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] } ] }, { "id": "property.position", "group": "对象属性、部件与底部面板", "name": "位置 X/Y/Z 数值", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleInput(event)", "line": 437 }, "implementation": "已有实现已完成验证", "boundary": "三个输入回写局部 position;不使用纯展示假字段。", "acceptance": "三个轴独立输入与手柄联动、失焦/history/重开准确。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] } ] }, { "id": "property.rotation", "group": "对象属性、部件与底部面板", "name": "旋转 X/Y/Z 数值", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleInput(event)", "line": 437 }, "implementation": "已有实现已完成验证", "boundary": "UI 为角度,文档为弧度。", "acceptance": "非整角/负角输入、实际姿态与 JSON/重开一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] } ] }, { "id": "property.scale", "group": "对象属性、部件与底部面板", "name": "缩放 X/Y/Z 数值及有效值", "source": { "file": "../../../unreal_tran_web/src/features/scene-legacy/modelPlacement.ts", "symbol": "parseSceneScale", "line": 36 }, "implementation": "本轮修复已完成验证", "boundary": "支持小数归一化 scale,避免小值格式化成零;非法值不得破坏模型。", "acceptance": "小正数、负数/零/清空按合同处理,三轴保存往返正常。", "fix": "实际手柄跨越枢轴时按既有严格正尺度合同限制,避免产生负或零尺度导致模型翻转或消失;未新增镜像建模功能。", "fixIds": [ "SCENE-F17" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "gizmo scaling across its pivot follows the same strictly positive scale contract as inspector edits", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "transform.scale", "property.scale" ], "file": "validation.json", "title": "gizmo scaling across its pivot follows the same strictly positive scale contract as inspector edits", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "scale input accepts real GLTF normalization factors below 0.05", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "property.scale" ], "file": "validation.json", "title": "scale input accepts real GLTF normalization factors below 0.05", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "property.device-id", "group": "对象属性、部件与底部面板", "name": "设备编号", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleInput(event)", "line": 437 }, "implementation": "本轮修复已完成验证", "boundary": "根对象绑定字符串;重复/空值由预检与后端核查。", "acceptance": "修改后绑定表同步、保存重开,重复值能被阻断。", "fix": "恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。", "fixIds": [ "SCENE-F06" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] } ] }, { "id": "property.model-id", "group": "对象属性、部件与底部面板", "name": "物模型编号", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleInput(event)", "line": 437 }, "implementation": "本轮修复已完成验证", "boundary": "与受控 assetCode、来源项目 ID 分开。", "acceptance": "修改后只改业务编号,源 GLB 仍加载正确。", "fix": "恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。", "fixIds": [ "SCENE-F06" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] } ] }, { "id": "property.semantic", "group": "对象属性、部件与底部面板", "name": "语义类型六个选项", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleChange(event)", "line": 438 }, "implementation": "已有实现已完成验证", "boundary": "实训装备/工位设施/安全区域/采集设备/训练人员/环境设施。", "acceptance": "逐项可选且保存重开保留;绑定表显示实际类型。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] } ] }, { "id": "part.source", "group": "对象属性、部件与底部面板", "name": "模型来源与部件权限说明", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "renderInspector()", "line": 1520 }, "implementation": "本轮修复已完成验证", "boundary": "固定模型版本/源节点;蒙皮仅查看高亮,非骨骼独立变换。", "acceptance": "狐狸蒙皮与卡车刚性部件说明/禁用态准确。", "fix": "模型来源按钮有来源权限时导航正确项目;无来源页面权限回到可访问列表,不将模型列表误称为来源工程编辑。", "fixIds": [ "SCENE-F11" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "recordedAt": "2026-09-06T10:29:25.167Z", "kind": "e2e", "ids": [ "assets.catalog", "assets.model-double-click", "assets.model-drop", "search.tree", "part.source", "property.visible" ], "file": "evidence/published-model-assets.json", "title": "模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开", "details": "API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。", "screenshots": [ "33-fox-placed-visible.png", "34-truck-placed-part-restored.png" ] } ] }, { "id": "part.transform", "group": "对象属性、部件与底部面板", "name": "刚性部件独立变换并保存覆盖", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "captureModelPartOverrides(root)", "line": 1123 }, "implementation": "本轮修复已完成验证", "boundary": "partOverrides 与稳定 interactionId;多个实例互不串改。", "acceptance": "两卡车实例改单部件,保存重开另一实例不受影响。", "fix": "新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。", "fixIds": [ "SCENE-F18" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] }, { "status": "PASS", "testTitle": "two published instances have unique tree part IDs and editing the second persists independently after reopen", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "part.transform", "selection.tree" ], "file": "validation.json", "title": "two published instances have unique tree part IDs and editing the second persists independently after reopen", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "legacy duplicate instance IDs migrate only later owners in document order despite reversed GLB completion", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "part.transform", "publish.fixed-model" ], "file": "validation.json", "title": "legacy duplicate instance IDs migrate only later owners in document order despite reversed GLB completion", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "part.soft-delete", "group": "对象属性、部件与底部面板", "name": "部件软删除与恢复", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "deleteSelected()", "line": 588 }, "implementation": "本轮修复已完成验证", "boundary": "被允许的部件 sceneDeleted/visible 持久化;可撤销恢复。", "acceptance": "实际删除、撤销、保存重开均一致;蒙皮不允许破坏骨架。", "fix": "新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。", "fixIds": [ "SCENE-F18" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] } ] }, { "id": "object.delete", "group": "对象属性、部件与底部面板", "name": "删除根对象按钮 / Delete", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "deleteSelected()", "line": 588 }, "implementation": "本轮修复已完成验证", "boundary": "移除当前场景实例,不删除源模型项目/固定发布版本。", "acceptance": "按钮和键盘各验证;源项目不变,撤销和重开正确。", "fix": "射线候选逐级检查父级可见性和软删除;删除根对象先恢复高亮材质再释放几何/材质,避免错误拾取及 GPU 资源残留。", "fixIds": [ "SCENE-F09" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "两种整体场地加载、重复放置阻止与显式删除后替换", "recordedAt": "2026-09-06T10:29:06.315Z", "kind": "e2e", "ids": [ "asset.vehicle-depot", "asset.field-terrain", "object.delete" ], "file": "evidence/venue-assets.json", "title": "两种整体场地加载、重复放置阻止与显式删除后替换", "details": "车库和起伏地形真实加载并分别保存重开;已有整体场地时阻止叠加,显式删除旧场地后才能替换。", "screenshots": [ "31-venue-depot-reopened.png", "32-venue-terrain-reopened.png" ] }, { "status": "PASS", "testTitle": "deleting a root disposes its geometry and materials after clearing selection", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "object.delete", "lifecycle.destroy" ], "file": "validation.json", "title": "deleting a root disposes its geometry and materials after clearing selection", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "panel.objects", "group": "对象属性、部件与底部面板", "name": "底部对象列表与选中", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "renderBottom(mode", "line": 1619 }, "implementation": "本轮修复已完成验证", "boundary": "基线显示前 6 个摘要,完整对象仍在大纲;需明确截断。", "acceptance": "大于 6 个对象时列表说明准确,摘要点击选择正确。", "fix": "设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。", "fixIds": [ "SCENE-F10" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "recordedAt": "2026-09-06T10:28:51.941Z", "kind": "e2e", "ids": [ "asset.diagnostic", "asset.equipment", "asset.workbench", "asset.shelf", "asset.safety-zone", "asset.camera", "asset.worker", "asset.light", "assets.basic-double-click", "assets.basic-drop", "search.assets", "panel.objects" ], "file": "evidence/basic-assets.json", "title": "八种基础资源逐项放置、真实拖放、搜索和保存重开", "details": "8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。", "screenshots": [ "30-eight-basic-assets-reopened.png" ] } ] }, { "id": "panel.binding", "group": "对象属性、部件与底部面板", "name": "设备绑定总表", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "renderBottom(mode", "line": 1619 }, "implementation": "本轮修复已完成验证", "boundary": "基线语义列恒写已绑定;需要真实类型/缺失/冲突反馈。", "acceptance": "编辑字段即时同步,空/重复数据不得假标已绑定。", "fix": "设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。", "fixIds": [ "SCENE-F10" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] } ] }, { "id": "panel.validation", "group": "对象属性、部件与底部面板", "name": "底部发布检查面板", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "renderBottom(mode", "line": 1619 }, "implementation": "本轮修复已完成验证", "boundary": "显示当前逐项检查,不等于已发布。", "acceptance": "顶部校验/发布失败可定位检查面板,内容随修改更新。", "fix": "设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。", "fixIds": [ "SCENE-F10" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "recordedAt": "2026-09-06T10:27:04.782Z", "kind": "browser", "ids": [ "validation.complete", "validation.bindings", "panel.validation", "publish.saved", "publish.failure", "publish.fixed-model", "cover.atomic", "project.back" ], "file": "evidence/publish-fixed-version.json", "title": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "details": "实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "verify opens live validation and whitespace binding edits immediately invalidate the active tab", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "panel.validation", "validation.bindings" ], "file": "validation.json", "title": "verify opens live validation and whitespace binding edits immediately invalidate the active tab", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "panel.logs", "group": "对象属性、部件与底部面板", "name": "操作日志", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "renderBottom(mode", "line": 1619 }, "implementation": "本轮修复已完成验证", "boundary": "基线为静态示例且误称本地存储;本轮只需真实本次运行日志,不扩展服务端审计。", "acceptance": "实际操作产生对应时间/结果;API 模式不显示本地存储为真值。", "fix": "设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。", "fixIds": [ "SCENE-F10" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "activity log reflects actual session events and keeps API error details transient", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "panel.logs" ], "file": "validation.json", "title": "activity log reflects actual session events and keeps API error details transient", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "history.undo", "group": "历史与预览", "name": "回退按钮 / Ctrl+Z", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async undo()", "line": 2417 }, "implementation": "本轮修复已完成验证", "boundary": "输入连续修改合并成合理历史步;恢复对象/模板/选择。", "acceptance": "属性、添加、删除、模板分别撤销,真实画面与文档一致。", "fix": "透视按钮实际恢复先前观察位置;导入、加载和历史恢复存在已保存相机时不再被车辆自动聚焦覆盖。;预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。;文档签名忽略更新时间和对象键顺序;保存后的 undo/redo 根据真实已保存文档判断 dirty;异步恢复失败保留现场并回滚历史索引。", "fixIds": [ "SCENE-F04", "SCENE-F05", "SCENE-F07" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] }, { "status": "PASS", "testTitle": "typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.undo", "history.redo", "transform.keys", "lifecycle.cache" ], "file": "validation.json", "title": "typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "loading and history restoration preserve an authored camera rather than auto-focusing a vehicle", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "json.rendering", "history.undo" ], "file": "validation.json", "title": "loading and history restoration preserve an authored camera rather than auto-focusing a vehicle", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "save B then undo A stays dirty, and redo B returns to the actual saved baseline", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.undo", "history.redo" ], "file": "validation.json", "title": "save B then undo A stays dirty, and redo B returns to the actual saved baseline", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.capacity", "history.undo", "history.redo" ], "file": "validation.json", "title": "default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "history.redo", "group": "历史与预览", "name": "前进按钮 / Ctrl+Y / Ctrl+Shift+Z", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async redo()", "line": 2437 }, "implementation": "本轮修复已完成验证", "boundary": "撤销后重做,分叉编辑清 redo。", "acceptance": "按钮与两种快捷键各执行;新编辑后旧 redo 不可用。", "fix": "预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。;文档签名忽略更新时间和对象键顺序;保存后的 undo/redo 根据真实已保存文档判断 dirty;异步恢复失败保留现场并回滚历史索引。", "fixIds": [ "SCENE-F05", "SCENE-F07" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "recordedAt": "2026-09-06T10:30:32.596Z", "kind": "browser", "ids": [ "project.save-key", "part.transform", "part.soft-delete", "history.undo", "history.redo", "property.name", "view.enclosure-full", "view.enclosure-xray", "view.enclosure-hidden" ], "file": "evidence/truck-part-history.json", "title": "牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致", "details": "实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。", "screenshots": [] }, { "status": "PASS", "testTitle": "typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.undo", "history.redo", "transform.keys", "lifecycle.cache" ], "file": "validation.json", "title": "typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "save B then undo A stays dirty, and redo B returns to the actual saved baseline", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.undo", "history.redo" ], "file": "validation.json", "title": "save B then undo A stays dirty, and redo B returns to the actual saved baseline", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.capacity", "history.undo", "history.redo" ], "file": "validation.json", "title": "default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "history.capacity", "group": "历史与预览", "name": "60 步历史上限与边界", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "new SceneHistory(60)", "line": 180 }, "implementation": "已有实现已完成验证", "boundary": "历史属于本次编辑会话;不宣称服务端版本历史。", "acceptance": "聚焦测试验证 60 步边界、空 undo/redo 与失焦快照。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.capacity", "history.undo", "history.redo" ], "file": "validation.json", "title": "default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "history.failure", "group": "历史与预览", "name": "异步历史恢复失败/互斥", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async restoreHistoryState", "line": 2328 }, "implementation": "本轮修复已完成验证", "boundary": "失败不能推进索引或丢当前有效场景;忙碌时写操作需一致。", "acceptance": "GLB 恢复失败、并发 undo/redo、保存混入均不半提交。", "fix": "文档签名忽略更新时间和对象键顺序;保存后的 undo/redo 根据真实已保存文档判断 dirty;异步恢复失败保留现场并回滚历史索引。", "fixIds": [ "SCENE-F07" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "failed undo restoration retains live objects and keeps the undo target available for retry", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.failure" ], "file": "validation.json", "title": "failed undo restoration retains live objects and keeps the undo target available for retry", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "replacement failure waits for other started loads before restoring the original scene", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.failure", "glb.pending" ], "file": "validation.json", "title": "replacement failure waits for other started loads before restoring the original scene", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "preview.enter", "group": "历史与预览", "name": "进入运行预览", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "togglePreview()", "line": 583 }, "implementation": "本轮修复已完成验证", "boundary": "当前默认预览只展示场景,未接模型 timeline/blueprint 自动执行。", "acceptance": "实际进入,辅助选择/手柄状态正常,模型可见。", "fix": "预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。", "fixIds": [ "SCENE-F05" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "recordedAt": "2026-09-06T10:31:05.221Z", "kind": "browser", "ids": [ "permission.readonly", "permission.update", "permission.publish", "preview.enter", "preview.exit", "preview.lock", "json.export" ], "file": "evidence/readonly.json", "title": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "details": "只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] }, { "id": "preview.exit", "group": "历史与预览", "name": "按钮 / Escape 退出预览", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "handleKeyboard(event)", "line": 469 }, "implementation": "本轮修复已完成验证", "boundary": "退出后恢复编辑选择与状态;不得丢未存变换。", "acceptance": "两种退出方式均有效,输入焦点与缓存激活行为正确。", "fix": "预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。", "fixIds": [ "SCENE-F05" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "recordedAt": "2026-09-06T10:31:05.221Z", "kind": "browser", "ids": [ "permission.readonly", "permission.update", "permission.publish", "preview.enter", "preview.exit", "preview.lock", "json.export" ], "file": "evidence/readonly.json", "title": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "details": "只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] }, { "status": "PASS", "testTitle": "preview locks mutation controls and Delete while Escape restores editing", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "preview.lock", "preview.exit" ], "file": "validation.json", "title": "preview locks mutation controls and Delete while Escape restores editing", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "preview.lock", "group": "历史与预览", "name": "预览中禁止编辑", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "togglePreview()", "line": 583 }, "implementation": "本轮修复已完成验证", "boundary": "显示“场景编辑锁定”必须覆盖属性、删除、模板、导入和快捷键。", "acceptance": "尝试所有写入口,预览时文档不改变;退出后正常。", "fix": "预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。", "fixIds": [ "SCENE-F05" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "recordedAt": "2026-09-06T10:31:05.221Z", "kind": "browser", "ids": [ "permission.readonly", "permission.update", "permission.publish", "preview.enter", "preview.exit", "preview.lock", "json.export" ], "file": "evidence/readonly.json", "title": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "details": "只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] }, { "status": "PASS", "testTitle": "preview locks mutation controls and Delete while Escape restores editing", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "preview.lock", "preview.exit" ], "file": "validation.json", "title": "preview locks mutation controls and Delete while Escape restores editing", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "cover.capture", "group": "保存封面、发布与依赖", "name": "保存时截取当前三维场景封面", "source": { "file": "../../../unreal_tran_web/src/features/scene-legacy/sceneCoverCapture.ts", "symbol": "export async function captureSceneCover", "line": 26 }, "implementation": "已有实现已完成验证", "boundary": "JPEG 最大边 640、当前相机;隐藏手柄/网格/高亮且恢复现场。", "acceptance": "截图与服务端图片可辨一致,像素实际有模型,UI 状态不被破坏。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "cover.capture", "selection.highlight", "view.grid" ], "file": "validation.json", "title": "copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "cover.atomic", "group": "保存封面、发布与依赖", "name": "封面资产与文档同次保存", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "saveSceneDocumentWithCover", "line": 47 }, "implementation": "已有实现已完成验证", "boundary": "封面 upload ticket 与 version 同一次 PUT 消费,不另存过期文档。", "acceptance": "检查资产/封面字段/当前版本相同,并真实列表加载图片。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "recordedAt": "2026-09-06T10:30:14.385Z", "kind": "browser", "ids": [ "project.load", "project.title", "project.save", "selection.tree", "property.name", "property.visible", "property.operable", "property.position", "property.rotation", "property.scale", "property.device-id", "property.model-id", "property.semantic", "part.source", "panel.binding", "cover.capture", "cover.atomic" ], "file": "evidence/fox-properties-cover.json", "title": "狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开", "details": "真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。", "screenshots": [] }, { "status": "PASS", "testTitle": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "recordedAt": "2026-09-06T10:27:04.782Z", "kind": "browser", "ids": [ "validation.complete", "validation.bindings", "panel.validation", "publish.saved", "publish.failure", "publish.fixed-model", "cover.atomic", "project.back" ], "file": "evidence/publish-fixed-version.json", "title": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "details": "实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "scene image ticket, fixed asset replacement, dependencies and coverUri commit in one versioned PUT", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "cover.atomic" ], "file": "validation.json", "title": "scene image ticket, fixed asset replacement, dependencies and coverUri commit in one versioned PUT", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "cover.failure", "group": "保存封面、发布与依赖", "name": "截图/封面上传失败保留原封面", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "coverFailed", "line": 390 }, "implementation": "已有实现已完成验证", "boundary": "正文可保存时给真实 warning;409 必须阻断且不得覆盖。", "acceptance": "截图失败、上传503、PUT409各自核对保留/提示/零错误发布。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "保存失败与封面降级:503 保留草稿,封面上传失败保留旧图,恢复后可重试", "recordedAt": "2026-09-06T10:26:33.388Z", "kind": "browser", "ids": [ "project.save-failure", "cover.failure" ], "file": "evidence/save-cover-failure.json", "title": "保存失败与封面降级:503 保留草稿,封面上传失败保留旧图,恢复后可重试", "details": "正文PUT503不清草稿;封面上传503保留旧图但正文可保存;恢复服务后保存刷新。截图异常和409另由聚焦合同验证。", "screenshots": [] }, { "status": "PASS", "testTitle": "capture failure saves content with the previous cover and reports a warning result", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "cover.failure" ], "file": "validation.json", "title": "capture failure saves content with the previous cover and reports a warning result", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "upload conflict propagates without any content PUT", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "project.conflict", "cover.failure" ], "file": "validation.json", "title": "upload conflict propagates without any content PUT", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "validation.complete", "group": "保存封面、发布与依赖", "name": "环境、对象用途、模型加载检查", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "verifyScene(showToast", "line": 1715 }, "implementation": "已有实现已完成验证", "boundary": "纯环境允许;模型未完成解析必须阻断。", "acceptance": "空地板/纯环境/带模型和加载中分别核对检查结果。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "recordedAt": "2026-09-06T10:27:04.782Z", "kind": "browser", "ids": [ "validation.complete", "validation.bindings", "panel.validation", "publish.saved", "publish.failure", "publish.fixed-model", "cover.atomic", "project.back" ], "file": "evidence/publish-fixed-version.json", "title": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "details": "实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。", "screenshots": [] } ] }, { "id": "validation.bindings", "group": "保存封面、发布与依赖", "name": "设备/物模型编号完整与唯一", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "verifyScene(showToast", "line": 1715 }, "implementation": "本轮修复已完成验证", "boundary": "空白、重复编号按前后端合同处理,不自动替用户分配新业务身份。", "acceptance": "空、空格、重复与合法编号;错误项可见,发布被阻断。", "fix": "恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。;设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。", "fixIds": [ "SCENE-F06", "SCENE-F10" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "recordedAt": "2026-09-06T10:27:04.782Z", "kind": "browser", "ids": [ "validation.complete", "validation.bindings", "panel.validation", "publish.saved", "publish.failure", "publish.fixed-model", "cover.atomic", "project.back" ], "file": "evidence/publish-fixed-version.json", "title": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "details": "实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "new resource bindings do not reuse a saved serial after earlier objects were deleted", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "validation.bindings" ], "file": "validation.json", "title": "new resource bindings do not reuse a saved serial after earlier objects were deleted", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "verify opens live validation and whitespace binding edits immediately invalidate the active tab", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "panel.validation", "validation.bindings" ], "file": "validation.json", "title": "verify opens live validation and whitespace binding edits immediately invalidate the active tab", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "validation.references", "group": "保存封面、发布与依赖", "name": "受控资源引用完整性", "source": { "file": "../../../unreal_tran_web/src/features/scene-legacy/serverAssetStore.ts", "symbol": "hasReferenceSync", "line": 18 }, "implementation": "本轮修复已完成验证", "boundary": "project/version/assetCode/sourceAssetId 必须指向实际固定资产,不能只测有字段。", "acceptance": "不存在编码/错误资产类型/跨项目引用显式拒绝;正确依赖通过。", "fix": "前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。;新增 SceneResourceReferenceValidator 检查 objects 和递归 splitParts;自有/固定版本资源的所有显式引用必须指向同一 READY 模型文件,错误引用在发布/提交前阻断,保留受控的内置资源和旧引用兼容。", "fixIds": [ "SCENE-F12", "SCENE-F13" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "recordedAt": "2026-09-06T10:26:19.703Z", "kind": "browser", "ids": [ "json.export", "json.import", "json.invalid", "json.missing-resource", "json.rendering", "glb.import", "glb.invalid", "glb.references", "validation.references" ], "file": "evidence/json-glb-roundtrip.json", "title": "工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返", "details": "真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。", "screenshots": [] }, { "status": "PASS", "testTitle": "an explicit missing code cannot fall back to the first READY asset", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "validation.references", "json.missing-resource" ], "file": "validation.json", "title": "an explicit missing code cannot fall back to the first READY asset", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "publish.saved", "group": "保存封面、发布与依赖", "name": "发布场景按钮的真实 API 闭环", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async publishScene()", "line": 2213 }, "implementation": "已有实现已完成验证", "boundary": "有编辑权时先保存含封面再发布;两步均成功才显示已发布。", "acceptance": "POST 真正发布当前版本,列表状态与下游目录一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "recordedAt": "2026-09-06T10:27:04.782Z", "kind": "browser", "ids": [ "validation.complete", "validation.bindings", "panel.validation", "publish.saved", "publish.failure", "publish.fixed-model", "cover.atomic", "project.back" ], "file": "evidence/publish-fixed-version.json", "title": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "details": "实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。", "screenshots": [] } ] }, { "id": "publish.only", "group": "保存封面、发布与依赖", "name": "只有发布权限时发布已存版本", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "if (this.readOnly)", "line": 335 }, "implementation": "已有实现已完成验证", "boundary": "publish-only 不先 PUT、不上传封面、不要求 update;服务器校验真值。", "acceptance": "隔离权限会话发布已存有效版本,零 update/upload 请求。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求", "recordedAt": "2026-09-06T10:25:19.519Z", "kind": "browser", "ids": [ "permission.publish", "publish.only", "publish.fixed-model" ], "file": "evidence/scene-publish-only.json", "title": "仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求", "details": "", "screenshots": [] } ] }, { "id": "publish.failure", "group": "保存封面、发布与依赖", "name": "预检/保存/发布失败阻断", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "async publishScene()", "line": 2213 }, "implementation": "本轮修复已完成验证", "boundary": "保存失败不 POST 发布;发布失败不假标已发布;保持错误恢复能力。", "acceptance": "预检错误、PUT409/503、POST失败及重试,版本/状态准确。", "fix": "新增 SceneResourceReferenceValidator 检查 objects 和递归 splitParts;自有/固定版本资源的所有显式引用必须指向同一 READY 模型文件,错误引用在发布/提交前阻断,保留受控的内置资源和旧引用兼容。", "fixIds": [ "SCENE-F13" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "recordedAt": "2026-09-06T10:27:04.782Z", "kind": "browser", "ids": [ "validation.complete", "validation.bindings", "panel.validation", "publish.saved", "publish.failure", "publish.fixed-model", "cover.atomic", "project.back" ], "file": "evidence/publish-fixed-version.json", "title": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "details": "实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。", "screenshots": [] } ] }, { "id": "publish.concurrent", "group": "保存封面、发布与依赖", "name": "重复发布与上传/保存队列", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "publishDocument:", "line": 509 }, "implementation": "已有实现已完成验证", "boundary": "整条 POST 完成前仍占 FIFO;重复操作不串版本。", "acceptance": "延迟上传/保存/发布并触发竞争,服务端只有完整合法版本。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "role switch waits through validation, save, and the later queued publish after dirty clears", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "publish.concurrent", "leave.busy", "leave.role-switch" ], "file": "validation.json", "title": "role switch waits through validation, save, and the later queued publish after dirty clears", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "project operation queue serializes upload, save and publish including failures", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "publish.concurrent" ], "file": "validation.json", "title": "project operation queue serializes upload, save and publish including failures", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "publish.fixed-model", "group": "保存封面、发布与依赖", "name": "固定 SCENE_MODEL 版本与多个实例", "source": { "file": "../../../unreal_tran_web/src/features/scene-legacy/contentApiClient.ts", "symbol": "collectSceneModelDependencies", "line": 938 }, "implementation": "本轮修复已完成验证", "boundary": "去重精确模型版本,不能随模型新发布静默换源版本。", "acceptance": "同模型多实例及另模型新版本,场景重开仍使用所存固定版本。", "fix": "前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。;新增 SceneResourceReferenceValidator 检查 objects 和递归 splitParts;自有/固定版本资源的所有显式引用必须指向同一 READY 模型文件,错误引用在发布/提交前阻断,保留受控的内置资源和旧引用兼容。;完整 PUT 回写原有相同 SCENE_MODEL 三元组时沿用场景授权边界;新增或改版依赖仍要求来源模型可见及范围权限,不扩大模型页面访问权。;新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。", "fixIds": [ "SCENE-F12", "SCENE-F13", "SCENE-F14", "SCENE-F18" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "recordedAt": "2026-09-06T10:27:04.782Z", "kind": "browser", "ids": [ "validation.complete", "validation.bindings", "panel.validation", "publish.saved", "publish.failure", "publish.fixed-model", "cover.atomic", "project.back" ], "file": "evidence/publish-fixed-version.json", "title": "发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用", "details": "实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求", "recordedAt": "2026-09-06T10:25:19.519Z", "kind": "browser", "ids": [ "permission.publish", "publish.only", "publish.fixed-model" ], "file": "evidence/scene-publish-only.json", "title": "仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面", "recordedAt": "2026-09-06T10:25:11.741Z", "kind": "browser", "ids": [ "permission.update", "permission.publish", "publish.fixed-model" ], "file": "evidence/scene-update-only.json", "title": "仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "legacy duplicate instance IDs migrate only later owners in document order despite reversed GLB completion", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "part.transform", "publish.fixed-model" ], "file": "validation.json", "title": "legacy duplicate instance IDs migrate only later owners in document order despite reversed GLB completion", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "fixed model reads the exact version and downloads through its visible scene dependency", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "publish.fixed-model", "permission.update" ], "file": "validation.json", "title": "fixed model reads the exact version and downloads through its visible scene dependency", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "fixed model dependencies are recursively collected, exact and de-duplicated", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "publish.fixed-model" ], "file": "validation.json", "title": "fixed model dependencies are recursively collected, exact and de-duplicated", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "publish.downstream", "group": "保存封面、发布与依赖", "name": "发布场景供训练选择和静态还原", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "场景已发布,训练编排可以引用", "line": 2227 }, "implementation": "已有实现已完成验证", "boundary": "本轮只验证版本/目标/静态可见性;不承诺模型蓝图或原生动画自动执行。", "acceptance": "新场景出现在正式训练目录,绑定后狐狸/卡车可见且目标身份一致。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "fox:场景发布后在真实训练编排选择、保存并重开静态模型", "recordedAt": "2026-09-06T10:24:43.055Z", "kind": "browser", "ids": [ "publish.downstream" ], "file": "evidence/downstream-fox.json", "title": "fox:场景发布后在真实训练编排选择、保存并重开静态模型", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "truck:场景发布后在真实训练编排选择、保存并重开静态模型", "recordedAt": "2026-09-06T10:25:01.457Z", "kind": "browser", "ids": [ "publish.downstream" ], "file": "evidence/downstream-truck.json", "title": "truck:场景发布后在真实训练编排选择、保存并重开静态模型", "details": "", "screenshots": [] } ] }, { "id": "permission.update", "group": "权限、缓存与未保存恢复", "name": "场景细分编辑权限", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "this.readOnly =", "line": 149 }, "implementation": "本轮修复已完成验证", "boundary": "只按 content.scene.update;基线残留 content.update 需修,不由角色名推断。", "acceptance": "具更新权会话可编辑;无更新权的写入口和接口全部拒绝。", "fix": "移除 runtime 旧 content.update 门槛,独立计算新建、更新和发布;无模型页面权限时不加载新模型目录,但既有固定依赖仍可按场景授权读取。;完整 PUT 回写原有相同 SCENE_MODEL 三元组时沿用场景授权边界;新增或改版依赖仍要求来源模型可见及范围权限,不扩大模型页面访问权。", "fixIds": [ "SCENE-F01", "SCENE-F14" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "recordedAt": "2026-09-06T10:31:05.221Z", "kind": "browser", "ids": [ "permission.readonly", "permission.update", "permission.publish", "preview.enter", "preview.exit", "preview.lock", "json.export" ], "file": "evidence/readonly.json", "title": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "details": "只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面", "recordedAt": "2026-09-06T10:25:11.741Z", "kind": "browser", "ids": [ "permission.update", "permission.publish", "publish.fixed-model" ], "file": "evidence/scene-update-only.json", "title": "仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "scene granular update permission enables editing without the retired shared code", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "permission.update" ], "file": "validation.json", "title": "scene granular update permission enables editing without the retired shared code", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "fixed model reads the exact version and downloads through its visible scene dependency", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "publish.fixed-model", "permission.update" ], "file": "validation.json", "title": "fixed model reads the exact version and downloads through its visible scene dependency", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "permission.publish", "group": "权限、缓存与未保存恢复", "name": "场景细分发布权限", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "this.canPublish =", "line": 152 }, "implementation": "本轮修复已完成验证", "boundary": "页面 content.scene 与 publish 动作分开;更新权不隐含发布。", "acceptance": "只更新不能发布;仅发布可发布已存版本;无页面权不可访问。", "fix": "移除 runtime 旧 content.update 门槛,独立计算新建、更新和发布;无模型页面权限时不加载新模型目录,但既有固定依赖仍可按场景授权读取。", "fixIds": [ "SCENE-F01" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "recordedAt": "2026-09-06T10:31:05.221Z", "kind": "browser", "ids": [ "permission.readonly", "permission.update", "permission.publish", "preview.enter", "preview.exit", "preview.lock", "json.export" ], "file": "evidence/readonly.json", "title": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "details": "只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "无场景页面权限:编辑路由和直接内容接口均拒绝访问", "recordedAt": "2026-09-06T10:25:24.402Z", "kind": "browser", "ids": [ "permission.publish" ], "file": "evidence/scene-no-page-permission.json", "title": "无场景页面权限:编辑路由和直接内容接口均拒绝访问", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求", "recordedAt": "2026-09-06T10:25:19.519Z", "kind": "browser", "ids": [ "permission.publish", "publish.only", "publish.fixed-model" ], "file": "evidence/scene-publish-only.json", "title": "仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面", "recordedAt": "2026-09-06T10:25:11.741Z", "kind": "browser", "ids": [ "permission.update", "permission.publish", "publish.fixed-model" ], "file": "evidence/scene-update-only.json", "title": "仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面", "details": "", "screenshots": [] } ] }, { "id": "permission.readonly", "group": "权限、缓存与未保存恢复", "name": "只读仍可查看、搜索、选中和预览", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "applyAccessMode()", "line": 198 }, "implementation": "本轮修复已完成验证", "boundary": "禁写不应误禁观察控件;临时视图不保存到服务器。", "acceptance": "真实键盘搜索、选择、相机、预览,零内容写请求。", "fix": "移除 runtime 旧 content.update 门槛,独立计算新建、更新和发布;无模型页面权限时不加载新模型目录,但既有固定依赖仍可按场景授权读取。", "fixIds": [ "SCENE-F01" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "recordedAt": "2026-09-06T10:31:05.221Z", "kind": "browser", "ids": [ "permission.readonly", "permission.update", "permission.publish", "preview.enter", "preview.exit", "preview.lock", "json.export" ], "file": "evidence/readonly.json", "title": "只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景", "details": "只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。", "screenshots": [] }, { "status": "PASS", "testTitle": "后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限", "recordedAt": "2026-09-06T10:25:39.438Z", "kind": "browser", "ids": [ "leave.role-switch", "leave.cancel", "leave.save", "permission.readonly" ], "file": "evidence/scene-role-switch.json", "title": "后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限", "details": "", "screenshots": [] } ] }, { "id": "recovery.autobuffer", "group": "权限、缓存与未保存恢复", "name": "800ms 未保存恢复缓冲", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "markDirty(label", "line": 654 }, "implementation": "本轮修复已完成验证", "boundary": "仅写隔离浏览器恢复稿,不伪称自动入库。", "acceptance": "实际修改后等待,恢复稿存在但无自动 PUT;手动保存后入库。", "fix": "顶部新建委托宿主命名与创建 API,先确认未保存内容。选择放弃也要等创建及导航成功后才清除原 dirty/恢复稿;创建503仍保留原稿。成功后刷新原缓存页,避免再次打开时将已放弃修改冒称已保存。取消、失败不清原场景,成功导航到新项目 ID,并有创建并发守卫。;开始保存时取消之前安排的恢复稿定时器,避免它覆盖宿主保存快照 nonce;保存中的后续编辑仍单独保留。", "fixIds": [ "SCENE-F02", "SCENE-F08" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载", "recordedAt": "2026-09-06T10:26:49.585Z", "kind": "browser", "ids": [ "project.conflict", "recovery.autobuffer", "recovery.same-version", "recovery.stale-version" ], "file": "evidence/real-recovery-conflict.json", "title": "同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载", "details": "真实未存输入产生恢复稿,同版本恢复不自动入库;另一版本先保存后409拒绝覆盖,旧稿隔离下载。", "screenshots": [] }, { "status": "PASS", "testTitle": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "recordedAt": "2026-09-06T10:24:27.147Z", "kind": "browser", "ids": [ "project.concurrent-save", "project.save", "project.save-key", "recovery.autobuffer", "recovery.same-version" ], "file": "evidence/save-concurrency.json", "title": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "details": "延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。", "screenshots": [] }, { "status": "PASS", "testTitle": "双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿", "recordedAt": "2026-09-06T10:25:56.822Z", "kind": "browser", "ids": [ "recovery.multi-session", "recovery.autobuffer", "recovery.discard", "project.save" ], "file": "evidence/scene-multi-session.json", "title": "双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿", "details": "", "screenshots": [] } ] }, { "id": "recovery.same-version", "group": "权限、缓存与未保存恢复", "name": "同版本真实未保存恢复", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "sceneRecoveryMatchesProjectVersion", "line": 46 }, "implementation": "本轮修复已完成验证", "boundary": "先回到视口,须显式保存才入库;相同文档不应产生假恢复。", "acceptance": "真实修改后刷新,恢复/保存/再刷新;无注入假 localStorage 代替。", "fix": "文档签名忽略更新时间和对象键顺序;保存后的 undo/redo 根据真实已保存文档判断 dirty;异步恢复失败保留现场并回滚历史索引。;开始保存时取消之前安排的恢复稿定时器,避免它覆盖宿主保存快照 nonce;保存中的后续编辑仍单独保留。", "fixIds": [ "SCENE-F07", "SCENE-F08" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载", "recordedAt": "2026-09-06T10:26:49.585Z", "kind": "browser", "ids": [ "project.conflict", "recovery.autobuffer", "recovery.same-version", "recovery.stale-version" ], "file": "evidence/real-recovery-conflict.json", "title": "同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载", "details": "真实未存输入产生恢复稿,同版本恢复不自动入库;另一版本先保存后409拒绝覆盖,旧稿隔离下载。", "screenshots": [] }, { "status": "PASS", "testTitle": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "recordedAt": "2026-09-06T10:24:27.147Z", "kind": "browser", "ids": [ "project.concurrent-save", "project.save", "project.save-key", "recovery.autobuffer", "recovery.same-version" ], "file": "evidence/save-concurrency.json", "title": "延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突", "details": "延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。", "screenshots": [] } ] }, { "id": "recovery.stale-version", "group": "权限、缓存与未保存恢复", "name": "不同版本恢复稿隔离", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "sceneRecoveryMatchesProjectVersion", "line": 46 }, "implementation": "已有实现已完成验证", "boundary": "旧稿不能自动覆盖新服务器版本;下载供人工合并。", "acceptance": "另一会话先保存,新会话面对旧稿只能下载/隔离/明确丢弃。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载", "recordedAt": "2026-09-06T10:26:49.585Z", "kind": "browser", "ids": [ "project.conflict", "recovery.autobuffer", "recovery.same-version", "recovery.stale-version" ], "file": "evidence/real-recovery-conflict.json", "title": "同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载", "details": "真实未存输入产生恢复稿,同版本恢复不自动入库;另一版本先保存后409拒绝覆盖,旧稿隔离下载。", "screenshots": [] } ] }, { "id": "recovery.discard", "group": "权限、缓存与未保存恢复", "name": "下载、保留、丢弃恢复稿", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "async function quarantineRecovery", "line": 223 }, "implementation": "已有实现已完成验证", "boundary": "关闭/Escape 不算明确丢弃;他会话新写入稿不得被误删。", "acceptance": "按钮逐项核对本地隔离键、下载文件与服务器无意外写入。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿", "recordedAt": "2026-09-06T10:25:56.822Z", "kind": "browser", "ids": [ "recovery.multi-session", "recovery.autobuffer", "recovery.discard", "project.save" ], "file": "evidence/scene-multi-session.json", "title": "双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "recovery conflict defaults to preserve and only explicit discard may remove it", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "recovery.discard" ], "file": "validation.json", "title": "recovery conflict defaults to preserve and only explicit discard may remove it", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "recovery.multi-session", "group": "权限、缓存与未保存恢复", "name": "多标签/多会话恢复所有权", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "recoveryStoragePrefix", "line": 104 }, "implementation": "已有实现已完成验证", "boundary": "会话键/nonce/锁避免覆盖别的未存稿;本地缓冲不冒充权限。", "acceptance": "两个编辑会话产生不同草稿,保存/丢弃一份不删另一份。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿", "recordedAt": "2026-09-06T10:25:56.822Z", "kind": "browser", "ids": [ "recovery.multi-session", "recovery.autobuffer", "recovery.discard", "project.save" ], "file": "evidence/scene-multi-session.json", "title": "双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "per-page recovery keys prevent copied tabs from deleting each other checkpoints", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "recovery.multi-session" ], "file": "validation.json", "title": "per-page recovery keys prevent copied tabs from deleting each other checkpoints", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "leave.cancel", "group": "权限、缓存与未保存恢复", "name": "关闭场景标签时取消离开", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "async function confirmLeave", "line": 704 }, "implementation": "本轮修复已完成验证", "boundary": "关闭确认保留当前 dirty 与恢复副本;返回列表或切普通标签不应提示。", "acceptance": "真实关闭确认的关闭/Escape、继续编辑,数据未丢;不把返回列表当关闭。", "fix": "关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。", "fixIds": [ "SCENE-F16" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限", "recordedAt": "2026-09-06T10:25:39.438Z", "kind": "browser", "ids": [ "leave.role-switch", "leave.cancel", "leave.save", "permission.readonly" ], "file": "evidence/scene-role-switch.json", "title": "后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "closing one selected scene checks its cached guard, leaves unrelated workspaces alone", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "leave.cancel", "lifecycle.cache" ], "file": "validation.json", "title": "closing one selected scene checks its cached guard, leaves unrelated workspaces alone", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "cancel or failed save in a later tab never commits earlier draft discard", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "leave.cancel", "leave.save", "leave.role-switch" ], "file": "validation.json", "title": "cancel or failed save in a later tab never commits earlier draft discard", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "leave.save", "group": "权限、缓存与未保存恢复", "name": "保存并离开 / 放弃修改", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "async function confirmLeave", "line": 704 }, "implementation": "本轮修复已完成验证", "boundary": "等待保存完成并确认最终 dirty;明确放弃只影响当前工程会话。", "acceptance": "保存成功才离开;503/409留原页;放弃按确认执行。", "fix": "关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。", "fixIds": [ "SCENE-F16" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限", "recordedAt": "2026-09-06T10:25:39.438Z", "kind": "browser", "ids": [ "leave.role-switch", "leave.cancel", "leave.save", "permission.readonly" ], "file": "evidence/scene-role-switch.json", "title": "后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "cancel or failed save in a later tab never commits earlier draft discard", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "leave.cancel", "leave.save", "leave.role-switch" ], "file": "validation.json", "title": "cancel or failed save in a later tab never commits earlier draft discard", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "all successful confirmations commit together and unmounted guards are removed", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "leave.save", "lifecycle.destroy" ], "file": "validation.json", "title": "all successful confirmations commit together and unmounted guards are removed", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "leave.busy", "group": "权限、缓存与未保存恢复", "name": "加载/上传/发布途中离开", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "const resourcesBusy =", "line": 708 }, "implementation": "已有实现已完成验证", "boundary": "资源未就绪阻止离开;进行中的保存/发布须等待最终结果。", "acceptance": "延迟资源与提交各验证取消/等待,旧请求不污染新项目。", "fix": "尚未关联本轮修复记录", "fixIds": [], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载", "recordedAt": "2026-09-06T10:24:14.518Z", "kind": "browser", "ids": [ "project.retry", "glb.pending", "leave.busy" ], "file": "evidence/loading-retry.json", "title": "加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载", "details": "GET503只留中性失败和重试;延迟受控下载时禁用写入/导出/预览并阻止关闭,释放后真实狐狸加载。", "screenshots": [] }, { "status": "PASS", "testTitle": "role switch waits through validation, save, and the later queued publish after dirty clears", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "publish.concurrent", "leave.busy", "leave.role-switch" ], "file": "validation.json", "title": "role switch waits through validation, save, and the later queued publish after dirty clears", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "role switch rechecks resource uploads started while the save was finishing", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "leave.busy" ], "file": "validation.json", "title": "role switch rechecks resource uploads started while the save was finishing", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "leave.role-switch", "group": "权限、缓存与未保存恢复", "name": "角色切换覆盖后台场景未存稿", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue", "symbol": "registerWorkspaceLeaveGuard", "line": 2 }, "implementation": "本轮修复已完成验证", "boundary": "全体守卫确认后才切角色;后一页取消不能抹掉前页 dirty。", "acceptance": "含后台场景的切角色确认取消/保存失败/成功后权限重建。", "fix": "关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。", "fixIds": [ "SCENE-F16" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限", "recordedAt": "2026-09-06T10:25:39.438Z", "kind": "browser", "ids": [ "leave.role-switch", "leave.cancel", "leave.save", "permission.readonly" ], "file": "evidence/scene-role-switch.json", "title": "后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "cancel or failed save in a later tab never commits earlier draft discard", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "leave.cancel", "leave.save", "leave.role-switch" ], "file": "validation.json", "title": "cancel or failed save in a later tab never commits earlier draft discard", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "role switch waits through validation, save, and the later queued publish after dirty clears", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "publish.concurrent", "leave.busy", "leave.role-switch" ], "file": "validation.json", "title": "role switch waits through validation, save, and the later queued publish after dirty clears", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "lifecycle.cache", "group": "权限、缓存与未保存恢复", "name": "标签失活 pause / 激活 resume", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "pause()", "line": 523 }, "implementation": "本轮修复已完成验证", "boundary": "停止后台 RAF、恢复时重算视口;后台实例不能抢当前页键盘。", "acceptance": "两场景标签切换、选择和未存值保留、后台无写/快捷键误动作。", "fix": "顶部新建委托宿主命名与创建 API,先确认未保存内容。选择放弃也要等创建及导航成功后才清除原 dirty/恢复稿;创建503仍保留原稿。成功后刷新原缓存页,避免再次打开时将已放弃修改冒称已保存。取消、失败不清原场景,成功导航到新项目 ID,并有创建并发守卫。;预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。;关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。", "fixIds": [ "SCENE-F02", "SCENE-F05", "SCENE-F16" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "缓存标签暂停键盘、草稿恢复以及关闭取消和保存失败留页", "recordedAt": "2026-09-06T10:30:03.089Z", "kind": "真实UI缓存/关闭+浏览器模拟503拒绝写入", "ids": [ "lifecycle.cache" ], "file": "evidence/viewport-cache-close.json", "title": "缓存标签暂停键盘、草稿恢复以及关闭取消和保存失败留页", "details": "", "screenshots": [] }, { "status": "PASS", "testTitle": "typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "history.undo", "history.redo", "transform.keys", "lifecycle.cache" ], "file": "validation.json", "title": "typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "closing one selected scene checks its cached guard, leaves unrelated workspaces alone", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "leave.cancel", "lifecycle.cache" ], "file": "validation.json", "title": "closing one selected scene checks its cached guard, leaves unrelated workspaces alone", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "lifecycle.destroy", "group": "权限、缓存与未保存恢复", "name": "关闭释放 WebGL/监听/资源", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "destroy()", "line": 421 }, "implementation": "本轮修复已完成验证", "boundary": "销毁后的异步回调无 DOM 写入,无残留全局事件和资产 URL。", "acceptance": "反复打开/关闭、加载中关闭边界,控制台无异常/泄漏事件。", "fix": "射线候选逐级检查父级可见性和软删除;删除根对象先恢复高亮材质再释放几何/材质,避免错误拾取及 GPU 资源残留。;关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。", "fixIds": [ "SCENE-F09", "SCENE-F16" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "deleting a root disposes its geometry and materials after clearing selection", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "object.delete", "lifecycle.destroy" ], "file": "validation.json", "title": "deleting a root disposes its geometry and materials after clearing selection", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "selection highlight cleanup restores source materials and releases its own GPU helper", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "selection.highlight", "lifecycle.destroy" ], "file": "validation.json", "title": "selection highlight cleanup restores source materials and releases its own GPU helper", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "all successful confirmations commit together and unmounted guards are removed", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "leave.save", "lifecycle.destroy" ], "file": "validation.json", "title": "all successful confirmations commit together and unmounted guards are removed", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] }, { "status": "PASS", "testTitle": "an upload finishing after a project switch cannot register into the new session", "kind": "unit", "runId": "scene-editor-2026-09-06T10:24:04.121Z", "ids": [ "glb.pending", "lifecycle.destroy" ], "file": "validation.json", "title": "an upload finishing after a project switch cannot register into the new session", "details": "实际执行真实方法/纯函数合同,未冒称浏览器操作", "screenshots": [] } ] }, { "id": "lifecycle.resize-theme", "group": "权限、缓存与未保存恢复", "name": "桌面尺寸与明亮/暗黑可读性", "source": { "file": "../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js", "symbol": "resize()", "line": 414 }, "implementation": "本轮修复已完成验证", "boundary": "继承正式主题;所有可达控件可读、无截断遮挡。", "acceptance": "1920/1440 下工具栏、属性、模板与弹窗截图,主题切换无低对比。", "fix": "原型 CSS 全局重置收敛到场景作用域,降低按钮重置优先级;侧栏、输入、模板、属性和摘要使用正式主题变量,修复白字白底;修复 1440 宽时长工具栏/画布撑开网格覆盖属性栏。", "fixIds": [ "SCENE-F15" ], "verification": "PASS", "evidence": [ { "status": "PASS", "testTitle": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "recordedAt": "2026-09-06T10:29:44.118Z", "kind": "真实UI+只读投影", "ids": [ "view.orbit", "view.pan", "view.zoom", "view.top", "view.perspective", "view.grid", "view.focus", "selection.viewport", "selection.highlight", "preview.enter", "preview.exit", "preview.lock", "lifecycle.resize-theme" ], "file": "evidence/viewport-camera-theme.json", "title": "真实相机操控、视口拾取、预览禁写和两尺寸主题可读性", "details": "", "screenshots": [ "40-viewport-dark-1920", "41-viewport-light-1440", "42-fox-preview-original-material" ] } ] } ] }