# WALLPAPER-LIFECYCLE.md — 壁纸流水线真机 bug 批次(2026-09-20) > 现场:用户真机 `:3080`,素材 = 库根 `allwallpaper/dd/3580207945` > (`project.json`: `type:"web"` / `file:"index.html"` / `preview:"preview.gif"`,Vite 构建: > `index.html` + `assets/(index-*.js,index-*.css,font.ttf)` + `loadJson.json` + `BGM.wav` + `1-1.wav.ogg`…`5.wav.ogg`) > 与容器档 `custommpkg|小鸟游星野01_04.mpkg`(`converted:"mp4"`、内嵌 `bgcs_abydos03.mp4`)。 > 归属口径:**服务端不可达(`:8899/:8901/:8902` 看门狗死过)与插件自身 bug 分开记** —— > 本文件只记插件/宿主代码的缺陷;"服务端当时挂了"导致的 8899 connection refused 与黑屏已由主对话重启三服务解决,不计入本批。 **最终读数**(提交 `84c6ffc` 之后、`update-plugin.sh` 同步完): * `bash tools/check.sh` ⇒ **12/12 全绿**(`全部通过 ✓ 下一步:bash update-plugin.sh`); * `node tools/wallpaper-lifecycle-test.mjs` ⇒ **133 通过 / 0 失败**(含 **15 组变异自证**,每组按期望变红); * `node tools/wallpaper-lifecycle-live-probe.mjs`(真机 `:3080`)⇒ **31 PASS / 0 FAIL / 1 SKIP**; `--selftest` ⇒ 13/13;跑前跑后 `settings.json` / `custom-dir.json` **逐字节一致=true**。 SKIP = ③-2c(帧内能力自证):宿主进程里的 `lib/web-wallpaper.js` 还是改动前那一份 (ESM 模块缓存;`dsh` 重启后该项会变 PASS,见 §10.1)。 * 探针的**已知抖动**(工具侧,不是被测行为):宿主设置弹窗 + 我们那一节是懒渲染, headless 下偶发"等不到 `.mpw_wallThumb`/`.mpw_reset`" ⇒ 已加轮询 + 兜底再点标签; 若某次跑出现该类 FAIL,先看探针日志里的"警告:等不到 …"再判。 判据全部常驻门禁: | 门禁 | 位置 | 覆盖 | | --- | --- | --- | | `tools/wallpaper-lifecycle-test.mjs` | `tools/check.sh` 第 2 步 | A–N 十四组判据 + **15 组变异自证**(每条修复一组) | | `tools/wallpaper-lifecycle-live-probe.mjs` | 真机(`--selftest` 无浏览器) | 真机读数:面板 rect / 清空 / 源不可用 / 沙箱对照 / 预览 / 分类 / 音频归属 / 联动 / 切页 | --- ## 0. 一句话结论表 | 条 | 根因(`文件:行` 均为修前) | 修法 | 真机读数(修前 → 修后) | | --- | --- | --- | --- | | ⑥ 设置面板被压进左栏 | `lib/client.js:11011` 给 `[class*="sidebarCol"]` 的 `backdrop-filter !important` 落在**设置 overlay 的祖先**上(overlay 在侧栏子树内,靠 fixed 逃出) | 结构判据 `data-mpw-holds-layer`(祖先链标记)+ 三条 blur 规则加 `:not([data-mpw-holds-layer])` + `:has()` 之外的延迟重算 | 面板 `{x:320,w:800}`(守卫在)→ **守卫失效时 `{x:13,w:254}`** ⇒ 修后任意时刻都 `{x:320,w:800}` | | ② 清除壁纸后壁纸被重新加载 | `mpwStickySource`(`lib/client.js:878`)把 `undefined` 当"没改到"⇒ `clearBg` 写的 `webUrl: undefined/sceneKey: undefined` 被**从旧档带回来** | `clearBg` 改用 `null`(唯一删除语义);换档走 `mpwSwitchPatchFull`(成套重写) | 清空后 `webUrl/sceneKey` **仍在** → 清空后 6 个源字段全空、层 `display:none`、iframe/video 无 src、6s 未复活 | | ② 残留 `webUrl` 抢先武装(黑屏级联) | `applyFromStorage` 旧写法"有 webUrl 就走 web"(`lib/client.js:4752`) | 挂载裁决纯函数 `mpwPickMount` + `hasImage` 同口径 + 卸载残留帧 | 混态档:`mpw-bgWrap mpw-web mpw-scene-fallback`(黑屏)→ **`mpw-bgWrap mpw-video mpw-sharp`**(正常播放) | | ① 预览框空白 | 预览分支只认 mpkg/视频/`image`,web 档 `image` 为空 ⇒ 全落空(`lib/client.js:15300` 附近) | 候选链 `mpwThumbCandidates`(`preview.gif/jpg/png/webp/jpeg`、`loading.webp`)+ `mpwThumbNext` 逐个退,耗尽落占位 | 预览框无候选 → `preview.gif` 真加载出像素(`naturalWidth=150`) | | ③ 沙箱档加载不了 | 沙箱帧是不透明源;作者脚本用 blob-URL Worker 时被策略挡(`SecurityError`),父页只看到"shim 已握手"所以毫不知情 | shim 侧能力自证(`probeCaps`→`caps`)+ 父页按**策略类错误**一次性降级兼容档(可判定状态 + 面板人话) | 沙箱档:`sandbox="allow-scripts"`、`shimOk=true`、作者脚本报 `Error`;降级路径见下 | | ④ 卡片暂停管不住帧内音频 | `applyWebMute` 只看 `npAudioOwns()`(我们**正在放**)⇒ 按下暂停那一刻帧内立刻解除静音 | `npFrameSoundBlocked()`(卡片暂停 ∨ 我们在放音)+ 暂停那一半 `pauseWebFrame` + 可判定状态 | 卡片暂停后帧内 `muted=false` 继续响 → `blocked=true reason=card-paused`、帧内 muted+paused | | ⑤ 切页不静音 | `powPauseHidden` 默认 `false`(`lib/client.js:6708`)+ 只 pause 壁纸 video | 默认改 `true`(只迁移"从没设过"的存量档)+ 覆盖我们自己的 `