MODEL AUTHORING · CLOSURE REVIEW · 2026.09.06

装备模型编辑器
全功能闭环审查与验收

沿资料 → 产品原型 → 设计 → 开发 → 测试核查每一个可达动作。用狐狸、牛奶卡车等轻量模型验证通用编排能力,保留现有业务流程与版本数据。

已列功能的记录验证通过

已验证 · 报告生成:2026/9/6 16:34:35(北京时间)。源码已接通、合同测试和真实浏览器验证分别记录;没有断言关联的功能仍为待验证。

功能清单103全部可达动作逐项整理
逐项证据103 / 1030 项仍待验证
浏览器 E2E19 / 190 失败 · 最终轮已标记
截图 / 待确认41 / 3截图按实际文件读取

“已实现/本轮修复”是源码状态;绿色“已验证”要求独立证据。过程截图与测试总数不能自动把 103 个功能全部变成通过。

从参考资料提取通用能力

原始两份 PDF 共 11 页已由主代理完整渲染并视觉阅读。原始诉求集中在部件选择、操作步骤、交互反馈和结果追踪。本轮以狐狸、牛奶卡车等轻量模型验证部件选择、流程编排、交互反馈和结果保存。

维修训练内容设计.pdf · 电器控制系统故障维修脚本.pdf。报告仅链接原文,不复制全文或整页图片。

资料能力本轮可观察结果矩阵追踪
部件级选择与操作整体与部件区分;树、视口和属性联动;操作后可保存重开。selection.scope / selection.sync / property.*
动作流程时间轴关键帧、曲线插值、蓝图顺序和真假分支有实际执行结果。timeline.* / curve.* / blueprint.*
交互反馈点击/双击/自动事件、高亮、文字提示、停止和错误反馈与对象匹配。blueprint.click / double-click / auto / highlight / message
结果追踪模型版本、封面、引用和本次运行结果有证据;明确运行日志不是教学成绩或审计。publish.* / feedback.* / project.conflict

确认用户实际使用的执行入口

内容模型列表

选中正式项目,进入模型编辑路由。

Vue 宿主

传 projectId / api=1,注入权限与未保存确认。

同源 iframe

承载 legacy-model-editor/editor.html。

真实原型源码

main.js + timeline.js + blueprint.js,经 stage 构建。

内容 API

受控资产、项目版本、保存和发布。

当前默认入口由 content.tsLegacyModelEditorView.vue 确定,stage-legacy-model-editor.mjs 将真实原型源码打入静态资源。备用 DigitalTwinEditor.js 不是本轮默认入口。

覆盖顶部工具、场景树/资源库、三维视口、对象属性/组件说明、时间轴、曲线、蓝图、发布面板、运行日志和快捷键。代码中保留但没有可达按钮的“训练场景 JSON 导入”不计为用户可达功能。

明确保存、运行与验证的边界

工程数据闭环

对象变换/材质/自定义字段、时间轴/蓝图和环境稳定引用必须能保存、重开、撤销及 JSON 往返。临时 blob 只用于渲染,服务端资产标识用于持久化。

导入、截图、保存与发布期间保留互斥和乐观锁;失败不得用成功消息掩盖,也不得发布半绑定资源。

界面与业务范围

网格、线框、PBR、透视和展开布局属于当前视图选项,未跨刷新保存不作为缺陷。运行日志属于本次运行记录,不等同审计和教学成绩。

现有组件新增是名称标记;现有离线 HTML 是步骤演示;下游自动执行模型动作须另行确认。

全功能覆盖矩阵

每行列出实际执行入口、实现状态、边界、对应修复及独立验证要求。静态功能清单 · 带证据的矩阵 JSON · 修复前基线与实施记录

103 项

只读模型的搜索、原生动画选择与键盘预览

权限桥误把查看控件的键盘交互当成内容编辑,导致只读用户不能在搜索框输入、切换原生动画或按 Enter 进入预览。

按实际查看控件放行键盘输入/选择/激活;模型编辑、内容上传/保存/发布仍依对应权限阻断。

真实 DOM 权限桥 14/14 通过;最终完整19条E2E全部通过,其中管理员用例验证真实键盘搜索、原生动画切换与Enter播放/暂停,期间没有内容写请求,直接创建接口返回403。

源码 · 聚焦测试

Auth / Tran 连接有效性检查增加时限与空闲保活

数据库连接健康检查可能在驱动网络读取上长时间阻塞,拖住借用连接的请求;不能据此把每个历史503都逐条归因为此缺陷。

两个模块统一使用有时限的 SELECT 1 健康检查,默认临时网络时限 2 秒;检查结束恢复原连接超时,开启空闲检查和保活。已有普通业务 SQL 超时策略不变。

实际 Kingbase 驱动隔离探针:正常检查 12ms,原0ms和原7000ms网络超时均恢复;隔离网络黑洞约2010ms抛出 SocketTimeoutException 并关闭连接。探针不是整个平台可用性测试,后端打包和全量E2E结果另记。

源码 · 另一模块源码 · 验证证据

曲线轨道与分量控件的可读性

曲线轨道改为按钮后遗漏背景和边框样式,显示为灰底灰字;分量下拉沿用浏览器默认样式并与数值标签重叠。

补齐整宽暗色轨道按钮、主副文字与选中/悬停/键盘焦点样式;分量下拉使用暗色样式并定位右上。仅修改曲线面板CSS。

最终浏览器轮次重新操作轨道、关键帧、五种插值和分量选择并截图;可视效果以最终22与28号截图核对。

源码 · 验证证据

工程与数据

功能 / 真实执行路径实现状态闭环与边界本轮对应修复测试证据 / 验收要求
新建工程project.newLegacyModelEditorView.vue:118 · createNewProject本轮修复宿主创建新项目 ID 并跳转;新建不是清空并覆盖当前工程。新建复用正式 create API;创建失败或取消保留原工程。已验证新建工程取消及失败保留输入;成功创建新ID且不覆盖当前模型 · e2e+api
验收要求与证据范围新建前后 ID 不同,原工程内容/版本不被覆盖;取消不发创建请求。
导入装备 GLBproject.import-glbmain.js:1264 · importModelFile已有实现只支持单文件 GLB;上传→登记→解析→最终保存。延续导入 busy/失败阻断及稳定资产校验。已验证受控模型与外部环境 GLB 上传、JSON 往返和刷新重开 · e2e+api
验收要求与证据范围选择轻量 GLB,真实模型可见;刷新仍加载同一资产。

e2e+api:真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。

导入工程 JSONproject.import-jsonmain.js:2863 · importProjectFile本轮修复导入到当前工程,不创建副本;受控引用必须属于当前可用资产。JSON 不含 GLB 字节。导入前 materialize 稳定资产引用。已验证受控模型与外部环境 GLB 上传、JSON 往返和刷新重开 · e2e+api非法 JSON 与未登记 blob 引用导入失败时保留当前文档 · e2e+api
验收要求与证据范围同项目导出后再导入,材质/动画/环境不丢;无效或跨项目缺失引用报错。

e2e+api:真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。

手动保存 / Ctrl+Sproject.savemain.js:2748 · saveProject已有实现同一工程保存内容和当前视口封面;成功以 API 响应为准。保留队列、版本保护和封面失败回退。已验证受控模型与外部环境 GLB 上传、JSON 往返和刷新重开 · e2e+api保存请求超时和 409 保留恢复副本,解除故障后可重试 · e2e+api对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围保存真实 PUT 成功,刷新字段不丢;重复点击不交错。

e2e+api:真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。

e2e+api:真实页面保存及真实服务端重试;失败由浏览器定向注入 timedout/409,非宣称自然发生的服务故障。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

30 秒自动保存project.autosavemain.js:2122 · bindShellEvents已有实现自动保存工程内容;不反复截图更新封面,恢复稿待确认期间暂停。导入/封面保存期间阻断重入。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围修改后等待自动保存并刷新;忙碌时不提交半成品。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

修改工程名project.namemain.js:457 · projectNameInput已有实现输入→失焦/Enter→保存;当前模型工程名。无本轮专门改动。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围改名保存后列表和重新打开一致。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

另存项目 JSONproject.export-jsonmain.js:2883 · saveProjectAs已有实现下载工程 JSON;不是另建服务端项目,也不内嵌 GLB 文件。稳定资产引用保留,临时 blob 地址清除。已验证受控模型与外部环境 GLB 上传、JSON 往返和刷新重开 · e2e+api
验收要求与证据范围下载文件可解析,包含轨道/蓝图/自定义字段,配套资源可重新解析。

e2e+api:真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。

撤销 / Ctrl+Zproject.undomain.js:160 · class HistoryStack本轮修复最多 60 个历史快照;不是数据库版本回滚。异步互斥,失败不移动索引,托管 GLB 快照重新解析。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-readhistory retains 60 states, truncates redo after edits and ignores duplicate snapshots · unithistory serializes async restores and leaves index unchanged when restoration fails · unitmanaged-GLB history downloads the snapshot stable reference before restoring, without rewriting it · unit
验收要求与证据范围属性/曲线修改撤销;连续点击不并发;下载失败索引与修改保留。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

unit:60 个快照上限、重复状态忽略及新编辑截断 redo 分支。

unit:异步互斥、下载失败不移动索引,解除锁后可继续操作。

unit:受控 GLB 快照先物化再恢复,不把临时 URL 写回快照。

重做 / Ctrl+Yproject.redomain.js:160 · class HistoryStack本轮修复撤销后重做;产生新编辑时清掉旧 redo 分支。与撤销共用异步恢复保护。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-readhistory retains 60 states, truncates redo after edits and ignores duplicate snapshots · unithistory serializes async restores and leaves index unchanged when restoration fails · unitmanaged-GLB history downloads the snapshot stable reference before restoring, without rewriting it · unit
验收要求与证据范围重做恢复相同字段及姿态,不回放无效分支。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

unit:60 个快照上限、重复状态忽略及新编辑截断 redo 分支。

unit:异步互斥、下载失败不移动索引,解除锁后可继续操作。

unit:受控 GLB 快照先物化再恢复,不把临时 URL 写回快照。

异常恢复副本project.recoverymain.js:2554 · reconcileApiRecovery本轮修复同版本恢复稿需用户确认;旧版本隔离/下载,不静默覆盖服务器。恢复稿重新解析受控资源;指纹忽略对象键顺序,避免已保存页面刷新误生恢复稿。已验证真实未保存稿同版本恢复并显式保存;旧版本恢复稿下载隔离保留 · e2e+apibeforeunload recovery does not create a draft for key order alone, but preserves real unsaved changes · unit
验收要求与证据范围同版本恢复可见;旧版本保留/下载;主动保存前不自动 PUT。

e2e+api:通过真实beforeunload创建未保存稿;同版恢复后仅显式保存落库;旧版只下载隔离,不自动覆盖服务端。丢弃/跨标签竞态另由既有恢复合同测试覆盖。

unit:同语义对象键顺序不产生假恢复稿;真正修改仍保留。不包含全部恢复/隔离/下载 UI 分支。

保存/发布版本冲突project.conflictmodel-content-api.js:1025 · queueSave已有实现409 阻断;恢复稿保留。发布封面队列同样遵守冲突保护。已验证保存请求超时和 409 保留恢复副本,解除故障后可重试 · e2e+apiAPI conflict leaves publication failed and releases the cover guard · unit
验收要求与证据范围定向模拟 409,无后续发布请求,不误报成功。

e2e+api:真实页面保存及真实服务端重试;失败由浏览器定向注入 timedout/409,非宣称自然发生的服务故障。

unit:409 分支不误报已发布并释放 busy 标记;模拟 API。

查看/编辑/新增/发布权限project.permissionslegacy-iframe-permissions.ts:32 · installLegacyModelPermissions本轮修复按 content.model.* 权限,不按 teacher/admin 身份推断写权限;只读仍能搜索、选择原生动画和键盘激活预览。新建接真实 create 能力;只发布可发布已保存版本;修复查看控件键盘事件误拦截。已验证管理员只读权限:编辑、新建、导入、保存发布均不可越权 · e2e+api
验收要求与证据范围只读查看操作可用且无内容写请求;不可编辑/保存;publisher 不上传/PUT,正常发布已有版本。

e2e+api:管理员只读UI、真实键盘搜索、原生片段切换与Enter播放/暂停、预览无内容写请求及创建403;纯publisher无需update的发布流程由独立权限合同回归补充。

离开/切角色未保存确认project.leaveLegacyModelEditorView.vue:147 · confirmLeave已有实现取消留在当前页;保存失败不得离开;上传/保存/发布忙时保留状态。新建也复用现有未保存确认。已验证补充环境与树:内置三环境保存重开、节点计数与折叠展开 · e2e+api未保存离开:关闭确认保留工作、保存失败留在原页、重试保存后离开 · e2e+api
验收要求与证据范围取消、放弃、保存失败与 busy 分别验证,没有误清 dirty。

e2e+api:真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。

资源库与场景树

功能 / 真实执行路径实现状态闭环与边界本轮对应修复测试证据 / 验收要求
资源库浏览 / 选择装备resource.catalogmain.js:1383 · renderAssets本轮修复切换装备可能应用其基准时间轴/蓝图;不是把第二个主装备追加进当前模型。修复单节点选择器误用导致启动报错;忙碌期间禁止切换资源。已验证补充资源库:搜索清空、真实卡片切换与刷新保留 · e2e+apiresource catalog startup renders all model/environment cards and binds every action with a real DOM · unit
验收要求与证据范围完整资源库 DOM 初始化、卡片事件、空目录启动正常;切换实际几何可见,保存后资源一致。

