8.7 KiB
B04 验收 01:展开图片的 Windows 连续帧
日期:2026-10-21,Asia/Shanghai。受测代码 342820690f644fb1cb3b0a76b143dbcd41aac8c4,交付记录:B04-交付-01,任务 YC-008。
结论:图片地址首帧闪现的核心修复通过本轮 Windows 组件与完整生产应用验证。没有再出现完整 Base64 文本或数千像素瞬间增高。交付的原生验收命令在本机超时,记为测试可移植性问题 T03,不能直接登记原命令通过。
这是验收,不是产品开发:未修改产品源码、正式测试脚本、依赖清单或构建配置。所有探针在固定 SHA 的新工作树和 .tmp 中运行;只提交本报告及可复核证据。
1. 环境与基本检查
- 新建
b04-accept-01工作树固定检出完整受测 SHA,独立npm ci --legacy-peer-deps,没有复用其他实例的 node_modules 链接。 - Windows,Node
24.21.0,npm11.19.0,Electron37.10.3;显式确认 Electron 运行时可用。 npm run build、npm test、指定 jsdom 后npm run test:alignment:dom、npm run test:image-lifecycle、npm run test:media均退出 0。- 生命周期回归实际 159 项断言、85 个 DOM 样本;alignment offline 11 套、DOM 7 套;媒体回归含图片/URL/下载失败与路径检查。
check:tokens仍退出 1,相同六项既存失败,没有改 baseline。B04 没有 CSS 改动。- 验证后受测工作树
git status --short为空。未重启用户 dev,未加载或改写用户真实历史消息/图片。
结果:基础命令、回归命令、生命周期日志、媒体日志、构建日志。
2. 原生组件夹具:136 项检查、105 个样本
按交付命令构建同一个实际 MessageBubble / CompletedFold / MarkdownImage 夹具,在 Electron Chromium 运行。图片为合成的有效 320×180 PNG,附约 18KB 二进制填充,生成约 25KB 的长 Base64,alt 为空。
- 覆盖合成会话 A→B→A,每次三个开关循环,真实图片重挂载。
- 在 layout effect、同步提交、开关前后、连续 requestAnimationFrame 中采样;不是只截稳定图片。
- 105 个样本最大区域高度 232px,最大文字长度 35 字符;图片实际解码,naturalWidth=320,存在非零图片高度。
- 未插入 URI/Base64 文本,没有捕获 renderer error。
- 组件 wrapper 保留 sandbox:true、contextIsolation:true、网络请求阻断。仅对 QA BrowserWindow 设置
backgroundThrottling:false,没有修改产品 CSS、动画或采样断言。
T03:原生交付脚本在本机超时
原命令:
node node_modules/electron/cli.js scripts/verify-b04-image-native.cjs <build-only目录>
实际退出 1,20 秒截止,报 Bounded native probe timed out after 20s。诊断确认夹具已加载,window.__b04 存在,没有即时脚本错误,但连续帧未完成。仅添加 console 诊断的副本仍超时;在同一副本增加 backgroundThrottling:false 后约 1 秒完成,保留原 20 秒截止与全部断言。
原命令日志、诊断日志、验收 wrapper。此处是后台帧节流条件下的脚本可移植性问题,没有证据表明图片仍闪现;也不能把临时 wrapper 通过写成原脚本通过。
开发端后续:为原生连续帧测试显式约定 QA 窗口帧调度/可见性和截止策略,确保目标平台运行稳定,同时保留真实 rAF 与布局、文字断言。不要靠去掉检查、改成 jsdom 或延长到无限等待来消除失败。
旧组件负对照
在同一新夹具中,仅用 esbuild 临时替换成基线 0b613e5 的 MarkdownImage.tsx;未改产品文件。相同原生 wrapper 退出 1,首先捕获:
synthetic-A: layout-before-passive-effects: no URI text
该 layout 断言抛错后 React 卸载树,后续又报 CompletedFold header 不存在;原始首因在 负对照日志 中保留。这证明探针能抓到修复前的 URI 首帧,退出 1 是期望的旧版失败,不是修复版失败。构建器:build-negative.mjs。
3. 完整生产应用:43 项检查、168 个连续样本
在新的专用 userData 中保存两条合成历史,启动未修改的生产 main/preload/renderer。图片和消息由探针生成,模型配置指向不可用本地端口,整个测试不发送模型请求。
- 选择会话 A→B→A,各进行四次展开/收起,共 12 次。
- 每次记录点击前、点击同步点和之后 12 个真实 rAF,共 168 个样本。
- 样本所在的实际 assistant 区域高度保持 322.75~357px,文字最长 56 字符。全部无
data:image//base64,文本,无数千像素增高,图片实际解码。 - 附加 MutationObserver 检查新增文字节点,没有检测到 URI 文本;没有捕获到的 renderer error。
- 点击实际图片,生产预览打开、解码;点击实际下载按钮,经真实 preload/IPC/下载解码/文件写入通路,下载字节与完整合成 PNG(含填充)逐字节相同。
- 为避免弹出交互式系统保存框,仅由 QA 启动器替换
dialog.showSaveDialog返回专用测试路径;因此验证了保存通路,未验证 Windows 原生文件选择框的人工交互。 - 预览关闭检查通过;未为截图修改产品样式。
实际应用 QA 窗口同样关闭后台节流以保证连续采样。该设置属于测试启动器,不是本次产品改动。
证据:应用报告及全部帧数据、应用探针、展开截图、跨会话后展开、预览截图。已人工查看截图,判断主要依据连续样本和 DOM 记录,截图不替代首帧验证。
4. AC 状态与限制
| AC | 判定与层级 |
|---|---|
| 1 长 Base64/空 alt 冷挂载 | 实际组件 layout-before-passive-effects、同步提交到原生帧通过;基线负对照能捕获旧行为。 |
| 2 多次开关、切换会话 | 组件夹具与真实生产应用历史夹具均通过。 |
| 3 连续帧、无数千像素尖峰 | Windows 实际布局通过,105+168 样本,不用 jsdom 的 height=0 代替。 |
| 4 失败/非法地址短占位 | 159 项生命周期回归覆盖,媒体/URL 检查通过;所有失败源的真实 Chromium 网络/图片事件矩阵未穷举。 |
| 5 快速切换、过期请求/取消 | DOM 夹具覆盖 src、resolver、host 偏好、A→B→A、迟到成功/失败、卸载、立即 error;真实应用开关/会话切换通过。真实异步宿主故障与全部快速切源排列仍未全测。 |
| 6 预览、下载、安全策略 | 实际预览解码/关闭、实际 IPC 保存字节通过;URL/下载失败回归通过。网络图片真实下载、系统文件框人工交互和全部预览缩放/旋转手势未另行全测。 |
本轮确认核心 bug 修复有效,T03 留给开发端修正式测试。不能据此声明 WorkBuddy 全界面 1:1,或所有图片源/平台/网络异常场景已验证。未采集原版 WorkBuddy,本轮没有新的 REA Evidence ID。
5. 复跑
固定受测 SHA 独立安装构建后,按交付命令运行 DOM/媒体/夹具构建。验收临时脚本放置位置如下;不要将用户 dev、真实 userData 或正式历史当作夹具:
New-Item -ItemType Directory -Force .tmp/b04-acceptance | Out-Null
Copy-Item docs/协作/evidence/B04-review-01/native-diagnostic.cjs .tmp/b04-acceptance/native-diagnostic.cjs
Copy-Item docs/协作/evidence/B04-review-01/app-frames.cjs .tmp/b04-acceptance/app-frames.cjs
node scripts/test-image-lifecycle.mjs --build-only
# 将上一条实际输出目录传给 wrapper;必须保留 sandbox:true。
node node_modules/electron/cli.js .tmp/b04-acceptance/native-diagnostic.cjs <fixture目录>
node .tmp/b04-acceptance/app-frames.cjs
原生 wrapper 内部 20 秒截止;完整应用探针内部 60 秒、启动器 70 秒上限,只结束自己创建的进程。本报告推送并核对 SHA 后,将协作窗口交回开发端,后续处理 T03 和未覆盖项;不撤销 B01/B02/B03 的历史边界。