e2e+api:真实搜索与资源卡片点击、服务端保存、刷新后原模型网格;副本独立登记待清理。

unit:真实 DOM 渲染、卡片绑定、空目录及 busy 保护;不包含 GLB 网络加载。

资源搜索resource.searchmain.js:1467 · applyAssetSearch已有实现按资源名称/关键词过滤现有目录,不搜索服务器全站。无本轮专门改动。已验证补充资源库:搜索清空、真实卡片切换与刷新保留 · e2e+api
验收要求与证据范围关键字命中、空结果与清空恢复。

e2e+api:真实搜索与资源卡片点击、服务端保存、刷新后原模型网格;副本独立登记待清理。

内置车间 / 野外 / 装备车库environment.builtinmain.js:789 · resetEnvironmentPlaceholder已有实现车间/野外为程序化环境;车库为随包 GLB。保留真实 GLB 和占位环境来源区别。已验证补充环境与树:内置三环境保存重开、节点计数与折叠展开 · e2e+api
验收要求与证据范围三种切换与重新打开可重现相应环境。

e2e+api:真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。

导入外部车间 / 野外 GLBenvironment.externalmain.js:1028 · loadEnvironmentSceneFile本轮修复一个环境根;受控 MODEL_FILE 资产 metadata.usage=environment,与主装备独立。上传、最终保存、JSON 与刷新恢复稳定引用;失败阻断半提交。已验证受控模型与外部环境 GLB 上传、JSON 往返和刷新重开 · e2e+api
验收要求与证据范围小 GLB 上传后主装备不被替换;环境刷新仍可见且资产引用一致。

e2e+api:真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。

模型加载进度 / 失败 / 重试resource.loadingLegacyModelEditorView.vue:33 · installLegacyModelIframeLoadingScope已有实现局部加载状态;错误短提示,中性失败状态可重试。复用前轮加载范围与错误消息修复。已验证受控 GLB 下载失败阻止编辑,重试成功;发布预检开关无写入且拒绝错误资源 JSON · e2e+api
验收要求与证据范围资源失败不无限转圈;重试恢复;无凭据泄露。

e2e+api:真实受控下载定向503→中性失败与禁止编辑→解除故障后UI重试成功;预检面板开关不写项目,错误资源JSON在替换当前模型之前被拒绝。服务端发布时code/URI一致性拒绝由ContentProjectServiceTest合同回归覆盖,未把面板绿标代替服务端校验。

场景树节点与计数tree.browsemain.js:1925 · buildSceneTree现有边界树行上限 220;统计节点总数不等于当前展开行数。无本轮容量重构。已验证补充环境与树:内置三环境保存重开、节点计数与折叠展开 · e2e+api
验收要求与证据范围模型/环境节点可达,计数说明不当作全部节点已经逐项验收。

e2e+api:真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。

节点展开 / 折叠tree.expandmain.js:1925 · buildSceneTree已有实现当前界面浏览状态;无须持久化展开布局。无本轮专门改动。已验证补充环境与树:内置三环境保存重开、节点计数与折叠展开 · e2e+api
验收要求与证据范围展开和折叠子级不修改模型对象。

e2e+api:真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。

场景树搜索tree.searchmain.js:1925 · buildSceneTree已有实现搜索当前载入对象,不新增/删除对象。无本轮专门改动。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围搜索节点后选中正确对象,清空恢复树。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

显示 / 隐藏对象tree.visibilitymain.js:1925 · buildSceneTree本轮修复对象 visible 保存;隐藏祖先的子对象不接受射线拾取。拾取与高亮统一检查祖先可见性。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2evisible child under a hidden ancestor cannot be picked or highlighted; trigger aliases include ancestors · unit
验收要求与证据范围隐藏父级后不能点到子级,保存重开仍隐藏。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

unit:共用可见性和祖先别名函数;鼠标事件另由 E2E 验证。

整体 / 部件级选择selection.scopemain.js:1938 · setSelectionScope已有实现整体作用主装备根;部件作用实际节点。保留当前选择层级合同。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围点击可见 mesh,整体/部件切换选择目标不同。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

树 / 视口 / 属性选择联动selection.syncmain.js:1947 · selectObject本轮修复显示实际对象并更新辅助框;环境也是可编辑对象。关闭轮廓后重选不恢复辅助框。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围三处名称/对象 ID 一致,重选后轮廓状态保持。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

三维视图与操作

功能 / 真实执行路径实现状态闭环与边界本轮对应修复测试证据 / 验收要求
鼠标旋转 / 平移 / 缩放视角view.orbitmain.js:2 · OrbitControls已有实现当前三维相机;不是模型变换。无本轮专门改动。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围鼠标操作改变镜头但不改变对象局部坐标。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

聚焦选中 / Fview.focusmain.js:2272 · fitCamera已有实现按当前对象包围盒定位相机。无本轮专门改动。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围小模型/部件聚焦后仍可见,不被裁剪。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

移动工具 / Wview.movemain.js:2234 · setTransformMode本轮修复拖拽编辑对象局部位置,保存后重开一致。动画姿态下更新基值只扣实际偏移。已验证真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开 · e2e+runtime-readback
验收要求与证据范围移动后保存、刷新及动画再播放不漂移。
旋转工具 / Eview.rotatemain.js:2234 · setTransformMode本轮修复对象旋转;与当前时间轴旋转叠加需要一致。统一动画基值处理。已验证真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开 · e2e+runtime-readback
验收要求与证据范围旋转后保存/撤销/重做姿态一致。
缩放工具 / Rview.scalemain.js:2234 · setTransformMode已有实现对象缩放持久化;时间轴没有缩放轨道 UI。无新增缩放动画能力。已验证真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开 · e2e+runtime-readback
验收要求与证据范围对象缩放保存和重开正确。
网格开关view.gridmain.js:2283 · toggleGrid已有实现临时视图选项,不要求写入工程。非持久化不计保存缺陷。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围开关只影响辅助网格,不改变模型。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

PBR 光照开关view.pbrmain.js:2236 · handleView已有实现当前照明预览选项;不是批量材质编辑。非持久化不计保存缺陷。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围开关改变显示,物体材质字段不被覆盖。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

线框开关view.wireframemain.js:2236 · handleView已有实现当前视图选项;不承诺保存到工程。非持久化不计保存缺陷。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围线框开关恢复正常着色。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

选中轮廓开关view.outlinemain.js:2236 · handleView本轮修复当前会话偏好,进出预览后保持;不要求跨重载存储。重选时不重新泄露轮廓。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围关闭→重选→预览→退出,轮廓仍关闭。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

透视视角按钮view.perspectivemain.js:2236 · handleView现有边界重置透视观察角度;当前没有正交视角菜单。不把临时视图状态当作保存缺陷。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围点击后相机和聚焦目标正常。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

下载视口截图view.snapshotmain.js:3092 · downloadSnapshot已有实现当前 canvas PNG,可包含编辑辅助元素;保存封面是独立截图流程。无本轮专门改动。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围PNG 下载可打开,内容与当前视口对应。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

底部页签 / Tab / 面板高度view.panelsmain.js:2215 · switchBottomMode已有实现时间轴、蓝图、曲线、日志切换;拖分割条只调整当前工作区。曲线入口接真实数据。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围各页签显示正确内容,调整高度后画布正确 resize。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

进入 / 退出运行预览view.previewmain.js:2284 · togglePreview本轮修复真实当前三维场景;隐藏辅助工具,退出不丢编辑状态。进入显式派发 auto;退出停止运行并恢复轮廓策略。已验证已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发 · e2e场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围进入/退出与 Esc,三维仍可见,无辅助框泄漏。

e2e:执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

预览播放 / 前后导航view.preview-stepmain.js:2122 · bindShellEvents现有边界播放运行蓝图;前后按钮当前按时间轴 ±2 秒,不是按业务工序跳转。无新增业务工序导航。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围播放触发真实流程;前后跳时与时间轴一致。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

对象属性与扩展标记

功能 / 真实执行路径实现状态闭环与边界本轮对应修复测试证据 / 验收要求
对象名称property.namemain.js:1996 · bindInspectorEvents已有实现修改当前节点名,标识保持稳定。无本轮专门改动。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围树和属性同步,保存重开名称保留。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

对象标识 / 状态说明property.identitymain.js:1958 · renderInspector本轮修复标识只读;可见性含祖先状态,阴影取实际castShadow;编辑能力由宿主权限注入。不再固定显示“可见/投射阴影”,补宿主可编辑状态钩子。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+apiinspector state tags reflect ancestor visibility and actual shadow flags, with a host editability hook · unit
验收要求与证据范围标识不可编辑;选中对象、隐藏状态、阴影及只读权限标签一致。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

unit:真实属性 DOM 显示隐藏/阴影状态,并提供宿主权限标签 hook;不伪称此单测完成会话鉴权。

位置 XYZ 数值property.positionmain.js:1996 · bindInspectorEvents本轮修复局部坐标;动画当前帧下编辑也能生成正确基值。中性姿态与实际应用偏移统一。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围三个轴编辑、保存重开、动画回放一致。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

旋转 XYZ 数值property.rotationmain.js:1996 · bindInspectorEvents本轮修复输入角度转弧度存储,显示角度。多轨旋转基值不重复扣除。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围度数显示与存储/播放的弧度一致。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

缩放 XYZ 数值property.scalemain.js:1996 · bindInspectorEvents已有实现对象当前缩放,不添加新的缩放轨道。无本轮专门改动。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围非均匀缩放可保存和重开。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

材质基础色property.colormain.js:1996 · bindInspectorEvents现有边界只编辑当前对象第一个材质槽;共享材质可能影响共享该实例的网格。界面明确材质槽 1/N。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围颜色修改、保存重开对应当前槽;不宣称所有槽可编辑。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

材质金属度property.metalnessmain.js:1996 · bindInspectorEvents现有边界当前首槽 PBR 参数 0~1。标明材质槽范围。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围拖动参数有效,保存重开值一致。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

材质粗糙度property.roughnessmain.js:1996 · bindInspectorEvents现有边界当前首槽 PBR 参数 0~1。标明材质槽范围。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围参数与当前槽持久化一致。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

材质透明度property.opacitymain.js:1996 · bindInspectorEvents现有边界当前首槽;联动 transparent/depthWrite。保留实际透明度保存字段。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+api
验收要求与证据范围透明效果与重开结果一致。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

自定义资源编码property.codemain.js:1996 · bindInspectorEvents本轮修复对象 customProperties.resourceCode,不是服务器资产主键。输入真正回写、保存、恢复及撤销。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+apicustom properties and extension labels survive actual capture, project JSON and object restore · unit
验收要求与证据范围编辑、清空、重选、JSON 往返、刷新均一致。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

unit:真实对象 capture、normalization、JSON 与 restore 往返;清空和撤销到未存在字段状态。

自定义运行状态property.statusmain.js:1996 · bindInspectorEvents本轮修复NORMAL/SERVICE 标签,不会发起真实设备控制。真正写回对象,首次变更可撤销。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+apicustom properties and extension labels survive actual capture, project JSON and object restore · unit
验收要求与证据范围两状态切换及刷新保留。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

unit:真实对象 capture、normalization、JSON 与 restore 往返;清空和撤销到未存在字段状态。

流程绑定说明property.bindingmain.js:1958 · renderInspector现有边界只读显示模板/时间轴名称,不是教学任务的设备或物联网绑定。无本轮业务绑定改造。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围只读,不将其当作任务绑定操作验收。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

组件页内置能力说明component.inspectmain.js:1975 · renderComponents现有边界变换、场景节点、渲染等派生说明,没有通用组件启停系统。区分内置说明与自定义标记。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围显示与对象类型一致,不虚报可执行组件配置。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

添加扩展标记component.tagmain.js:1975 · renderComponents本轮澄清只保存名称标签;没有脚本、启停、运行时行为。从“添加组件”改为“扩展标记,仅保存标签”。已验证对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做 · e2e+apicustom properties and extension labels survive actual capture, project JSON and object restore · unit
验收要求与证据范围新增标签保存/重开;不得声称已启用新行为。

e2e+api:真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。

unit:真实对象 capture、normalization、JSON 与 restore 往返;清空和撤销到未存在字段状态。

时间轴与曲线

功能 / 真实执行路径实现状态闭环与边界本轮对应修复测试证据 / 验收要求
播放 / 暂停 / Spacetimeline.playtimeline.js:393 · play()已有实现播放当前编辑时间轴;原生 GLB 动画单独控制。停止清理与时间同步由本轮补强。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围播放进度/真实对象姿态变化,暂停停在当前帧。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

停止timeline.stoptimeline.js:429 · stop()已有实现停止并回到起始时间,按起始帧应用动画。无本轮语义扩展。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围按钮停止后不再自行前进。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

循环开关timeline.looptimeline.js:198 · #bindEvents本轮修复循环设置随 timeline 保存;不循环到达终点只完成一次。循环变更进入历史/dirty。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围开关、保存重开、到末尾/循环边界正确。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

上一 / 下一关键帧timeline.key-navtimeline.js:232 · #onClick已有实现按时间轴关键帧时间定位。无本轮专门改动。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围多个轨道时跳到正确相邻关键时刻。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

拖播放头 / 点击刻度timeline.seektimeline.js:263 · #onPointerDown本轮修复实时求值当前帧;键盘左右移动一帧,Shift 移动一秒。拖动补发 timechange,解绑指针监听。已验证补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移 · e2e+runtime-read
验收要求与证据范围时间码、曲线和视口姿态同步。

e2e+runtime-read:真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。

添加轨道timeline.add-tracktimeline.js:471 · addTrack(已有实现当前主装备节点的变换轨道。无新增轨道类型。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围增加后可绑定节点并保存。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

删除轨道timeline.remove-tracktimeline.js:495 · removeTrack(本轮修复删除后不再保留该轨道施加的旧姿态。宿主恢复此前作用的属性基值。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围删除即时复原,再保存/重开一致。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

修改轨道目标节点timeline.targettimeline.js:506 · updateSelectedTrack本轮修复用稳定对象标识绑定,不靠同名第一项。重绑清理旧目标的应用姿态。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-readchanging a binding resets the former target and keeps manual transform edits as base · unit
验收要求与证据范围从 A 改为 B,A 复原,B 播放。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

unit:轨道重绑后旧目标复位,手动变换成为新基值;不假称鼠标拖动已验。

选择位置 / 旋转属性timeline.propertytimeline.js:506 · updateSelectedTrack已有实现位置 XYZ 向量、旋转 X/Y/Z;没有缩放轨道 UI。保留当前实际能力。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围属性切换后关键帧类型和播放一致。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

轨道静音timeline.mutetimeline.js:232 · #onClick本轮修复仅忽略当前轨道,不关闭其他轨道。不再停留旧姿态或保存时重复扣值。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围静音/恢复后实际姿态与重开一致。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

记录当前姿态关键帧timeline.recordmain.js:2276 · addKeyframeFromSelection本轮修复记录相对对象基值的当前值。与多轨实际应用/保存采用同一基值。已验证补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移 · e2e+runtime-read
验收要求与证据范围操作对象后记录,回放命中相同姿态。

e2e+runtime-read:真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。

双击新增关键帧timeline.add-keytimeline.js:255 · #onDoubleClick已有实现在轨道对应时刻创建/更新关键帧。时间碰撞保留当前编辑帧。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围新增后节点可选,时间/数值可保存。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

删除关键帧 / Deletetimeline.delete-keytimeline.js:584 · deleteSelected已有实现删除选中的关键帧;输入框编辑时不截获删除键。删除和监听清理回归。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围删除后曲线和播放变化,撤销可恢复。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

关键帧拖动 / Alt 取消吸附timeline.drag-keytimeline.js:263 · #onPointerDown本轮修复默认 1/30 秒吸附;重叠时合并,保留正在编辑帧。拖动时间同步,碰撞处理确定。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围拖动/重叠/Alt 行为和保存后时间正确。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

时间轴缩放timeline.zoomtimeline.js:451 · setZoom已有实现显示缩放随 timeline 记录;不改变时长/关键帧数值。无本轮专门改动。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-read
验收要求与证据范围缩放不改变动作内容。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

多轨组合与保存基值timeline.multitrackmodel-editor-state.js:12 · applyEditorTimelinePose本轮修复不同属性组合;同对象同属性最后有效轨道生效。求值、手工编辑基值、序列化一致。已验证multiple tracks on one property use last effective value; capture subtracts it only once · unitchanging a binding resets the former target and keeps manual transform edits as base · unit
验收要求与证据范围两轨同属性、静音、删轨、重绑、重开无漂移。

unit:调用实际 apply/capture/update 基值方法;验证同属性最后有效轨道、静音/删除后中性姿态,偏移只扣一次。

unit:轨道重绑后旧目标复位,手动变换成为新基值;不假称鼠标拖动已验。

载入装备基准动作timeline.baselinemain.js:3058 · loadBaselineAnimation现有边界匹配部件后直接替换当前时间轴/蓝图;当前没有单独确认框,不是任意维修流程自动生成。保持当前装备限定;无匹配只提示,不改工程。已验证补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移 · e2e+runtime-read
验收要求与证据范围匹配时载入/保存/重开;无匹配时保留原数据并给出提示。

e2e+runtime-read:真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。

GLB 原生动画选择 / 播放timeline.nativemain.js:1715 · selectNativeAnimation已有实现真实 GLB 自带片段;保存选择索引,不是蓝图 clip 字段。无片段时禁用。已验证场景树搜索、可见性、选择、视口工具、原生动画与截图 · e2e
验收要求与证据范围狐狸实际动画播放/暂停,选择保存后重开一致。

e2e:树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。

曲线选择轨道 / 关键帧timeline.curvemodel-curve-editor.js:5 · class ModelCurveEditor本轮修复曲线与时间轴共用一份轨道和关键帧数据。移除固定 SVG 假数据。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-readcurve edits real scalar key values, time and interpolation, persisting through document reload · unit
验收要求与证据范围实际轨道名和点数匹配时间轴,点击同步当前帧。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

unit:真实曲线控件与 TimelineEditor,标量编辑与文档往返。

曲线编辑时间curve.timemodel-curve-editor.js:30 · change(input)本轮修复更新实际 key.time,约束在时间轴时长内。调用 timeline.updateKeyframe 并保持编辑目标。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-readcurve edits real scalar key values, time and interpolation, persisting through document reload · unit
验收要求与证据范围改时间后重排、撤销、JSON 往返一致。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

unit:真实曲线控件与 TimelineEditor,标量编辑与文档往返。

曲线编辑标量 / XYZ 数值curve.valuemodel-curve-editor.js:30 · change(input)本轮修复修改实际关键帧值,向量编辑保留其他分量。无效空值拒绝提交。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-readcurve edits real scalar key values, time and interpolation, persisting through document reload · unitcurve vector editing preserves other components and selected track; invalid values do not mutate · unit
验收要求与证据范围标量和向量都驱动真实姿态并保存。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

unit:真实曲线控件与 TimelineEditor,标量编辑与文档往返。

unit:向量其它分量保留,当前轨道、非法值保护;不包含显示轴切换或可视像素验收。

曲线插值curve.easingmodel-curve-editor.js:30 · change(input)本轮修复支持线性、平滑、缓入、缓出、阶梯;没有 Bezier 切线 UI。阶梯插值与已有下游时间轴求值一致。已验证时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致 · e2e+runtime-readcurve edits real scalar key values, time and interpolation, persisting through document reload · unit
验收要求与证据范围修改插值改变中间帧,重开保留。

e2e+runtime-read:真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。

unit:真实曲线控件与 TimelineEditor,标量编辑与文档往返。

曲线 XYZ 分量显示curve.axismodel-curve-editor.js:30 · change(input)已有实现改变图上显示分量,不改另外两轴的数据。当前实际向量轨道才出现。已验证补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移 · e2e+runtime-read
验收要求与证据范围切分量显示不同曲线,数值不被改写。

e2e+runtime-read:真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。

蓝图与交互反馈

功能 / 真实执行路径实现状态闭环与边界本轮对应修复测试证据 / 验收要求
节点库搜索 / 拖入节点blueprint.paletteblueprint.js:287 · _renderPalette已有实现当前七类节点:触发、时间轴、等待、高亮、提示、条件、完成。不新增任意脚本节点。已验证补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移 · e2e+runtime-read
验收要求与证据范围搜索、拖入后节点类型和参数可保存。

e2e+runtime-read:真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。

添加节点按钮blueprint.addblueprint.js:257 · _bind()已有实现工具栏默认添加提示节点;其他类型来自节点库。无本轮专门改动。已验证补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移 · e2e+runtime-read
验收要求与证据范围新增进入画布、历史与 JSON。

e2e+runtime-read:真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。

节点标题 / 参数编辑blueprint.parametersblueprint.js:431 · _renderInspector已有实现按节点类型提供数字、文字、枚举、复选框。保留数值边界与真实数据写回。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围改参数与标题后重开保留。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

节点拖动 / 多选框选blueprint.moveblueprint.js:566 · _onViewportPointerDown已有实现节点布局随蓝图 JSON 保存。无本轮专门改动。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围多选移动位置正确,撤销恢复。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

连接 / 替换 / 删除执行线blueprint.connectblueprint.js:767 · connect(本轮修复单输出重连替换;允许多个来源汇聚同一输入。不再清掉同一输入的其他合法来源。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围true/false 汇聚保留两条,替换输出只影响该线。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

删除节点 / 连线blueprint.deleteblueprint.js:786 · deleteSelection已有实现删除节点一并清理相关边。复制/剪切使用同一图数据合同。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围无悬空边;撤销恢复完整图。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

复制 / 剪切 / 粘贴blueprint.copyblueprint.js:714 · copySelection本轮修复Ctrl/Cmd+C/X/V;复制选区内部边,不复制指向外部的边。新节点 ID 和内部连线同步生成,阻止快捷键冒泡。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围副本内部连接正确,不误删外部对象。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

平移 / 缩放 / 适应画布blueprint.canvasblueprint.js:801 · frameAll已有实现蓝图画布浏览;不是三维相机。无本轮专门改动。已验证补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移 · e2e+runtime-read
验收要求与证据范围滚轮、Space 拖动和适应可见全部节点。

e2e+runtime-read:真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。

运行 / F5blueprint.runblueprint.js:871 · async run(本轮修复在当前模型编辑器驱动真实场景;有 200 步循环上限。条件与等待流程回归。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围执行高亮、动作、反馈;循环图可终止不挂死。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

停止运行blueprint.stopblueprint.js:997 · stop(本轮修复停止中断挂起等待,旧运行不覆盖新运行状态。AbortSignal 贯穿宿主动作与高亮清理。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围等待/动画中停止及时退出,无迟到完成提示。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

重置执行blueprint.resetblueprint.js:1014 · resetExecution已有实现复位执行状态、时间轴和高亮;保留蓝图编辑内容。高亮恢复原材质。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围运行后重置,进度归零且节点图不丢。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

点击触发目标blueprint.clickblueprint.js:933 · async trigger(本轮修复匹配事件和目标 ID/名称/祖先别名;空目标可通配。不再点击 B 触发 A。已验证已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发 · e2e
验收要求与证据范围两个不同目标分别点击,只运行匹配分支。

e2e:执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。

双击触发目标blueprint.double-clickmain.js:2256 · pickObject本轮修复双击独立派发 double-click,与单击区分。增加真实视口双击派发并抑制待发单击。已验证已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发 · e2e
验收要求与证据范围双击只触发目标的双击分支。

e2e:执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。

自动触发blueprint.automain.js:2284 · togglePreview本轮修复仅进入预览显式派发 auto,不把每次单击当 auto。事件匹配与进入预览联动。已验证已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发 · e2e
验收要求与证据范围进入预览触发一次,普通点击不重放自动分支。

e2e:执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。

播放时间轴节点blueprint.timelinemain.js:2024 · playTimelineForBlueprint本轮修复播放当前唯一时间轴,动作名称仅描述;支持起始时间、速度、等待。起始末帧不反跳零,终止可取消。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围起始时间和速度实际生效,等待与不等待分支正确。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

等待节点blueprint.waitblueprint.js:1 · NODE_TYPES已有实现指定毫秒等待;可被停止取消。异步停止及时结束。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围等待期间停止不会继续执行后续节点。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

高亮节点 / 呼吸效果blueprint.highlightmain.js:2052 · highlightPart本轮修复准确命中可见对象;同名歧义要求标识;没有可高亮材质则报错。拒绝错误整机回退,真实呼吸效果,保存忽略临时高亮。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-readvisible child under a hidden ancestor cannot be picked or highlighted; trigger aliases include ancestors · unithighlight rejects missing/hidden targets and transient colors never enter a saved project · unit
验收要求与证据范围目标正确、找不到报错、结束恢复、保存不永久染色。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

unit:共用可见性和祖先别名函数;鼠标事件另由 E2E 验证。

unit:目标不存在/隐藏报错,执行时暂态发光颜色不进入保存文档,结束恢复原材质。

操作提示节点blueprint.messagemain.js:2099 · showTeachingMessage现有边界显示标题/文本,可点击关闭或约 1.8/2.5 秒自动继续;不是强制人员确认。不将其当作业务审核/签名。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围文本出现、关闭和停止不留弹层。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

条件判断节点blueprint.conditionmain.js:2095 · evaluateBlueprintCondition本轮修复当前运行变量、嵌套字段、truthy/布尔/数字比较。false 没有出口不走 true,字符串布尔正确解析。已验证已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发 · runtimeboolean, numeric and nested blueprint conditions select their true/false branches · unit
验收要求与证据范围真假分支分别验证,缺出口不误执行。

unit:布尔、数字、truthy 与嵌套路径值解析。

流程完成节点blueprint.finishmain.js:1075 · initEditors已有实现本次预览结果/进度提示,不写教学任务成绩。保持本次运行反馈边界。已验证蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存 · e2e+runtime-read
验收要求与证据范围成功/完成提示与实际执行结果一致。

e2e+runtime-read:节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。

发布、交付与运行信息

功能 / 真实执行路径实现状态闭环与边界本轮对应修复测试证据 / 验收要求
生成 / 发布检查面板publish.preflightmain.js:2978 · togglePublishPanel现有边界本地主要检查资源引用存在及轨道/节点数量,不执行完整蓝图可达性、终止性或业务校验;服务端发布校验最终生效。发布目标与离线交付能力明确。已验证受控 GLB 下载失败阻止编辑,重试成功;发布预检开关无写入且拒绝错误资源 JSON · e2e+api
验收要求与证据范围可打开关闭;不完整工程不能凭界面绿标绕过服务端。

e2e+api:真实受控下载定向503→中性失败与禁止编辑→解除故障后UI重试成功;预检面板开关不写项目,错误资源JSON在替换当前模型之前被拒绝。服务端发布时code/URI一致性拒绝由ContentProjectServiceTest合同回归覆盖,未把面板绿标代替服务端校验。

发布到场景资源库publish.apimain.js:2897 · publishToSceneLibrary本轮修复先保存当前工程再发布固定版本;纯 publisher 发布已保存版本。整个保存/发布队列互斥,失败不发后续发布。已验证直接发布同步截图封面、固定版本目录与真实离线步骤 HTML · e2e+apiAPI publish supplies current cover, blocks duplicate publication and reports preserved old cover once · unitAPI conflict leaves publication failed and releases the cover guard · unit
验收要求与证据范围发布版本/模型资产/列表状态一致,保存失败不发布。

e2e+api:目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。

unit:实际发布宿主方法传封面、重入阻断和上传失败只提示一次;模拟 API,不是实际服务响应。

unit:409 分支不误报已发布并释放 busy 标记;模拟 API。

发布时当前三维截图封面publish.covermain.js:2897 · publishToSceneLibrary本轮修复JPEG 当前视口;截图或上传失败可保留旧封面并明确提示。与手动保存共用封面链路,409 阻断。已验证直接发布同步截图封面、固定版本目录与真实离线步骤 HTML · e2e+apiAPI publish supplies current cover, blocks duplicate publication and reports preserved old cover once · unitAPI conflict leaves publication failed and releases the cover guard · unit
验收要求与证据范围真实封面图对应当前模型;上传失败提示一次,旧图保留。

e2e+api:目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。

unit:实际发布宿主方法传封面、重入阻断和上传失败只提示一次;模拟 API,不是实际服务响应。

unit:409 分支不误报已发布并释放 busy 标记;模拟 API。

导出步骤演示 HTMLpublish.htmlmain.js:3011 · exportStandaloneHtml本轮澄清工程数据、静态截图和步骤演示;不包含 GLB、不执行真实三维交互。生成面板和按钮移除完整离线运行的误导。已验证直接发布同步截图封面、固定版本目录与真实离线步骤 HTML · e2e+api
验收要求与证据范围下载可打开、步骤导航可用,醒目标明不含三维模型。

e2e+api:目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。

下游读取模型固定版本publish.downstreammodel-content-api.js:1159 · publishDocument现有边界已发布模型作为资源引用;下游自动执行模型 timeline/blueprint 尚无统一运行桥接。本轮不将资源加载成功等同动作自动运行成功。已验证直接发布同步截图封面、固定版本目录与真实离线步骤 HTML · e2e+api
验收要求与证据范围固定引用/可见性单独测;自动动作执行列为待确认范围。

e2e+api:目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。

运行日志feedback.logmain.js:3116 · log(message已有实现本次内存运行日志,最多 160 条;不是服务端审计日志。错误信息转义,保留清晰运行结果。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围选中/运行/保存产生相应日志,不要求刷新保留。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

节点 / 三角面 / FPS 等状态feedback.statsmain.js:3094 · updateStats现有边界节点与三角面统计、当前 FPS;内存为估算值,不是精确浏览器内存剖析。无本轮性能仪表重构。已验证补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存 · e2e+api
验收要求与证据范围载入模型后统计有合理变化,状态不作为性能验收数据。

e2e+api:真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。

成功 / 错误短消息feedback.toastmain.js:3117 · toast(message已有实现瞬时反馈;保存/发布成功以接口完成为准。封面失败不伪报全部成功。已验证保存请求超时和 409 保留恢复副本,解除故障后可重试 · e2e+apiAPI publish supplies current cover, blocks duplicate publication and reports preserved old cover once · unit
验收要求与证据范围失败出现一次明确提示,自动消失,不形成重复常驻错误条。

e2e+api:真实页面保存及真实服务端重试;失败由浏览器定向注入 timedout/409,非宣称自然发生的服务故障。

unit:实际发布宿主方法传封面、重入阻断和上传失败只提示一次;模拟 API,不是实际服务响应。

执行结果与证据分层

浏览器 E2E

19/19 项通过 · 已验证 · Playwright 原始结果

用例结果耗时 / 失败原因
受控模型与外部环境 GLB 上传、JSON 往返和刷新重开已验证14.6 秒
非法 JSON 与未登记 blob 引用导入失败时保留当前文档已验证6.0 秒
保存请求超时和 409 保留恢复副本,解除故障后可重试已验证9.7 秒
直接发布同步截图封面、固定版本目录与真实离线步骤 HTML已验证7.9 秒
时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致已验证11.8 秒
补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移已验证20.1 秒
蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存已验证14.1 秒
已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发已验证13.1 秒
对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做已验证8.4 秒
场景树搜索、可见性、选择、视口工具、原生动画与截图已验证15.4 秒
新建工程取消及失败保留输入;成功创建新ID且不覆盖当前模型已验证10.5 秒
管理员只读权限:编辑、新建、导入、保存发布均不可越权已验证4.7 秒
真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开已验证9.4 秒
未保存离开:关闭确认保留工作、保存失败留在原页、重试保存后离开已验证9.5 秒
补充资源库:搜索清空、真实卡片切换与刷新保留已验证8.5 秒
补充环境与树:内置三环境保存重开、节点计数与折叠展开已验证22.8 秒
补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存已验证34.9 秒
真实未保存稿同版本恢复并显式保存;旧版本恢复稿下载隔离保留已验证12.5 秒
受控 GLB 下载失败阻止编辑,重试成功;发布预检开关无写入且拒绝错误资源 JSON已验证9.8 秒

通过浏览器定向注入的 409/503 等异常仅验证失败处理,不代表开发服务真实发生该故障。UI 点击、脚本调用运行时和纯 API 检查按各用例证据说明区分。

保留首轮失败与定向重跑事实

仅保留安全的次数、名称、时间及源文件哈希;不复制请求正文、会话、错误上下文或私有运行日志。历史轮次不替代当前 results.json,定向2/2不等同全量19/19。 安全迭代记录

历史轮次原始结果解释范围
全量首轮17/19 通过 · 该轮失败两例均已完成保存并返回列表,末尾测试辅助GET项目详情返回503;分别定位 interaction:114 与 navigation:142。
首次鉴权诊断定向重跑0/2 通过 · 该轮失败此轮才在登录阶段超时,业务操作未开始;不与首轮末尾GET详情503混为同一发生阶段。
后续定向重跑2/2 通过 · 该轮通过只证明这两条在此次运行通过,不能把首轮17/19改写为19/19。
修复与曲线配色更新后的最终完整轮19/19 通过 · 该轮通过保留原始失败,不以随后同名证据覆盖。

已修复 Auth/Tran 数据库连接健康检查可能无界阻塞的问题,并完成实际 Kingbase 驱动隔离黑洞探针(约2010ms释放关闭);最终双模块构建和完整E2E另行记录。该探针不能证明历史每一次503均已逐条与该原因建立因果关联。

完成修复后另跑完整19条,使用新的完整results.json、对应时段证据/附件、最终validation及artifact检查;旧失败保留在本历史记录。

真实 API / 运行时断言

逐份读取 evidence/*.json。文件存在本身不代表通过;每份证据须注明断言结果和覆盖的 featureIds。

evidence/30-assets-roundtrip.json 已验证
{
  "time": "2026-09-06T08:20:27.495Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开",
    "featureIds": [
      "project.import-glb",
      "environment.external",
      "project.import-json",
      "project.export-json",
      "project.save"
    ],
    "projectId": "226",
    "mainCode": "MODEL_MTPJKFPW_09-FOX",
    "environmentCode": "ENV_MODEL_MTPJKH7H_01-BOX",
    "mainUri": "fsvc://unreal-tran/model/2026/09/06/b85bd2722c4e133e61469c9f413380f0.glb",
    "environmentUri": "fsvc://unreal-tran/model/2026/09/06/9bd7351f3ee917431354690f44a449b5.glb",
    "modelMeshes": 1,
    "environmentMeshes": 1,
    "nativeClips": 3,
    "environmentPosition": [
      6,
      0,
      -7
    ],
    "environmentScale": [
      0.15,
      0.15,
      0.15
    ],
    "result": "PASS",
    "scope": "真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。"
  }
}
evidence/31-invalid-import.json 已验证
{
  "time": "2026-09-06T08:20:33.535Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "非法 JSON 与未登记 blob 引用导入失败时保留当前文档",
    "featureIds": [
      "project.import-json"
    ],
    "projectId": "227",
    "result": "PASS",
    "cases": [
      "JSON语法错误",
      "无受控资源编号/地址的跨页面blob引用"
    ],
    "retainedVersion": 1
  }
}
evidence/32-save-failure-recovery.json 已验证
{
  "time": "2026-09-06T08:20:43.214Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "保存请求超时和 409 保留恢复副本,解除故障后可重试",
    "featureIds": [
      "project.conflict",
      "project.save",
      "feedback.toast"
    ],
    "projectId": "228",
    "result": "PASS",
    "intercepted": [
      {
        "mode": "timedout",
        "method": "PUT",
        "projectId": "228"
      },
      {
        "mode": "409",
        "method": "PUT",
        "projectId": "228"
      }
    ],
    "scope": "真实页面保存及真实服务端重试;失败由浏览器定向注入 timedout/409,非宣称自然发生的服务故障。"
  }
}
evidence/34-publish-fixed-version.json 已验证
{
  "time": "2026-09-06T08:20:51.047Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML",
    "featureIds": [
      "publish.api",
      "publish.cover",
      "publish.html",
      "publish.downstream"
    ],
    "projectId": "229",
    "result": "PASS",
    "versionId": "283",
    "coverUri": "fsvc://unreal-tran/image/2026/09/06/14ab7b4e1e7a9d21f13f3504cb8b89ae.jpg",
    "priorCoverUri": "fsvc://unreal-tran/image/2026/09/05/c09a49efd4423dcf8ccbc46b46d1e209.jpg",
    "mainCode": "MODEL_MTO6V5W6_09-FOX",
    "scope": "目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。"
  }
}
evidence/41-resource-search-switch.json 已验证
{
  "time": "2026-09-06T08:22:56.960Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "补充资源库:搜索清空、真实卡片切换与刷新保留",
    "featureIds": [
      "resource.search",
      "resource.catalog"
    ],
    "projectId": "235",
    "original": {
      "id": "imported-model_mto6v5w6_09-fox",
      "name": "09-Fox",
      "code": "MODEL_MTO6V5W6_09-FOX"
    },
    "boxId": "imported-model_mtpjnqqk_01-box",
    "restored": {
      "id": "imported-model_mto6v5w6_09-fox",
      "code": "MODEL_MTO6V5W6_09-FOX",
      "meshes": 1
    },
    "scope": "真实搜索与资源卡片点击、服务端保存、刷新后原模型网格;副本独立登记待清理。"
  }
}
evidence/43-builtin-environments-tree.json 已验证
{
  "time": "2026-09-06T08:23:19.759Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "补充环境与树:内置三环境保存重开、节点计数与折叠展开",
    "featureIds": [
      "environment.builtin",
      "tree.browse",
      "tree.expand",
      "project.leave"
    ],
    "projectId": "236",
    "snapshots": [
      {
        "type": "workshop",
        "mode": "placeholder",
        "sourceKind": "placeholder",
        "nodes": 48,
        "meshes": 45,
        "id": "ee302132-152c-4575-a2ec-0a56b4820079",
        "shownRows": 76,
        "totalNodes": 76
      },
      {
        "type": "outdoor",
        "mode": "placeholder",
        "sourceKind": "placeholder",
        "nodes": 70,
        "meshes": 65,
        "id": "bf88db35-635d-4012-8df6-dd41382e624f",
        "shownRows": 98,
        "totalNodes": 98
      },
      {
        "type": "depot",
        "mode": "placeholder",
        "sourceKind": "builtin-gltf",
        "nodes": 41,
        "meshes": 32,
        "id": "cebab0bb-6910-4dff-b846-9978adcb3450",
        "shownRows": 69,
        "totalNodes": 69
      }
    ],
    "depotSavedLeave": {
      "promptedForUnsavedChanges": false,
      "version": 5
    },
    "scope": "真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。"
  }
}
evidence/46-navigation-panels-autosave.json 已验证
{
  "time": "2026-09-06T08:23:54.656Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存",
    "featureIds": [
      "project.name",
      "project.autosave",
      "view.orbit",
      "view.focus",
      "view.panels",
      "view.preview-step",
      "feedback.log",
      "feedback.stats",
      "property.identity",
      "property.binding",
      "selection.scope",
      "component.inspect"
    ],
    "projectId": "237",
    "focused": {
      "position": [
        11.153569378931522,
        8.526809324389628,
        13.922975975299929
      ],
      "target": [
        0.01518877997232826,
        2.4006999949620704,
        2.266009371254541e-7
      ],
      "distance": 18.853169271563605
    },
    "rotated": {
      "position": [
        1.7511663652214757,
        6.181727848275404,
        18.38837160033089
      ],
      "target": [
        0.01518877997232826,
        2.4006999949620704,
        2.266009371254541e-7
      ],
      "distance": 18.853169271563594
    },
    "panned": {
      "position": [
        -5.273341581654527,
        5.082450786888863,
        17.990688935160133
      ],
      "target": [
        -0.5866856170824521,
        2.73698783970429,
        -0.11942014831462
      ],
      "distance": 18.85316927156359
    },
    "focusTargetScreen": [
      -5.177525136316571e-19,
      -1.227265217497261e-16,
      0.9980999049952497
    ],
    "panelModes": [
      "timeline",
      "blueprint",
      "curve",
      "console"
    ],
    "logCount": 8,
    "stats": {
      "objects": "98 节点 / 66 网格",
      "triangles": "1,438",
      "fps": "54"
    },
    "scope": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。"
  }
}
evidence/51-gizmos.json 已验证
{
  "time": "2026-09-06T08:22:38.815Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+runtime-readback",
    "testTitle": "真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开",
    "featureIds": [
      "view.move",
      "view.rotate",
      "view.scale"
    ],
    "projectId": "233",
    "changes": [
      {
        "mode": "translate",
        "before": [
          0,
          0,
          0
        ],
        "after": [
          0.7257266613731312,
          0,
          0
        ]
      },
      {
        "mode": "rotate",
        "before": [
          0,
          0,
          0
        ],
        "after": [
          -1.194547194225429,
          0,
          0
        ]
      },
      {
        "mode": "scale",
        "before": [
          1,
          1,
          1
        ],
        "after": [
          0.30167729043770014,
          1,
          1
        ]
      }
    ],
    "reopened": {
      "position": [
        0.7257266613731312,
        0,
        0
      ],
      "rotation": [
        -1.194547194225429,
        0,
        0
      ],
      "scale": [
        0.30167729043770014,
        1,
        1
      ]
    }
  }
}
evidence/52-leave.json 已验证
{
  "time": "2026-09-06T08:22:48.155Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "未保存离开:关闭确认保留工作、保存失败留在原页、重试保存后离开",
    "featureIds": [
      "project.leave"
    ],
    "projectId": "234",
    "closeKeptDraft": true,
    "failureKeptDraft": true,
    "retriedSavePersisted": true
  }
}
evidence/60-project-recovery.json 已验证
{
  "time": "2026-09-06T08:24:07.184Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "真实未保存稿同版本恢复并显式保存;旧版本恢复稿下载隔离保留",
    "featureIds": [
      "project.recovery"
    ],
    "projectId": "238",
    "sameVersion": 1,
    "explicitSaveVersion": 2,
    "concurrentVersion": 3,
    "retainedFilename": "recovery-238-isolated.json",
    "scope": "通过真实beforeunload创建未保存稿;同版恢复后仅显式保存落库;旧版只下载隔离,不自动覆盖服务端。丢弃/跨标签竞态另由既有恢复合同测试覆盖。"
  }
}
evidence/63-loading-publish-preflight.json 已验证
{
  "time": "2026-09-06T08:24:17.025Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "受控 GLB 下载失败阻止编辑,重试成功;发布预检开关无写入且拒绝错误资源 JSON",
    "featureIds": [
      "resource.loading",
      "publish.preflight"
    ],
    "projectId": "239",
    "failedDownloads": 1,
    "retainedVersion": 1,
    "scope": "真实受控下载定向503→中性失败与禁止编辑→解除故障后UI重试成功;预检面板开关不写项目,错误资源JSON在替换当前模型之前被拒绝。服务端发布时code/URI一致性拒绝由ContentProjectServiceTest合同回归覆盖,未把面板绿标代替服务端校验。"
  }
}
evidence/new-project.json 已验证
{
  "time": "2026-09-06T08:22:24.723Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "新建工程取消及失败保留输入;成功创建新ID且不覆盖当前模型",
    "featureIds": [
      "project.new"
    ],
    "oldId": "168",
    "newId": "232",
    "cancelPreserved": true,
    "failureRetainedName": true,
    "originalVersion": 17,
    "sourceUnchanged": true
  }
}
evidence/permissions.json 已验证
{
  "time": "2026-09-06T08:22:29.489Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "管理员只读权限:编辑、新建、导入、保存发布均不可越权",
    "featureIds": [
      "project.permissions"
    ],
    "scope": "管理员只读UI、真实键盘搜索、原生片段切换与Enter播放/暂停、预览无内容写请求及创建403;纯publisher无需update的发布流程由独立权限合同回归补充。",
    "readonlyUi": true,
    "readonlySearch": true,
    "nativeClipChanged": true,
    "keyboardPreview": true,
    "previewWriteRequests": 0,
    "createStatus": 403
  }
}
evidence/properties.json 已验证
{
  "time": "2026-09-06T08:21:58.738Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+api",
    "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做",
    "featureIds": [
      "property.name",
      "property.position",
      "property.rotation",
      "property.scale",
      "property.color",
      "property.metalness",
      "property.roughness",
      "property.opacity",
      "property.code",
      "property.status",
      "component.tag",
      "project.undo",
      "project.redo",
      "project.save"
    ],
    "scope": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。",
    "state": {
      "id": "model-imported-model_mto6v5w6_09-fox-26",
      "position": [
        1.25,
        0,
        0
      ],
      "rotation": [
        0,
        0.2617993877991494,
        0
      ],
      "scale": [
        1,
        1,
        1.1
      ],
      "customProperties": {
        "resourceCode": "CLOSURE-FOX-001",
        "operatingStatus": "SERVICE"
      },
      "customComponents": [
        "扩展标记 1",
        "扩展标记 2",
        "扩展标记 3",
        "扩展标记 4",
        "扩展标记 5"
      ]
    },
    "serverVersion": 15,
    "persisted": true
  }
}
evidence/truck-blueprint-events.json 已验证
{
  "time": "2026-09-06T08:21:50.227Z",
  "data": {
    "status": "PASS",
    "kind": "e2e",
    "testTitle": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发",
    "featureIds": [
      "blueprint.click",
      "blueprint.double-click",
      "blueprint.auto",
      "view.preview"
    ],
    "projectId": "169",
    "runtimeSupplement": {
      "status": "PASS",
      "kind": "runtime",
      "featureIds": [
        "blueprint.condition"
      ],
      "branches": {
        "falseBranch": {
          "result": true,
          "visitedFinish": false,
          "logs": [
            {
              "time": "16:21:41",
              "message": "开始执行 · 闭环-条件判断",
              "level": "run",
              "nodeId": "node-mtpjlz9t-p124m"
            },
            {
              "time": "16:21:41",
              "message": "执行节点:闭环-条件判断",
              "level": "run",
              "nodeId": "node-mtpjlz9t-p124m"
            },
            {
              "time": "16:21:41",
              "message": "条件结果:否 / FALSE",
              "level": "warning",
              "nodeId": "node-mtpjlz9t-p124m"
            },
            {
              "time": "16:21:41",
              "message": "节点“闭环-条件判断”没有后续连线,流程结束",
              "level": "warning",
              "nodeId": "node-mtpjlz9t-p124m"
            },
            {
              "time": "16:21:41",
              "message": "蓝图执行完成 · 1 个节点",
              "level": "success",
              "nodeId": null
            }
          ]
        },
        "trueBranch": {
          "result": true,
          "visitedFinish": true,
          "logs": [
            {
              "time": "16:21:41",
              "message": "开始执行 · 闭环-条件判断",
              "level": "run",
              "nodeId": "node-mtpjlz9t-p124m"
            },
            {
              "time": "16:21:41",
              "message": "执行节点:闭环-条件判断",
              "level": "run",
              "nodeId": "node-mtpjlz9t-p124m"
            },
            {
              "time": "16:21:41",
              "message": "条件结果:是 / TRUE",
              "level": "success",
              "nodeId": "node-mtpjlz9t-p124m"
            },
            {
              "time": "16:21:41",
              "message": "执行节点:闭环-流程完成",
              "level": "run",
              "nodeId": "node-mtpjlznf-cxlzk"
            },
            {
              "time": "16:21:41",
              "message": "蓝图执行完成 · 2 个节点",
              "level": "success",
              "nodeId": null
            }
          ]
        }
      },
      "mismatch": {
        "result": false,
        "started": false
      }
    },
    "browserEvents": [
      {
        "event": "click",
        "starts": 1,
        "finished": true,
        "source": "真实 renderer canvas 指针事件",
        "logs": [
          {
            "time": "16:21:42",
            "message": "运行状态已重置",
            "level": "system",
            "nodeId": null
          },
          {
            "time": "16:21:42",
            "message": "开始执行 · 闭环-点击触发",
            "level": "run",
            "nodeId": "node-mtpjlx7c-sgcyx"
          },
          {
            "time": "16:21:42",
            "message": "执行节点:闭环-点击触发",
            "level": "run",
            "nodeId": "node-mtpjlx7c-sgcyx"
          },
          {
            "time": "16:21:42",
            "message": "执行节点:闭环-播放时间轴",
            "level": "run",
            "nodeId": "node-mtpjlxj9-y3g3v"
          },
          {
            "time": "16:21:42",
            "message": "执行节点:闭环-等待",
            "level": "run",
            "nodeId": "node-mtpjlxxe-ond97"
          },
          {
            "time": "16:21:42",
            "message": "执行节点:闭环-高亮部件",
            "level": "run",
            "nodeId": "node-mtpjlyci-4c5oi"
          },
          {
            "time": "16:21:42",
            "message": "执行节点:闭环-操作提示",
            "level": "run",
            "nodeId": "node-mtpjlyuw-164xj"
          },
          {
            "time": "16:21:43",
            "message": "执行节点:闭环-条件判断",
            "level": "run",
            "nodeId": "node-mtpjlz9t-p124m"
          },
          {
            "time": "16:21:43",
            "message": "条件结果:是 / TRUE",
            "level": "success",
            "nodeId": "node-mtpjlz9t-p124m"
          },
          {
            "time": "16:21:43",
            "message": "执行节点:闭环-流程完成",
            "level": "run",
            "nodeId": "node-mtpjlznf-cxlzk"
          },
          {
            "time": "16:21:43",
            "message": "蓝图执行完成 · 7 个节点",
            "level": "success",
            "nodeId": null
          }
        ]
      },
      {
        "event": "double-click",
        "starts": 1,
        "finished": true,
        "source": "真实 renderer canvas 指针事件",
        "logs": [
          {
            "time": "16:21:43",
            "message": "运行状态已重置",
            "level": "system",
            "nodeId": null
          },
          {
            "time": "16:21:44",
            "message": "开始执行 · 闭环-点击触发",
            "level": "run",
            "nodeId": "node-mtpjlx7c-sgcyx"
          },
          {
            "time": "16:21:44",
            "message": "执行节点:闭环-点击触发",
            "level": "run",
            "nodeId": "node-mtpjlx7c-sgcyx"
          },
          {
            "time": "16:21:44",
            "message": "执行节点:闭环-播放时间轴",
            "level": "run",
            "nodeId": "node-mtpjlxj9-y3g3v"
          },
          {
            "time": "16:21:44",
            "message": "执行节点:闭环-等待",
            "level": "run",
            "nodeId": "node-mtpjlxxe-ond97"
          },
          {
            "time": "16:21:44",
            "message": "执行节点:闭环-高亮部件",
            "level": "run",
            "nodeId": "node-mtpjlyci-4c5oi"
          },
          {
            "time": "16:21:44",
            "message": "执行节点:闭环-操作提示",
            "level": "run",
            "nodeId": "node-mtpjlyuw-164xj"
          },
          {
            "time": "16:21:45",
            "message": "执行节点:闭环-条件判断",
            "level": "run",
            "nodeId": "node-mtpjlz9t-p124m"
          },
          {
            "time": "16:21:45",
            "message": "条件结果:是 / TRUE",
            "level": "success",
            "nodeId": "node-mtpjlz9t-p124m"
          },
          {
            "time": "16:21:45",
            "message": "执行节点:闭环-流程完成",
            "level": "run",
            "nodeId": "node-mtpjlznf-cxlzk"
          },
          {
            "time": "16:21:45",
            "message": "蓝图执行完成 · 7 个节点",
            "level": "success",
            "nodeId": null
          }
        ]
      },
      {
        "event": "auto",
        "starts": 1,
        "finished": true,
        "source": "真实点击进入预览",
        "logs": [
          {
            "time": "16:21:45",
            "message": "运行状态已重置",
            "level": "system",
            "nodeId": null
          },
          {
            "time": "16:21:45",
            "message": "开始执行 · 闭环-点击触发",
            "level": "run",
            "nodeId": "node-mtpjlx7c-sgcyx"
          },
          {
            "time": "16:21:45",
            "message": "执行节点:闭环-点击触发",
            "level": "run",
            "nodeId": "node-mtpjlx7c-sgcyx"
          },
          {
            "time": "16:21:45",
            "message": "执行节点:闭环-播放时间轴",
            "level": "run",
            "nodeId": "node-mtpjlxj9-y3g3v"
          },
          {
            "time": "16:21:45",
            "message": "执行节点:闭环-等待",
            "level": "run",
            "nodeId": "node-mtpjlxxe-ond97"
          },
          {
            "time": "16:21:45",
            "message": "执行节点:闭环-高亮部件",
            "level": "run",
            "nodeId": "node-mtpjlyci-4c5oi"
          },
          {
            "time": "16:21:46",
            "message": "执行节点:闭环-操作提示",
            "level": "run",
            "nodeId": "node-mtpjlyuw-164xj"
          },
          {
            "time": "16:21:46",
            "message": "执行节点:闭环-条件判断",
            "level": "run",
            "nodeId": "node-mtpjlz9t-p124m"
          },
          {
            "time": "16:21:46",
            "message": "条件结果:是 / TRUE",
            "level": "success",
            "nodeId": "node-mtpjlz9t-p124m"
          },
          {
            "time": "16:21:46",
            "message": "执行节点:闭环-流程完成",
            "level": "run",
            "nodeId": "node-mtpjlznf-cxlzk"
          },
          {
            "time": "16:21:46",
            "message": "蓝图执行完成 · 7 个节点",
            "level": "success",
            "nodeId": null
          }
        ]
      }
    ],
    "persistedNodeCount": 7,
    "persistedEdgeCount": 6,
    "scope": "执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。"
  }
}
evidence/truck-blueprint-tool-supplement.json 已验证
{
  "time": "2026-09-06T08:21:23.003Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+runtime-read",
    "testTitle": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移",
    "featureIds": [
      "timeline.seek",
      "timeline.record",
      "timeline.baseline",
      "curve.axis",
      "blueprint.palette",
      "blueprint.add",
      "blueprint.canvas"
    ],
    "projectIds": [
      "230",
      "231"
    ],
    "pose": {
      "time": 0.9666666666666666,
      "value": [
        2.4166666666666665,
        -0.9666666666666666,
        0
      ],
      "actual": [
        2.4166666666666665,
        -0.9666666666666666,
        0
      ],
      "base": [
        0,
        0,
        0
      ]
    },
    "recorded": {
      "id": "key-u60x1lh",
      "time": 0.9666666666666666,
      "value": [
        2.4166666666666665,
        -0.9666666666666666,
        0
      ],
      "easing": "easeInOut"
    },
    "curveAxis": {
      "xAndYPathsDiffer": true,
      "values": [
        5,
        -2,
        0
      ]
    },
    "noMatchPreserved": true,
    "baseline": {
      "name": "闭环工具补充-基准匹配-mtpjlmiy",
      "timeline": {
        "version": 1,
        "duration": 12,
        "currentTime": 0,
        "loop": true,
        "zoom": 1,
        "tracks": [
          {
            "id": "bridge-front-outrigger-drop",
            "name": "前支腿下落支撑",
            "targetId": "bridge-front-outrigger",
            "property": "position",
            "color": "#ffd166",
            "muted": false,
            "keyframes": [
              {
                "id": "bfo-k0",
                "time": 0,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bfo-k1",
                "time": 2.4,
                "value": [
                  0,
                  -0.45,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bfo-k2",
                "time": 10,
                "value": [
                  0,
                  -0.45,
                  0
                ],
                "easing": "linear"
              },
              {
                "id": "bfo-k3",
                "time": 12,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-rear-outrigger-drop",
            "name": "后支腿下落支撑",
            "targetId": "bridge-rear-outrigger",
            "property": "position",
            "color": "#f4a261",
            "muted": false,
            "keyframes": [
              {
                "id": "bro-k0",
                "time": 0,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bro-k1",
                "time": 2.2,
                "value": [
                  0,
                  -0.19,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bro-k2",
                "time": 10,
                "value": [
                  0,
                  -0.19,
                  0
                ],
                "easing": "linear"
              },
              {
                "id": "bro-k3",
                "time": 12,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-rear-support-drop",
            "name": "后支座下落支撑",
            "targetId": "bridge-rear-support",
            "property": "position",
            "color": "#e76f51",
            "muted": false,
            "keyframes": [
              {
                "id": "brs-k0",
                "time": 0,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "brs-k1",
                "time": 0.4,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "brs-k2",
                "time": 2.6,
                "value": [
                  0,
                  -0.25,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "brs-k3",
                "time": 10.2,
                "value": [
                  0,
                  -0.25,
                  0
                ],
                "easing": "linear"
              },
              {
                "id": "brs-k4",
                "time": 12,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-gantry-slew",
            "name": "回转门架回转",
            "targetId": "bridge-gantry",
            "property": "rotation.y",
            "color": "#2a9d8f",
            "muted": false,
            "keyframes": [
              {
                "id": "bgs-k0",
                "time": 0,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bgs-k1",
                "time": 2.2,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bgs-k2",
                "time": 4.8,
                "value": 0.24,
                "easing": "easeInOut"
              },
              {
                "id": "bgs-k3",
                "time": 8.6,
                "value": 0.24,
                "easing": "linear"
              },
              {
                "id": "bgs-k4",
                "time": 11.4,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bgs-k5",
                "time": 12,
                "value": 0,
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-gantry-upper-luffing",
            "name": "门架上臂变幅",
            "targetId": "bridge-gantry-upper",
            "property": "rotation.x",
            "color": "#8a68e8",
            "muted": false,
            "keyframes": [
              {
                "id": "bgu-k0",
                "time": 0,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bgu-k1",
                "time": 3,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bgu-k2",
                "time": 5.4,
                "value": -0.22,
                "easing": "easeInOut"
              },
              {
                "id": "bgu-k3",
                "time": 8.8,
                "value": -0.22,
                "easing": "linear"
              },
              {
                "id": "bgu-k4",
                "time": 11.2,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bgu-k5",
                "time": 12,
                "value": 0,
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-erection-frame-luffing",
            "name": "后架设架起落(抬头 → 落桥)",
            "targetId": "bridge-erection-frame",
            "property": "rotation.x",
            "color": "#4cc9f0",
            "muted": false,
            "keyframes": [
              {
                "id": "bef-k0",
                "time": 0,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bef-k1",
                "time": 3.6,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bef-k2",
                "time": 5,
                "value": -0.07,
                "easing": "easeInOut"
              },
              {
                "id": "bef-k3",
                "time": 7.8,
                "value": 0.2,
                "easing": "easeInOut"
              },
              {
                "id": "bef-k4",
                "time": 9.8,
                "value": 0.2,
                "easing": "linear"
              },
              {
                "id": "bef-k5",
                "time": 11.6,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bef-k6",
                "time": 12,
                "value": 0,
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-luffing-cylinder-follow",
            "name": "变幅缸随动",
            "targetId": "bridge-luffing-cylinder",
            "property": "rotation.x",
            "color": "#90be6d",
            "muted": false,
            "keyframes": [
              {
                "id": "blc-k0",
                "time": 0,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "blc-k1",
                "time": 3.6,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "blc-k2",
                "time": 5,
                "value": -0.03,
                "easing": "easeInOut"
              },
              {
                "id": "blc-k3",
                "time": 7.8,
                "value": 0.09,
                "easing": "easeInOut"
              },
              {
                "id": "blc-k4",
                "time": 9.8,
                "value": 0.09,
                "easing": "linear"
              },
              {
                "id": "blc-k5",
                "time": 11.6,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "blc-k6",
                "time": 12,
                "value": 0,
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-carriage-travel",
            "name": "移动架沿滑道后行",
            "targetId": "bridge-carriage",
            "property": "position",
            "color": "#32b8e6",
            "muted": false,
            "keyframes": [
              {
                "id": "bct-k0",
                "time": 0,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bct-k1",
                "time": 5.4,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bct-k2",
                "time": 8.4,
                "value": [
                  0,
                  0,
                  -0.42
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bct-k3",
                "time": 10,
                "value": [
                  0,
                  0,
                  -0.42
                ],
                "easing": "linear"
              },
              {
                "id": "bct-k4",
                "time": 12,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-launch-pinion-drive",
            "name": "导梁销齿轮驱动",
            "targetId": "bridge-launch-pinion",
            "property": "rotation.x",
            "color": "#f9c74f",
            "muted": false,
            "keyframes": [
              {
                "id": "blp-k0",
                "time": 0,
                "value": 0,
                "easing": "linear"
              },
              {
                "id": "blp-k1",
                "time": 5.4,
                "value": 0,
                "easing": "linear"
              },
              {
                "id": "blp-k2",
                "time": 8.4,
                "value": 5.2,
                "easing": "linear"
              },
              {
                "id": "blp-k3",
                "time": 10,
                "value": 5.2,
                "easing": "linear"
              },
              {
                "id": "blp-k4",
                "time": 12,
                "value": 0,
                "easing": "linear"
              }
            ]
          },
          {
            "id": "bridge-front-cradle-luffing",
            "name": "前托架抬头",
            "targetId": "bridge-front-cradle",
            "property": "rotation.x",
            "color": "#ff9f2f",
            "muted": false,
            "keyframes": [
              {
                "id": "bfc-k0",
                "time": 0,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bfc-k1",
                "time": 6.8,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bfc-k2",
                "time": 9,
                "value": 0.12,
                "easing": "easeInOut"
              },
              {
                "id": "bfc-k3",
                "time": 10.4,
                "value": 0.12,
                "easing": "linear"
              },
              {
                "id": "bfc-k4",
                "time": 11.8,
                "value": 0,
                "easing": "easeInOut"
              },
              {
                "id": "bfc-k5",
                "time": 12,
                "value": 0,
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-front-pusher-extend",
            "name": "前推送装置送桥",
            "targetId": "bridge-front-pusher",
            "property": "position",
            "color": "#ef476f",
            "muted": false,
            "keyframes": [
              {
                "id": "bfp-k0",
                "time": 0,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bfp-k1",
                "time": 7.2,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bfp-k2",
                "time": 9.4,
                "value": [
                  0,
                  0,
                  0.34
                ],
                "easing": "easeInOut"
              },
              {
                "id": "bfp-k3",
                "time": 10.6,
                "value": [
                  0,
                  0,
                  0.34
                ],
                "easing": "linear"
              },
              {
                "id": "bfp-k4",
                "time": 12,
                "value": [
                  0,
                  0,
                  0
                ],
                "easing": "easeInOut"
              }
            ]
          },
          {
            "id": "bridge-feed-pinion-drive",
            "name": "前推送销齿轮驱动",
            "targetId": "bridge-feed-pinion",
            "property": "rotation.x",
            "color": "#c77dff",
            "muted": false,
            "keyframes": [
              {
                "id": "bfd-k0",
                "time": 0,
                "value": 0,
                "easing": "linear"
              },
              {
                "id": "bfd-k1",
                "time": 7.2,
                "value": 0,
                "easing": "linear"
              },
              {
                "id": "bfd-k2",
                "time": 9.4,
                "value": 4,
                "easing": "linear"
              },
              {
                "id": "bfd-k3",
                "time": 10.6,
                "value": 4,
                "easing": "linear"
              },
              {
                "id": "bfd-k4",
                "time": 12,
                "value": 0,
                "easing": "linear"
              }
            ]
          }
        ]
      },
      "resolved": true
    },
    "addedNode": {
      "id": "node-mtpjlj3c-n13gc",
      "type": "message",
      "title": "操作提示",
      "x": 440,
      "y": 160,
      "params": {
        "title": "操作提示",
        "text": "请观察铲斗与动臂的联动过程",
        "level": "info"
      }
    },
    "draggedNode": {
      "id": "node-mtpjlj9e-sko2s",
      "type": "wait",
      "title": "等待",
      "x": -40,
      "y": -170,
      "params": {
        "duration": 600
      }
    },
    "canvasPreservedGraph": true,
    "scope": "真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。"
  }
}
evidence/truck-blueprint-ui.json 已验证
{
  "time": "2026-09-06T08:21:37.144Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+runtime-read",
    "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存",
    "featureIds": [
      "blueprint.parameters",
      "blueprint.move",
      "blueprint.connect",
      "blueprint.delete",
      "blueprint.copy",
      "blueprint.run",
      "blueprint.stop",
      "blueprint.reset",
      "blueprint.timeline",
      "blueprint.wait",
      "blueprint.highlight",
      "blueprint.message",
      "blueprint.finish",
      "project.undo",
      "project.redo"
    ],
    "scope": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。",
    "projectId": "169",
    "graph": {
      "nodes": [
        {
          "id": "node-mtpjlx7c-sgcyx",
          "type": "trigger-click",
          "title": "闭环-点击触发",
          "x": 160,
          "y": -120,
          "params": {
            "event": "click",
            "target": "model-imported-model_mto6wi6k_08-cesiummilktruck-3"
          }
        },
        {
          "id": "node-mtpjlxj9-y3g3v",
          "type": "play-timeline",
          "title": "闭环-播放时间轴",
          "x": 420,
          "y": -120,
          "params": {
            "clip": "牛奶卡车曲线联动",
            "from": 8,
            "speed": 2,
            "waitForEnd": true
          }
        },
        {
          "id": "node-mtpjlxxe-ond97",
          "type": "wait",
          "title": "闭环-等待",
          "x": 680,
          "y": -120,
          "params": {
            "duration": 100
          }
        },
        {
          "id": "node-mtpjlyci-4c5oi",
          "type": "highlight",
          "title": "闭环-高亮部件",
          "x": 160,
          "y": 70,
          "params": {
            "target": "model-imported-model_mto6wi6k_08-cesiummilktruck-3",
            "color": "#ff8800",
            "pulse": false
          }
        },
        {
          "id": "node-mtpjlyuw-164xj",
          "type": "message",
          "title": "闭环-操作提示",
          "x": 420,
          "y": 70,
          "params": {
            "title": "卡车模型闭环提示",
            "text": "检查真实目标高亮、时间轴完成和条件出口。",
            "level": "success"
          }
        },
        {
          "id": "node-mtpjlz9t-p124m",
          "type": "condition",
          "title": "闭环-条件判断",
          "x": 680,
          "y": 70,
          "params": {
            "variable": "timelineComplete",
            "operator": "==",
            "value": true
          }
        },
        {
          "id": "node-mtpjlznf-cxlzk",
          "type": "finish",
          "title": "闭环-流程完成",
          "x": 420,
          "y": 260,
          "params": {
            "result": "success",
            "text": "牛奶卡车蓝图验证完成"
          }
        }
      ],
      "edges": [
        {
          "id": "edge-mtpjm04u-tt0qb",
          "from": {
            "node": "node-mtpjlx7c-sgcyx",
            "port": "then"
          },
          "to": {
            "node": "node-mtpjlxj9-y3g3v",
            "port": "in"
          }
        },
        {
          "id": "edge-mtpjm0bc-mnf7c",
          "from": {
            "node": "node-mtpjlxj9-y3g3v",
            "port": "then"
          },
          "to": {
            "node": "node-mtpjlxxe-ond97",
            "port": "in"
          }
        },
        {
          "id": "edge-mtpjm0gx-mu6ys",
          "from": {
            "node": "node-mtpjlxxe-ond97",
            "port": "then"
          },
          "to": {
            "node": "node-mtpjlyci-4c5oi",
            "port": "in"
          }
        },
        {
          "id": "edge-mtpjm0nr-y06n7",
          "from": {
            "node": "node-mtpjlyci-4c5oi",
            "port": "then"
          },
          "to": {
            "node": "node-mtpjlyuw-164xj",
            "port": "in"
          }
        },
        {
          "id": "edge-mtpjm0ts-px732",
          "from": {
            "node": "node-mtpjlyuw-164xj",
            "port": "then"
          },
          "to": {
            "node": "node-mtpjlz9t-p124m",
            "port": "in"
          }
        },
        {
          "id": "edge-mtpjm0zf-sa3h2",
          "from": {
            "node": "node-mtpjlz9t-p124m",
            "port": "true"
          },
          "to": {
            "node": "node-mtpjlznf-cxlzk",
            "port": "in"
          }
        }
      ],
      "running": false,
      "logs": [
        {
          "time": "16:21:35",
          "message": "运行状态已重置",
          "level": "system",
          "nodeId": null
        }
      ]
    },
    "completedLog": [
      {
        "time": "16:21:25",
        "message": "蓝图编辑器就绪 · 7 类节点已加载",
        "level": "system",
        "nodeId": null
      },
      {
        "time": "16:21:33",
        "message": "开始执行 · 闭环-点击触发",
        "level": "run",
        "nodeId": "node-mtpjlx7c-sgcyx"
      },
      {
        "time": "16:21:33",
        "message": "执行节点:闭环-点击触发",
        "level": "run",
        "nodeId": "node-mtpjlx7c-sgcyx"
      },
      {
        "time": "16:21:33",
        "message": "执行节点:闭环-播放时间轴",
        "level": "run",
        "nodeId": "node-mtpjlxj9-y3g3v"
      },
      {
        "time": "16:21:33",
        "message": "执行节点:闭环-等待",
        "level": "run",
        "nodeId": "node-mtpjlxxe-ond97"
      },
      {
        "time": "16:21:33",
        "message": "执行节点:闭环-高亮部件",
        "level": "run",
        "nodeId": "node-mtpjlyci-4c5oi"
      },
      {
        "time": "16:21:34",
        "message": "执行节点:闭环-操作提示",
        "level": "run",
        "nodeId": "node-mtpjlyuw-164xj"
      },
      {
        "time": "16:21:35",
        "message": "执行节点:闭环-条件判断",
        "level": "run",
        "nodeId": "node-mtpjlz9t-p124m"
      },
      {
        "time": "16:21:35",
        "message": "条件结果:是 / TRUE",
        "level": "success",
        "nodeId": "node-mtpjlz9t-p124m"
      },
      {
        "time": "16:21:35",
        "message": "执行节点:闭环-流程完成",
        "level": "run",
        "nodeId": "node-mtpjlznf-cxlzk"
      },
      {
        "time": "16:21:35",
        "message": "蓝图执行完成 · 7 个节点",
        "level": "success",
        "nodeId": null
      }
    ],
    "actions": "真实节点库/参数控件/端口拖线/复制剪切粘贴/删除/撤销重做/运行停止重置/保存",
    "boundaries": "200 步循环保护由独立 Node 回归覆盖;本浏览器流程不注入假图或替换执行 handler。"
  }
}
evidence/truck-timeline-curve.json 已验证
{
  "time": "2026-09-06T08:21:02.898Z",
  "data": {
    "status": "PASS",
    "kind": "e2e+runtime-read",
    "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致",
    "featureIds": [
      "timeline.play",
      "timeline.stop",
      "timeline.loop",
      "timeline.key-nav",
      "timeline.add-track",
      "timeline.remove-track",
      "timeline.target",
      "timeline.property",
      "timeline.mute",
      "timeline.add-key",
      "timeline.delete-key",
      "timeline.drag-key",
      "timeline.zoom",
      "timeline.curve",
      "curve.time",
      "curve.value",
      "curve.easing"
    ],
    "scope": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。",
    "projectId": "169",
    "actions": "真实 DOM 轨道/帧增删、关键帧指针拖移、播放控件、曲线输入和保存重开",
    "restored": {
      "id": "track-0-che4ljm",
      "name": "新轨道 6",
      "targetId": "interaction-5f7e4bee",
      "property": "position",
      "color": "#ffb22e",
      "muted": false,
      "keyframes": [
        {
          "id": "key-0-0-mi1ztwf",
          "time": 0,
          "value": [
            0,
            0,
            0
          ],
          "easing": "easeInOut"
        },
        {
          "id": "key-zi8i3f7",
          "time": 2,
          "value": [
            5,
            0,
            0
          ],
          "easing": "easeInOut"
        },
        {
          "id": "key-0-1-2lnk2wi",
          "time": 8,
          "value": [
            0,
            0,
            0
          ],
          "easing": "easeInOut"
        }
      ]
    },
    "interpolationSamples": {
      "linear": 1.25,
      "easeInOut": 0.625,
      "easeIn": 0.3125,
      "easeOut": 2.1875,
      "step": 0
    },
    "excluded": [
      "未新增 solo 能力",
      "未将原生 GLB 动画片段当成多条编辑时间轴"
    ]
  }
}
evidence/viewport.json 已验证
{
  "time": "2026-09-06T08:22:14.169Z",
  "data": {
    "status": "PASS",
    "kind": "e2e",
    "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图",
    "featureIds": [
      "tree.search",
      "tree.visibility",
      "selection.sync",
      "view.grid",
      "view.pbr",
      "view.wireframe",
      "view.outline",
      "view.perspective",
      "view.preview",
      "view.snapshot",
      "timeline.native"
    ],
    "scope": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。",
    "clips": 3,
    "nativeClipPersisted": true,
    "hiddenObjectPersisted": true,
    "snapshot": "装备模型编辑工具_1788682931740.png"
  }
}

构建、聚焦单测与版本检查

完整验证记录。单测/合同测试不并入真实 E2E 数量。

{
  "time": "2026-09-06T08:28:23.891Z",
  "status": "PASS",
  "e2eComplete": true,
  "checks": [
    {
      "name": "装备模型编辑回归",
      "command": "node --test (8 model editor test files)",
      "tests": 54,
      "status": "PASS",
      "failures": 0,
      "exitCode": 0
    },
    {
      "name": "权限桥新建、脏状态和只读键盘回归",
      "command": "node --test tools/content-permissions.test.mjs",
      "tests": 14,
      "status": "PASS",
      "failures": 0,
      "exitCode": 0
    },
    {
      "name": "报告证据归属与附件校验回归",
      "command": "node --test tools/model-editor-report-evidence.test.mjs",
      "tests": 7,
      "status": "PASS",
      "failures": 0,
      "exitCode": 0
    },
    {
      "name": "实际 Kingbase 驱动正常检查与隔离网络无响应验证",
      "command": "KingbaseValidationProbe (isolated loopback TCP proxy, SELECT 1 only)",
      "evidence": "data/kingbase-validation-probe-result.json",
      "tests": 3,
      "status": "PASS",
      "failures": 0,
      "exitCode": 0
    },
    {
      "name": "Auth / Tran 完整构建与测试",
      "command": "mvn -pl ut_auth,ut_tran -am package",
      "tests": 476,
      "moduleTests": [
        120,
        356
      ],
      "status": "PASS",
      "failures": 0,
      "exitCode": 0
    },
    {
      "name": "Web 构建和 iframe 产物一致性",
      "command": "pnpm run build",
      "warning": "既有大包体积提示;构建及10项public/dist哈希校验通过",
      "status": "PASS",
      "failures": 0,
      "exitCode": 0
    },
    {
      "name": "6 E2E specs strict TypeScript",
      "command": "tsc --noEmit --strict (closure specs, helper, config)",
      "status": "PASS",
      "failures": 0,
      "exitCode": 0
    }
  ],
  "coverage": [
    {
      "title": "resource catalog startup renders all model/environment cards and binds every action with a real DOM",
      "featureIds": [
        "resource.catalog"
      ],
      "scope": "真实 DOM 渲染、卡片绑定、空目录及 busy 保护;不包含 GLB 网络加载。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "curve edits real scalar key values, time and interpolation, persisting through document reload",
      "featureIds": [
        "timeline.curve",
        "curve.time",
        "curve.value",
        "curve.easing"
      ],
      "scope": "真实曲线控件与 TimelineEditor,标量编辑与文档往返。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "curve vector editing preserves other components and selected track; invalid values do not mutate",
      "featureIds": [
        "curve.value"
      ],
      "scope": "向量其它分量保留,当前轨道、非法值保护;不包含显示轴切换或可视像素验收。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "multiple tracks on one property use last effective value; capture subtracts it only once",
      "featureIds": [
        "timeline.multitrack"
      ],
      "scope": "调用实际 apply/capture/update 基值方法;验证同属性最后有效轨道、静音/删除后中性姿态,偏移只扣一次。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "changing a binding resets the former target and keeps manual transform edits as base",
      "featureIds": [
        "timeline.multitrack",
        "timeline.target"
      ],
      "scope": "轨道重绑后旧目标复位,手动变换成为新基值;不假称鼠标拖动已验。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "custom properties and extension labels survive actual capture, project JSON and object restore",
      "featureIds": [
        "property.code",
        "property.status",
        "component.tag"
      ],
      "scope": "真实对象 capture、normalization、JSON 与 restore 往返;清空和撤销到未存在字段状态。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "boolean, numeric and nested blueprint conditions select their true/false branches",
      "featureIds": [
        "blueprint.condition"
      ],
      "scope": "布尔、数字、truthy 与嵌套路径值解析。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "visible child under a hidden ancestor cannot be picked or highlighted; trigger aliases include ancestors",
      "featureIds": [
        "tree.visibility",
        "blueprint.highlight"
      ],
      "scope": "共用可见性和祖先别名函数;鼠标事件另由 E2E 验证。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "inspector state tags reflect ancestor visibility and actual shadow flags, with a host editability hook",
      "featureIds": [
        "property.identity"
      ],
      "scope": "真实属性 DOM 显示隐藏/阴影状态,并提供宿主权限标签 hook;不伪称此单测完成会话鉴权。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "history retains 60 states, truncates redo after edits and ignores duplicate snapshots",
      "featureIds": [
        "project.undo",
        "project.redo"
      ],
      "scope": "60 个快照上限、重复状态忽略及新编辑截断 redo 分支。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "history serializes async restores and leaves index unchanged when restoration fails",
      "featureIds": [
        "project.undo",
        "project.redo"
      ],
      "scope": "异步互斥、下载失败不移动索引,解除锁后可继续操作。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "managed-GLB history downloads the snapshot stable reference before restoring, without rewriting it",
      "featureIds": [
        "project.undo",
        "project.redo"
      ],
      "scope": "受控 GLB 快照先物化再恢复,不把临时 URL 写回快照。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "beforeunload recovery does not create a draft for key order alone, but preserves real unsaved changes",
      "featureIds": [
        "project.recovery"
      ],
      "scope": "同语义对象键顺序不产生假恢复稿;真正修改仍保留。不包含全部恢复/隔离/下载 UI 分支。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "highlight rejects missing/hidden targets and transient colors never enter a saved project",
      "featureIds": [
        "blueprint.highlight"
      ],
      "scope": "目标不存在/隐藏报错,执行时暂态发光颜色不进入保存文档,结束恢复原材质。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "API publish supplies current cover, blocks duplicate publication and reports preserved old cover once",
      "featureIds": [
        "publish.api",
        "publish.cover",
        "feedback.toast"
      ],
      "scope": "实际发布宿主方法传封面、重入阻断和上传失败只提示一次;模拟 API,不是实际服务响应。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    },
    {
      "title": "API conflict leaves publication failed and releases the cover guard",
      "featureIds": [
        "project.conflict",
        "publish.api",
        "publish.cover"
      ],
      "scope": "409 分支不误报已发布并释放 busy 标记;模拟 API。",
      "status": "PASS",
      "kind": "unit",
      "testFile": "unreal_tran/tests/model-main-closure.test.mjs"
    }
  ],
  "runtime": {
    "authSha256": "95b8487ca6a1d970387214b027ee9c1d34ae0d1d5c988574c024805d8bb61d9b",
    "tranSha256": "e9528d739d6d817ebf7c5b9a34302195156b1543663066f5484c86c594c6e729",
    "builtEqualsRunning": true,
    "fileService": "开发进程撤销旧17180转发覆盖,恢复已配置远端文件服务"
  },
  "previousWorkPushed": {
    "api": "71df00dffa39a8f49a4f0654969a3014c2f78f67",
    "web": "3f39e2c94df9cdadf3545ae14bf0df41f2c37bf1",
    "e2e": "b3d4ce8ffd8144090c18c8a28f5ca824170042c8"
  }
}

测试数据保留与清理

清理结果

{
  "time": "2026-09-06T08:27:13.581Z",
  "status": "PASS",
  "originals": [
    {
      "id": "157",
      "name": "实测模型-狐狸动画-已验证-20260905",
      "version": 15,
      "unchanged": true
    },
    {
      "id": "156",
      "name": "实测模型-牛奶卡车-已验证-20260905",
      "version": 20,
      "unchanged": true
    },
    {
      "id": "94",
      "name": "桥梁架设装备整机模型",
      "version": 253,
      "unchanged": true
    }
  ],
  "projects": [
    {
      "id": "168",
      "alreadyAbsent": true
    },
    {
      "id": "169",
      "alreadyAbsent": true
    },
    {
      "id": "170",
      "alreadyAbsent": true
    },
    {
      "id": "171",
      "alreadyAbsent": true
    },
    {
      "id": "172",
      "alreadyAbsent": true
    },
    {
      "id": "173",
      "alreadyAbsent": true
    },
    {
      "id": "174",
      "alreadyAbsent": true
    },
    {
      "id": "175",
      "alreadyAbsent": true
    },
    {
      "id": "176",
      "alreadyAbsent": true
    },
    {
      "id": "177",
      "alreadyAbsent": true
    },
    {
      "id": "178",
      "alreadyAbsent": true
    },
    {
      "id": "179",
      "alreadyAbsent": true
    },
    {
      "id": "180",
      "alreadyAbsent": true
    },
    {
      "id": "181",
      "alreadyAbsent": true
    },
    {
      "id": "182",
      "alreadyAbsent": true
    },
    {
      "id": "183",
      "name": "闭环导航名称-183",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "184",
      "name": "闭环验收-资源往返-mtph19ll",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "185",
      "name": "闭环验收-三维手柄-1788678747289",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "186",
      "name": "闭环验收-离开保护-1788678780620",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "187",
      "name": "闭环验收-恢复副本-mtph6hw8",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "188",
      "name": "闭环验收-加载与预检-mtph6ubj",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "189",
      "name": "闭环验收-三维手柄-1788678838243",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "190",
      "name": "闭环验收-离开保护-1788678850196",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "191",
      "name": "闭环工具补充-卡车控件-mtpha049",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "192",
      "name": "闭环工具补充-卡车控件-mtphbg8t",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "193",
      "name": "闭环工具补充-基准匹配-mtphbpqq",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "194",
      "name": "闭环工具补充-卡车控件-mtphfk1m",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "195",
      "name": "闭环工具补充-基准匹配-mtphfu32",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "196",
      "name": "闭环验收-资源往返-mtphjhoi",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "197",
      "name": "闭环验收-拒绝非法资源-mtphjv5x",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "198",
      "name": "闭环验收-保存失败恢复-mtphk0kk",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "199",
      "name": "闭环验收-发布封面版本-mtphk960",
      "withdrawn": true,
      "removed": true
    },
    {
      "id": "200",
      "name": "闭环工具补充-卡车控件-mtphkrbz",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "201",
      "name": "闭环工具补充-基准匹配-mtphkzlj",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "202",
      "name": "闭环新建-1788679561949",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "203",
      "name": "闭环验收-三维手柄-1788679613072",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "204",
      "name": "闭环验收-离开保护-1788679623529",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "205",
      "name": "闭环补充-资源库-mtphobxb",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "206",
      "name": "闭环补充-内置环境-mtphomov",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "207",
      "name": "闭环导航名称-207",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "208",
      "name": "闭环验收-恢复副本-mtphpz7v",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "209",
      "name": "闭环验收-加载与预检-mtphq9y1",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "210",
      "name": "闭环验收-离开保护-1788681347476",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "211",
      "name": "闭环补充-内置环境-mtpip60q",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "212",
      "name": "闭环验收-资源往返-mtpjbnl2",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "213",
      "name": "闭环验收-拒绝非法资源-mtpjbyqo",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "214",
      "name": "闭环验收-保存失败恢复-mtpjc2vu",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "215",
      "name": "闭环验收-发布封面版本-mtpjcafv",
      "withdrawn": true,
      "removed": true
    },
    {
      "id": "216",
      "name": "闭环工具补充-卡车控件-mtpjcof8",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "217",
      "name": "闭环工具补充-基准匹配-mtpjcvxe",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "218",
      "name": "闭环新建-1788682528069",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "219",
      "name": "闭环验收-三维手柄-1788682536810",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "220",
      "name": "闭环验收-离开保护-1788682545004",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "221",
      "name": "闭环补充-资源库-mtpjesgj",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "222",
      "name": "闭环补充-内置环境-mtpjezfi",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "223",
      "name": "闭环导航名称-223",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "224",
      "name": "闭环验收-恢复副本-mtpjg6y2",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "225",
      "name": "闭环验收-加载与预检-mtpjgfnd",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "226",
      "name": "闭环验收-资源往返-mtpjkcpq",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "227",
      "name": "闭环验收-拒绝非法资源-mtpjko7g",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "228",
      "name": "闭环验收-保存失败恢复-mtpjkshn",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "229",
      "name": "闭环验收-发布封面版本-mtpjkzt1",
      "withdrawn": true,
      "removed": true
    },
    {
      "id": "230",
      "name": "闭环工具补充-卡车控件-mtpjlf69",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "231",
      "name": "闭环工具补充-基准匹配-mtpjlmiy",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "232",
      "name": "闭环新建-1788682938712",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "233",
      "name": "闭环验收-三维手柄-1788682951454",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "234",
      "name": "闭环验收-离开保护-1788682960426",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "235",
      "name": "闭环补充-资源库-mtpjnoh9",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "236",
      "name": "闭环补充-内置环境-mtpjnv0t",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "237",
      "name": "闭环导航名称-237",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "238",
      "name": "闭环验收-恢复副本-mtpjp3nr",
      "withdrawn": false,
      "removed": true
    },
    {
      "id": "239",
      "name": "闭环验收-加载与预检-mtpjpdaj",
      "withdrawn": false,
      "removed": true
    }
  ]
}

交付物凭据与附件检查

最终扫描通过 · 脱敏扫描结果

四个仓库相对 HEAD 的变动/新增文件与本轮完整报告;真实本地账号密码精确匹配、可解码 JWT、ZIP/Playwright 内嵌报告逐项解压扫描及 PNG 文本元数据。输出不包含任何命中值或附近正文。

扫描 104 份文本、122 张图片的文本元数据及 1 个归档;图片正文不以文本扫描代替视觉 QA。

检查范围、历史常量排除与限制
{
  "time": "2026-09-06T08:33:57.163137+00:00",
  "status": "PASS",
  "secretScan": "PASS",
  "phase": "final",
  "finalArtifactScan": true,
  "scope": "四个仓库相对 HEAD 的变动/新增文件与本轮完整报告;真实本地账号密码精确匹配、可解码 JWT、ZIP/Playwright 内嵌报告逐项解压扫描及 PNG 文本元数据。输出不包含任何命中值或附近正文。",
  "baselinePolicy": "只排除 HEAD 已存在且邻近至少64字符完全一致的普通源码/固定库字符串(统一CRLF/LF);新哈希模型bundle另允许至少25个邻近词元在仅归一化压缩变量名后完全一致,且命中须为更长固定常量的子串、不能是完整密码值。唯一经本轮复核的配置例外是 ut_auth/src/main/resources/application.yml 的 bootstrap-admin-password 环境变量默认项:文件、键、整行须与 HEAD 完全相同,且新旧文件各只出现一次;改值、改变量名、复制到其他文件或新增同一行均仍失败。其他真实凭据赋值与ZIP内元数据不作排除。Playwright静态库对比HEAD上一轮报告壳。",
  "limitations": [
    "图片仅扫描可读元数据,不执行OCR;图像正文由浏览器报告视觉QA核对。",
    "只扫描本轮交付范围,不声称已审计整个历史仓库。",
    "初查通过不代表最终结果文件已完成,最终需 --final 再执行。"
  ],
  "selfChecks": {
    "status": "PASS",
    "tests": 15
  },
  "repositoryHeads": {
    "unreal_tran": "d16184acf1686bb4a6ad44949d0587c5373ffbde",
    "unreal_tran_web": "3f39e2c94df9cdadf3545ae14bf0df41f2c37bf1",
    "unreal_tran_api": "71df00dffa39a8f49a4f0654969a3014c2f78f67",
    "ute2e": "b3d4ce8ffd8144090c18c8a28f5ca824170042c8"
  },
  "changedFileCounts": {
    "unreal_tran": 14,
    "unreal_tran_web": 8,
    "unreal_tran_api": 15,
    "ute2e": 184
  },
  "deletedFileCounts": {
    "unreal_tran": 0,
    "unreal_tran_web": 2,
    "unreal_tran_api": 0,
    "ute2e": 0
  },
  "textArtifactsScanned": 104,
  "pngMetadataFilesScanned": 122,
  "archives": [
    {
      "file": "ute2e/reports/model-editor-closure-20260906/playwright/index.html!embedded-report-1.zip",
      "entries": 7,
      "textEntries": 7
    }
  ],
  "finalInputs": {
    "results.json": true,
    "validation.json": true,
    "cleanup.json": true,
    "playwright/index.html": true
  },
  "finalInputSha256": {
    "results.json": "f37013658c8c38a5ec5993e636a30ad57eee9d3bcc119650c929952de8d556e9",
    "validation.json": "6e0e87d0df73061bc8549d3405f03731fecf077d7425f52eeed45008f7d09959",
    "cleanup.json": "ab2776afa143d9937651daa8fa18b531a3fb84c703f5f5b0918930e78c76eb26",
    "playwright/index.html": "92dcca3fe570530bde973b9e09199c064fc2b343bb6f8a21b67fb3c72b8949ed"
  },
  "unchangedBaselineExclusions": [
    {
      "file": "unreal_tran_api/ut_auth/src/main/resources/application.yml",
      "baseline": "unreal_tran_api/HEAD:ut_auth/src/main/resources/application.yml",
      "occurrences": 1
    },
    {
      "file": "unreal_tran_api/wiki/11-内容制作模块.md",
      "baseline": "unreal_tran_api/HEAD:wiki/11-内容制作模块.md",
      "occurrences": 17
    },
    {
      "file": "unreal_tran_web/public/legacy-model-editor/assets/editor-BAqfQ3VC.js",
      "baseline": "unreal_tran_web/HEAD:public/legacy-model-editor/assets/editor-CunHwJSa.js",
      "occurrences": 1
    },
    {
      "file": "ute2e/reports/model-editor-closure-20260906/playwright/index.html",
      "baseline": "ute2e/HEAD:reports/role-permissions-20260906/playwright/index.html",
      "occurrences": 1
    }
  ],
  "unscannedBinaryFiles": [],
  "findings": []
}

较大调整的具体范围

以下能力超出现有小范围修复。本轮保留入口并明确现状;确认范围后才能据此另行实现和验收。

D1

真正的组件运行系统

当前行为
“组件”页目前是内置能力说明;新增项只保存扩展标记名称,没有启停、参数执行或生命周期。
拟实现的可观察行为
用户新增一种行为组件,配置参数后能在当前模型和约定下游环境执行;停用后立即停止,重开后按保存状态恢复。
建议确认的实施范围
先定义有限组件目录、参数 schema、权限与版本;实现初始化/更新/销毁及错误隔离。不要直接开放任意脚本。
验收条件
每种批准组件可添加、编辑、启停、删除、保存重开;重复载入不重复绑定事件;失败组件不阻断其他对象,运行结果可定位。
D2

完整三维离线运行包

当前行为
现有 HTML 是嵌入静态截图与工程数据的步骤演示,可离线查看;没有 GLB 字节和完整三维运行时。
拟实现的可观察行为
断网电脑打开交付包,真实模型/材质/原生动画/编辑时间轴与交互节点可以运行,且不依赖服务端 blob URL。
建议确认的实施范围
明确交付形式(目录/ZIP或单文件)、体积约束、资产授权和三维依赖;打包全部受控资产、运行时和相对资源清单。
验收条件
清缓存断网运行;模型和环境完整可见,动作/分支/高亮可交互;资源缺失明确报错;包内清单/哈希与发布版本一致。
D3

下游场景 / 训练自动执行模型动画与蓝图

当前行为
发布模型可供下游按固定版本引用;不同下游已有局部动画能力,但没有统一的模型 timeline/blueprint 自动执行桥接。加载成功不等于动作已自动执行。
拟实现的可观察行为
在场景或训练放入模型实例后,按明确触发策略启动该发布版本的时间轴/蓝图;多个实例互不干扰。
建议确认的实施范围
统一实例目标命名空间、事件总线、生命周期和停止规则;明确自动播放、用户触发与训练步骤触发优先级,以及错误/完成结果回传。
验收条件
两个同模型实例分别触发不串台;锁定发布版本;停止/切步骤/卸载无迟到动作;真实对象姿态、分支和结果回传均有 E2E 证据。

41 张截图 · 点击放大

截图来自实际目录;首次加载、过程画面和最终结果按图注说明,不把空场景或加载遮罩当作模型可见证据。

证据与复现入口

原图