@skillbuilder/miniapp-simulator 0.2.2 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/docs/assets/audit-auto-stop-config-2026-10-21.jpg +0 -0
  2. package/docs/assets/audit-balanced-panel-2026-10-16.jpg +0 -0
  3. package/docs/assets/audit-lcp-input-2026-10-20.jpg +0 -0
  4. package/docs/assets/audit-paint-game3d-2026-10-16.jpg +0 -0
  5. package/docs/assets/audit-paint-webgl-2026-10-16.jpg +0 -0
  6. package/docs/assets/audit-paint-webgl-apm-excerpt-2026-10-16.json +397 -0
  7. package/docs/assets/audit-poetry-2026-10-16.jpg +0 -0
  8. package/docs/assets/audit-remaining-results-2026-10-16.json +2749 -0
  9. package/docs/assets/miniapp-audit-risk-review-2026-10-13.png +0 -0
  10. package/docs/assets/miniapp-audit-runtime-hung-2026-10-13.png +0 -0
  11. package/docs/audit-data-collection.codex.md +105 -0
  12. package/docs/audit-local-validation.codex.md +165 -0
  13. package/docs/audit-score-design.codex.md +222 -0
  14. package/docs/audit-score-design.md +832 -0
  15. package/docs/audit-score-tech-plan.md +450 -0
  16. package/docs/audit-tech-design.codex.md +485 -0
  17. package/docs/electron-review.md +223 -12
  18. package/docs/quality-evaluation-redoc.xml +163 -0
  19. package/docs/tracker.md +3 -2
  20. package/lib/client-BjYHK9LR.mjs +1220 -0
  21. package/lib/{index-D5AYVv8s.mjs → index-CqClPZVv.mjs} +230 -184
  22. package/lib/index.js +2204 -1999
  23. package/lib/remote-client.js +2 -2
  24. package/lib/types/host/shadow-host.d.ts +10 -0
  25. package/lib/types/injection/registry.d.ts +5 -0
  26. package/lib/types/remote/audit-controller.d.ts +61 -0
  27. package/lib/types/remote/audit-leave-monitor.d.ts +12 -0
  28. package/lib/types/remote/audit-package-sources.d.ts +9 -0
  29. package/lib/types/remote/audit-telemetry.d.ts +36 -0
  30. package/lib/types/remote/audit-watchdog.d.ts +14 -0
  31. package/lib/types/remote/client.d.ts +25 -1
  32. package/lib/types/remote/load-timing.d.ts +5 -0
  33. package/lib/types/remote/protocol.d.ts +162 -1
  34. package/lib/types/remote/runtime.d.ts +2 -0
  35. package/lib/types/remote-client.d.ts +2 -2
  36. package/lib/types/runtime/jsapi-modules/interaction.d.ts +1 -1
  37. package/lib/types/runtime/jsapi-modules/local-media.d.ts +4 -0
  38. package/lib/types/runtime/jsapi-modules/media.d.ts +1 -1
  39. package/lib/types/runtime/jsapi-modules/note-publish-media.d.ts +9 -0
  40. package/lib/types/sw/service-worker-host.d.ts +15 -0
  41. package/lib/types/tracker/constants.d.ts +1 -1
  42. package/lib/types/tracker/default-tracker.d.ts +9 -1
  43. package/lib/types/types/options.d.ts +24 -0
  44. package/lib/workbench.js +1 -1
  45. package/package.json +1 -1
  46. package/lib/client-_vNHVHSm.mjs +0 -775
@@ -0,0 +1,485 @@
1
+ # 小工具体验评测技术方案(一期:远程模拟器)
2
+
3
+ ## 1. 范围与目标
4
+
5
+ 体验评测服务于远程浏览器模拟器中的小工具审核。它在不改变小工具业务语义的前提下采集运行期资源与体验数据,产出可解释的评分、风险告警和三态报告。
6
+
7
+ 系统目标:
8
+
9
+ - 识别持续内存增长、显存高水位、WebGL 上下文丢失、加载期主线程卡死等稳定性风险。
10
+ - 采集包体、图片、脚本、画布、WASM、长任务、帧率和 JS 错误等体验指标。
11
+ - 在 `businessLine === mars` 时,把评分面板常驻展示在手机壳右侧;未加载 ZIP 时展示空态,内容启动后自动开始采集。鼠标离开整个模拟器容器(含留白与评测面板) 30 秒后自动结算,也可手动停止评测。
12
+ - 让阈值、权重、卡口开关和 profile 由 Apollo 配置调控,首期只观察,不自动阻断审核流。
13
+
14
+ 边界:
15
+
16
+ - 只支持浏览器 Runtime 和 ES2020,不引入 Electron、Node 侧评分服务或规则计算包。
17
+ - 服务端只负责策略下发;评测时序、事件、包体摘要和结果由模拟器内部采集并统一上报现有 APM,不新增评测数据接口或存储。
18
+ - 评测不干预小工具逻辑,不通过 hook 修改参数、返回值、异常或控制流。
19
+
20
+ ## 2. 模块与数据流
21
+
22
+ 实现位于一个浏览器内插件包:`packages/miniapp-audit-plugin`。
23
+
24
+ ```text
25
+ guest 小工具 iframe Runtime iframe 宿主页
26
+ ───────────────── ────────────── ──────
27
+ probe.impl.ts
28
+ 安全 hook / 1s 快照
29
+ │ audit:ready / sample / incident
30
+ └──────────────────────────────▶ collector / AuditSession
31
+ │
32
+ derive → applicable → evaluate → aggregate
33
+ │
34
+ ┌────────────────────┴────────────────────┐
35
+ ▼ ▼
36
+ auditReport 远程事件 右侧 Shadow DOM 面板
37
+ │
38
+ RemoteMiniAppSimulator.audit
39
+ ```
40
+
41
+ | 目录 | 职责 |
42
+ | --- | --- |
43
+ | `src/probe/` | document-start 注入字符串、安全 hook、资源配对记账、1 秒快照 |
44
+ | `src/collector/` | ZIP 静态扫描、策略解析、会话状态机 |
45
+ | `src/rules/` | 纯数据的指标派生、适用性、逐项评分、聚合和卡口判定 |
46
+ | `src/panel/` | 原生 DOM + Shadow DOM 评分面板 |
47
+ | `src/index.ts` | 插件安装、评测时钟、熔断线覆盖与报告收口 |
48
+
49
+ `src/probe/probe.impl.ts` 保持自包含、无 import。`pnpm gen:probe` 使用 esbuild `transform` 去除 TypeScript 类型,目标为 ES2020,生成受版本控制的 `probe.impl.built.js`。`injection.ts` 通过 `?raw` 读入该文件并用 IIFE 包裹,避免与其他注入模块共享顶层变量。
50
+
51
+ ## 3. 安全探针
52
+
53
+ ### 3.1 Hook 不改变业务语义
54
+
55
+ 所有可替换方法统一使用 `safeHook`:原函数先执行,业务异常自然抛出;统计只在原调用成功后执行,统计异常完全隔离。
56
+
57
+ ```ts
58
+ const result = original.apply(this, args);
59
+ try {
60
+ collect(this, args, result);
61
+ } catch {
62
+ // 统计失败不能影响小工具
63
+ }
64
+ return result;
65
+ ```
66
+
67
+ 包装器保留 `this`、实参、返回值和原异常,并复制 `name`、`length` 和 `toString()` 可观察元数据。不可写或不可配置的原生属性直接跳过采集,不以失败换取覆盖率。
68
+
69
+ 探针从不在高频 WebGL 路径调用 `gl.getParameter`。同步查询会刷新 GPU 命令队列,反而污染被测帧率;绑定状态由 `bindTexture`、`bindBuffer`、`bindRenderbuffer` 的安全 hook 在 JS 内维护。
70
+
71
+ ### 3.2 资源配对记账
72
+
73
+ | 资源 | 写入 / 更新 | 释放 | 快照值 |
74
+ | --- | --- | --- | --- |
75
+ | 纹理 | `texImage2D`、`texStorage*`、压缩纹理、mipmap | `deleteTexture` | 当前纹理水位 |
76
+ | Buffer | `bufferData` 覆盖写 | `deleteBuffer` | 当前 buffer 水位 |
77
+ | Renderbuffer | `renderbufferStorage*` 覆盖写 | `deleteRenderbuffer` | 当前 renderbuffer 水位 |
78
+ | Canvas / Image | `getContext`、图片 load | DOM 移除或弱引用失效 | 当前像素后备缓冲 |
79
+ | WASM | 被构造 hook 捕获的 `WebAssembly.Memory` 缓冲 | 当前实现保留最多 64 个已捕获 Memory 引用 | 已捕获 Memory buffer 字节数,非完整 WASM 内存 |
80
+ | Worker | 被构造 hook 捕获的创建和显式 terminate | `terminate` | 已捕获对象的创建/终止余额 |
81
+
82
+ 记账值表示当前存活水位,而不是累计上传量。场景切换中“创建 → 释放 → 创建”的资源会回落;长会话不会因历史资源总量而产生虚假的内存泄漏。
83
+
84
+ 上述图形数据是应用层逻辑估算,不能当作 GPU 驱动真实占用。WASM 内部创建/导出的 Memory、Worker 内资源与自行退出等路径未保证全量覆盖;有读数不等于全量已测,没有读数也不能证明没有风险。
85
+
86
+ 纹理、Buffer 和 Renderbuffer 使用 `WeakMap<object, id>` 定位对象,用 `Map<id, entry>` 保存可遍历账本,并用 `FinalizationRegistry` 在对象被回收时删除账目。Canvas 与 Image 用弱引用集合;快照时顺带清除已失效的引用。
87
+
88
+ 每个注册表最多 20,000 条。达到上限后停止新增、置 `probeSaturated`,报告将显存与画布项降级为 `na`,不会以已知偏低的数值出分或触发卡口。探针本身始终有界。
89
+
90
+ ### 3.3 一秒快照与离散事件
91
+
92
+ 探针每秒向父 Runtime 发送一次 `audit:sample`,包括:
93
+
94
+ - `performance.memory.usedJSHeapSize`(不可用时为 `null`)
95
+ - 纹理、Buffer、Renderbuffer、Canvas、Image、WASM 和 Worker 当前水位
96
+ - 探针自身 rAF 节拍、最大长任务、JS 错误数、真实输入累计次数
97
+ - `saturated` 标记
98
+
99
+ `audit:ready` 表示探针已在新 guest Realm 中完成安装;`audit:incident` 承载 `contextLost`、`jsFatal`、`probeSaturated` 等离散事件。每秒快照之外不发送逐 API 调用日志,避免探针自身成为性能负担。
100
+
101
+ 当前 rAF 值表示页面调度节拍,默认权重为 0,不能当作游戏真实呈现帧率。`netPending` 字段已存在,但探针尚未接 fetch/XHR 状态维护,其当前值不能用于判断网络空闲。业务循环、绘制提交和实际网络状态属于 1.1 期待实施项。
102
+
103
+ JS 异常由 `error` 和 `unhandledrejection` 采集,区分 `app`、`simulator-sdk` 和 `audit-probe` 来源;基础设施异常进入 `infraError` 和采集质量,不扣小工具 JS 分。小工具未捕获异常计入 `jsErrorCount`,前 3 次形成 `jsFatal` 事故证据。该事件名不单独证明页面已经崩溃。默认 JS 项权重为 0.5,`blocking.jsFatal: false`;功能是否受影响结合真实操作和故障证据判断。
104
+
105
+ ### 3.4 FCP / LCP
106
+
107
+ document-start 注册 `paint` 与 `largest-contentful-paint` 的 buffered PerformanceObserver,FCP 精确匹配 `first-contentful-paint`,LCP 取最新合格候选。独立的 `audit:paint` 事件立即上报,不受预热和 1Hz 快照限制。时钟为 guest navigation,不包含宿主 ZIP 上传、解压和 Runtime 建连。
108
+
109
+ 首次输入(包含模拟器转发的输入)、滚动、隐藏或 pagehide 收口 LCP。停止审核时插件向当前 guest 请求 `audit:finish-paint`,guest 排空 `takeRecords()` 后返回同一 requestId,插件最多等待 150ms;超时仍保留候选原值和未收口标记,LCP 不计分。reload 清空首屏指标,忽略旧 guest Realm 的消息。
110
+
111
+ 报告包含 `paintTiming`:`fcpMs/lcpMs`、`fcpStatus/lcpStatus`(pending/measured/unsupported/hidden)、`lcpFinalized` 与 `finalizeReason`。不支持、首屏前已隐藏或没有有效绘制时为 N/A,不伪造 0。Canvas/WebGL 内容不属于 LCP,不能把 DOM 标题的 LCP 解释为游戏可玩时间。
112
+
113
+ 评分参考 [Lighthouse 曲线](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring):FCP 的 p10/median 为 1800/3000ms,LCP 为 2500/4000ms,分别映射约 90/50 分,默认权重 4/10。[FCP](https://web.dev/articles/fcp) 与 [LCP](https://web.dev/articles/lcp) 的良好边界用于初始参考,桌面模拟器结果不等同移动端或完整 Lighthouse 测试。配置项 `loadingScoreRanges` 可由 Apollo 调整控制点。
114
+
115
+ ## 4. 会话与评分
116
+
117
+ ### 4.1 会话状态机
118
+
119
+ `AuditSession` 的状态为:
120
+
121
+ ```text
122
+ idle → arming → collecting → finished
123
+ │
124
+ └──────────────→ aborted
125
+ ```
126
+
127
+ 1. Mars 审核动作启动内容前,Runtime 先注册 document-start 探针、刷新 SW 注入载荷并绑定采集监听;然后由同一动作调用 `simulator.loadFiles`。加载期、首屏和审核交互因此属于同一个会话,不为评测额外 reload。
128
+ 2. 会话总时钟由 Apollo 的 `evaluationDurationMs` 控制,默认 `120000` 毫秒。时钟从开始装载内容起计算,包含 guest 启动和预热,避免因页面迟迟未 ready 而无限采集。
129
+ 3. Runtime 收到 `audit:ready` 后进入 `arming`,预热样本不进入评分时序;预热后的第一个样本进入 `collecting`。
130
+ 4. Mars 会话中,鼠标离开整个模拟器容器(含留白与评测面板)后启动 `autoStopOnLeaveMs` 倒计时,默认 30000 毫秒。到期、点击“停止评测”或达到 `evaluationDurationMs` 均执行正常收口。正常完整收口需共同满足有效时长、样本数、真实输入和最低覆盖度,且无评测基础设施异常,才能得到 `scored`;各指标另按自身样本条件出分或 `na`。证据不足时为 `unscorable`,保留已有指标并上报。停止仅结束采集,不代表功能或审核通过。
131
+ 5. 评测期收到手动 reload 的 `beforeLoad` 事件时,旧 guest 的时间线、离散事件和饱和状态全部丢弃,等待新 Realm 再次 `ready` 后重新预热。
132
+ 6. 资源守卫触发、guest 失联或内容装载失败时,当前会话才进入 `aborted`,报告为 `unscorable`;Runtime/guest 主线程卡死由宿主 watchdog 生成 `loadFailed`。
133
+
134
+ 面向已运行内容的手动 `audit.start` 保留 reload 语义,用于调试或主动重测;Mars 正常审核流不依赖它。会话结束时恢复普通会话的堆熔断配置。
135
+
136
+ ### 4.2 真实操作与队列隔离
137
+
138
+ 审核员平均约一分钟处理一个队列项,按现有节奏实际操作主要功能并检查结果,随后可移向 Mars 审核表单。离开整个模拟器容器(含留白与评测面板) 30 秒后,模拟器自动结束采集、生成报告并触发 APM 上报,无需额外点击停止。默认出分门槛为有效采集 30 秒、30 个样本、至少 1 次真正到达 guest 的输入,以及覆盖度不低于 60%,并要求采集完整、无评测基础设施异常。默认预热 3 秒不计入有效时长;总时钟最长 120 秒。证据不足只影响本轮报告,不阻塞审核提交或下一项。
139
+
140
+ 离开检测规则:
141
+
142
+ - 整个模拟器宿主容器构成评测区域,包括手机画布、蓝色背景留白、手机与面板间隙、体验评测面板。监听 `shadowHost.host`,这些内部区域之间移动、guest iframe 内部操作均不触发倒计时。Mars 外层的审核原因、通过/拒绝和提交表单不属于该区域。
143
+ - 探针就绪后监听稳定容器。进入过区域或记录到真实输入后,才启用离开倒计时;仅等待包启动不视为已完成体验。
144
+ - 倒计时内移回任一区域即取消;再次离开重新计时。鼠标在外但探针仍收到真实输入时延后结束,避免键盘操作游戏时误停;普通采样不延长倒计时。
145
+ - 面板显示剩余秒数。停止、换包、guest reload、异常和销毁会清理计时器;已结束的会话不因鼠标移回而自动重开。
146
+ - Apollo 审核策略配置 `autoStopOnLeaveMs`,单位毫秒,默认 30000,设为 0 可关闭。普通非 Mars 会话不启用此机制。
147
+
148
+ 输入事件以 200ms 窗口去重,只保留次数和时间,不采集键值、输入内容、坐标或页面文本。模拟器转发的合成输入可能 `isTrusted === false`,因此不使用该属性过滤所有操作。计数提供输入到达证据,功能是否正确完成仍由审核员按 SOP 判断;系统不将停止动作或输入次数记成业务完成。
149
+
150
+ 每个队列项结束后完整重载模拟器,创建新的连接和评测会话。当前包内的手动 reload 使用新的 `attempt_no`,重置样本、事故和交互计数;不同 Realm 的时序不能拼接计算斜率。
151
+
152
+ 离开自动结束和 APM 上报由模拟器内部完成,不依赖 Mars 调用接口。若在倒计时到期前直接刷新或销毁,不能保证取得正常完成报告;宿主按会话中断处理并尽力冲刷已收到的数据。`simulator.audit.stop()` 可用于宿主主动结束,RPC 返回仅表示请求已处理,报告通过 `auditReport` 异步返回。报告等待或上报失败不能阻塞队列。
153
+
154
+ ### 4.3 四步规则流水线
155
+
156
+ 规则只消费 `AuditInput`,不访问 DOM、window 或远程接口。执行在 Runtime 浏览器内:
157
+
158
+ ```text
159
+ deriveMetrics → healTags / isApplicable → evaluateItems → buildReport
160
+ ```
161
+
162
+ | 步骤 | 结果 |
163
+ | --- | --- |
164
+ | `deriveMetrics` | P95 水位、稳定期最小二乘堆斜率、帧率分位数、静态包体指标 |
165
+ | `healTags` / `isApplicable` | 包扫描与实采能力合并,得到 appType、适用指标和 NA 原因 |
166
+ | `evaluateItems` | 每项的实测值、阈值、分数、pass/fail/na |
167
+ | `buildReport` | 覆盖度、硬卡口、总分、报告三态 |
168
+
169
+ JS 堆峰值取有效采样的最大读数,P95 单独进入 APM。Runtime 和 guest 的堆统计范围不保证一致,因此峰值使用 guest 原始读数。该值不代表进程总内存或小工具独占内存。
170
+
171
+ JS 堆变化速率丢弃前 20% 样本,对其余有效样本做线性回归;至少 30 个样本才提供数值。默认权重为 0,仅观察并上报,不单独进入硬风险。图片解码、缓存预热和 GC 均可能造成短时增长;复测需观察重复操作后的多个时间窗口、净增长和回落,不能凭斜率认定泄漏或 OOM。
172
+
173
+ ### 游戏形态与渲染能力
174
+
175
+ 框架指纹覆盖 PixiJS、Phaser、Cocos、Unity、Three.js、Babylon.js、PlayCanvas、LayaAir、Egret。报告分别保留业务形态、包内框架和命中依据:
176
+
177
+ - 文件名命中记为候选;同一脚本同时命中框架标识和特有 API 记为强指纹。Unity/Cocos 的完整构建结构也构成强指纹。
178
+ - 支持 `index-xxx.js` 等改名脚本、HTML 内联脚本和同包多个框架。框架名称不能直接证明游戏玩法或 2D/3D 维度。
179
+ - Mars ZIP 和 `loadFiles` 的原始内容由远程 Runtime 提供给评测插件,源码扫描不执行包内代码。每包最多扫描 16MiB,每文件最多 8MiB,大文件取首尾片段;截断或遇到压缩脚本时记录扫描不完整。未命中表示没有找到已支持的指纹,不能证明未使用框架。
180
+ - 面板展示“包内框架”,候选标注“待确认”;报告保留包内文件位置。APM 仅上报框架 ID、证据规则和扫描完整性,源码及文件位置不进入 APM。这部分随远程资源发布,Mars 接入参数保持现有契约。
181
+
182
+ 框架版本、混淆、模块裁剪以及包外动态加载会影响覆盖率;自写渲染器需要独立的绘制证据,不能依靠框架清单完成识别。
183
+
184
+ 自动推断 3D 游戏需要两组独立证据同时成立:
185
+
186
+ 1. **游戏引擎构建指纹**:Unity 同一构建名的 `loader.js + framework.js + wasm + data` 四件套(支持 br/gz/unityweb);或 Cocos 引擎脚本、启动配置与资源包结构共同命中。
187
+ 2. **三维运行证据**:实际 WebGL 绘制同时使用透视投影/MVP 和深度测试,且至少三个采样窗口中有新增绘制。按 context/program 隔离统计,正交 2D 相机、单次初始化和零顶点绘制不计为该证据。
188
+
189
+ 面板对自动推断显示“推测形态”,保留引擎和依据供排查。Unity/Cocos 支持 2D/3D,单个引擎名称不能确定维度。WebGL、WASM、Three.js/Babylon/PlayCanvas、显存占用只补充能力,不能单独把工具类归为游戏。业务已申报的 2D、3D 类型保持其申报类型;没有足够证据时使用工具类预算,业务玩法仍需结合实际操作确认。包内媒体文件作为资源特征记录,业务形态仍使用工具类或游戏类。正交 3D、Uniform Buffer、自定义/模块化引擎以及改名构建可能无法自动识别,需要进一步补齐证据,不能宣称通用游戏语义识别。
190
+
191
+ 依据:[Unity Web 构建产物](https://docs.unity.com/en-us/engine/6000.5/manual/platform-specific/webgl/building-distribution/building)、[Cocos 2D/3D 引擎说明](https://docs.cocos.com/creator/3.8/manual/en/)。多证据分类是本方案的保守推断规则,不属于引擎官方游戏分类标准。
192
+
193
+
194
+ ### 4.4 报告三态和卡口
195
+
196
+ | 状态 | 条件 |
197
+ | --- | --- |
198
+ | `scored` | 会话完整,时长、样本、真实操作与覆盖度均满足,且无评测基础设施异常 |
199
+ | `unscorable` | 采集截断、基础设施异常或有效证据不足,需要人工复核 |
200
+ | `loadFailed` | 包无法启动或 Runtime/guest 主线程卡死,不出分 |
201
+
202
+ 包体积按 ZIP 压缩体积、解压后体积、最大单图、JS 资源排列,权重分别为 5、3、1、1。`loadZip` 以 ZIP 二进制 `byteLength` 为压缩体积,解析结果的文件字节数之和为解压体积。`size.zip` 控制压缩体积的 100/60/0 分线(默认 2/5/10MiB),`profiles.<形态>.size` 控制解压体积;原始字节数、分项分数及权重均进入 APM 结果摘要。
203
+
204
+ 默认硬风险包括显存预算告警、`webglcontextlost` 和 guest crash;普通 JS 异常按低权重评分。峰值堆、ZIP 压缩体积、解压总体积、图片与脚本体积进入评分,不作为加载安全上限;真正无法加载的大包由既有 `package-limits` 处理。
205
+
206
+ 总分按已采到且适用的指标权重归一化。覆盖度为“已采到适用项权重 / 当前能力集适用项权重”;能力不适用项不进分母,适用但缺数据的项进分母、不进分子。上下文丢失与 guest 失联是稳定性风险,不自动等同于真实 OOM 或进程崩溃。风险命中时面板显示“需复核”,总分同步使用风险色。
207
+
208
+ `enforce` 决定卡口是否参与准入:首期默认 `false`,所有命中都完整展示但只告警。自动准入必须按如下顺序消费报告:
209
+
210
+ ```text
211
+ status === "scored"
212
+ → quality.complete === true
213
+ → blocking.passed === true
214
+ → 再使用总分与分项建议
215
+ ```
216
+
217
+ ## 5. 模拟器接入
218
+
219
+ ### 5.1 注入扩展点
220
+
221
+ `MiniAppSimulator` 提供:
222
+
223
+ ```ts
224
+ registerInjection(module)
225
+ unregisterInjection(id)
226
+ refreshInjection()
227
+ setHeapLimitOverride(bytes | false | null)
228
+ openSidePanel(width)
229
+ closeSidePanel()
230
+ ```
231
+
232
+ `security` 注入是保留模块,不能被注册覆盖或卸载。每次新增评测探针后,注册表都把 `security` 移到末位,确保安全 seal 在探针安装之后执行;否则原型被锁定会让 hook 静默失效。
233
+
234
+ ### 5.2 远程协议
235
+
236
+ 协议保持 v8,通过 capability 协商新增 `audit`,不提高主协议版本。Runtime 的五个 RPC 为:
237
+
238
+ | RPC | 作用 |
239
+ | --- | --- |
240
+ | `audit.start` | 对已运行内容手动重测;注册探针后 reload 并启动采集,返回成功、`PLUGIN_UNAVAILABLE`、`POLICY_DISABLED` 或 `NO_CONTENT` |
241
+ | `audit.stop` | 正常停止当前会话并结算评分 |
242
+ | `audit.status` | 返回当前阶段和 sessionId |
243
+ | `audit.report` | 获取最后一份报告 |
244
+ | `audit.setPolicy` | 设置当前 Runtime 会话的策略覆盖值 |
245
+
246
+ `audit` capability 表示 Runtime 认识该协议。插件作为独立浏览器 ESM 资源发布,地址由 release 的 `auditPluginUrl` 注入 bootstrap;Runtime 主包只保留动态 `import(/* @vite-ignore */ url)`,远程协议自行定义序列化报告契约,不依赖插件包的模块或类型。仅 `businessLine === mars` 在建连时请求该资源以渲染空态面板,普通远程会话既不请求资源,也不包含探针、规则和面板代码。插件实际不可用时,面板和 `audit.start` 都显式呈现 `PLUGIN_UNAVAILABLE`。
247
+
248
+ ### 5.3 Mars 审核面板
249
+
250
+ 分类汇总与明细使用同一顺序:运行性能 → 包体积 → 稳定性 → 内存。稳定性当前只有 JS 异常,独立聚合得分并通过 APM 的 `score_stability` 上报;内存分类不包含 JS 异常。硬风险提示单独保留。
251
+
252
+ 长任务使用低权重种子曲线:最长一次 ≤500ms 为 100 分,500~3000ms 线性递减,≥3000ms 为 0 分,权重为 1。例如 567ms 为 97 分、1000ms 为 80 分。原始次数与最长时长继续采集,Apollo 可整体覆盖阈值与权重;这里的评分容忍区间不改变浏览器 Long Task 的采集定义。
253
+
254
+ Runtime 在 `businessLine === mars` 建连后立即准备面板。它通过 `openSidePanel(320)` 在设备画布右侧创建槽位,面板使用原生 DOM 和独立 Shadow DOM,不依赖 Vue,也不运行在 guest 小工具 iframe 内。
255
+
256
+ 侧栏与设备画布并排,窄挂载区时侧栏最多占一半,设备画布仍保留至少一半宽度。审核页的挂载容器为手机宽度加 320px 侧栏预留空间;playground 审核模式也相应扩展容器,避免手机壳缩成细线。
257
+
258
+ 面板阶段:
259
+
260
+ | 阶段 | 展示 |
261
+ | --- | --- |
262
+ | `idle` | 等待小工具启动的空态 |
263
+ | `arming` | guest 启动与预热状态;可点击“停止评测” |
264
+ | `collecting` | 已采时长 / 最长时长、样本数、真实操作次数、“停止评测”与离开倒计时 |
265
+ | `done` | 总分、形态、覆盖度、卡口、逐项指标和采集质量 |
266
+ | `failed` | 启动或采集失败原因 |
267
+
268
+ Mars 的 ZIP 先解析并等待审核页“开始审核”动作;动作开始时 Runtime 先准备采集,再加载内容并自动进入评测。普通业务线不创建常驻面板,也不会加载评测插件,但仍可通过 RPC 手动评测。
269
+
270
+ 评测面板保留“停止评测”操作,也支持离开区域自动结束;报告中的使用证据为 `interactionCount`、`firstInteractionAt`、有效时长、样本数与采集完整性。规则版本为 `1.2.0`,APM 分析按 `rules_version` 区分出分条件,不用停止方式推断核心流程完成。
271
+
272
+ ### 5.4 自动截图
273
+
274
+ Mars 业务方可通过公开 API 控制自动截图:
275
+
276
+ ```ts
277
+ await simulator.setAutoScreenShot(false);
278
+ ```
279
+
280
+ 评测数据采集和结算独立于自动截图。关闭截图不影响指标上报;截图失败属于附件证据失败,不能据此中断评测或把小工具判成异常。
281
+
282
+ ## 6. 评测熔断与卡死收口
283
+
284
+ 普通会话继续使用原始 `runtimeLimits.maxHeapUsedBytes`。评测启动后,`auditModeHeapLimitBytes` 临时覆盖 guest 资源守卫;默认值为 3 GiB,给高内存包留出完整测量窗口。会话结束恢复原配置,后续普通 reload 回到常规熔断线。
285
+
286
+ 资源守卫仍触发时,guest iframe 会被物理替换,探针的 Realm 不再存在。Runtime 通过 `notifyGuardTriggered` 作废会话,最终报告为 `unscorable`,并保留中断原因。
287
+
288
+ 用户代码若在加载期同步阻塞,guest 与 Runtime 可能同时无法运行定时器。此时宿主页的 watchdog 是唯一可继续执行的计时器:
289
+
290
+ 1. Mars 自动会话在加载内容前、手动 `audit.start` 在 reload 前生成同包的 `loadFailed` 报告模板;两条路径都通过 `auditStarted` 事件交给宿主。
291
+ 2. 宿主每次收到 `auditProgress` 都重置 12 秒静默计时器;连续 12 秒没有进度时用模板发布 `auditReport`,状态为 `loadFailed`,错误码为 `RUNTIME_HUNG`。
292
+ 3. 宿主另保留 `evaluationDurationMs + 35s` 的总期限,防止持续空进度但迟迟不结算。
293
+ 4. 宿主缓存该终态;迟到的 Runtime 事件不能覆盖它。
294
+
295
+ 因此“卡死”与普通 `auditFailed` 文本错误不同:它有可汇总的状态、错误码、质量字段和阻断失败项。
296
+
297
+ Runtime 与 guest 共用线程阻塞时,Runtime 内侧面板可能无法更新。Mars 外层队列必须消费宿主的 `auditReport(source=host-watchdog)`,展示失败原因并允许保存和切换下一项。
298
+
299
+ ## 7. Apollo 策略
300
+
301
+ 服务端独立配置 key 为 `miniappSimulatorAuditPolicies`。每项规则的 `strategy` 是完整 `AuditPolicy`:
302
+
303
+ ```ts
304
+ interface AuditPolicyRule {
305
+ priority: number;
306
+ scope: {
307
+ appIds?: string[];
308
+ userIds?: string[];
309
+ businessLines?: string[];
310
+ };
311
+ strategy: AuditPolicy;
312
+ }
313
+ ```
314
+
315
+ `priority` 必须为唯一的非负安全整数。scope 的 `appIds`、`userIds`、`businessLines` 任一维度命中即可生效;命中多条时选择 priority 最大的一份 `strategy`。
316
+
317
+ 外层 JSON、scope 或 priority 结构无法解析时,服务端保留 Apollo 的 last-known-good。服务端不检查 `strategy` 内的 profile、阈值、权重或开关。CSP、性能、JSAPI 与包体限制仍使用各自既有语义,不受 audit priority 影响。
318
+
319
+ Runtime 把服务端返回的 audit 对象作为完整策略直接交给规则包,不做字段级业务校验。策略可调 profile 预算、评分区间、体积曲线、权重、最低覆盖度、最长审核时长、卡口开关和评测熔断线。
320
+
321
+ 规则包内置完整默认策略,默认 `enforce: false`。远端配置命中后整份覆盖本地默认;未命中或未取得 audit 配置时使用本地默认。Apollo 调整参数;采集代码、评分公式和指标 ID 随代码版本发布。
322
+
323
+ ### 7.1 完整 Apollo 示例
324
+
325
+ ```json
326
+ [
327
+ {
328
+ "priority": 0,
329
+ "scope": { "businessLines": ["mars"] },
330
+ "strategy": {
331
+ "policyVersion": "phase1-zip-priority-v9-2026-10-29",
332
+ "enforce": false,
333
+ "minCoverage": 0.6,
334
+ "defaultProfile": "tool",
335
+ "profiles": {
336
+ "tool": {
337
+ "peakHeapMB": 32, "textureMB": 48, "canvasMB": 32,
338
+ "imageMB": 48, "wasmMB": 32,
339
+ "size": { "idealMB": 2, "acceptMB": 4, "maxMB": 9 }
340
+ },
341
+ "game2d": {
342
+ "peakHeapMB": 96, "textureMB": 96, "canvasMB": 64,
343
+ "imageMB": 96, "wasmMB": 64,
344
+ "size": { "idealMB": 6, "acceptMB": 12, "maxMB": 24 }
345
+ },
346
+ "game3d": {
347
+ "peakHeapMB": 200, "textureMB": 48, "canvasMB": 32,
348
+ "imageMB": 32, "wasmMB": 64,
349
+ "size": { "idealMB": 12, "acceptMB": 20, "maxMB": 32 }
350
+ }
351
+ },
352
+ "weights": {
353
+ "peakHeap": 6, "heapGrowth": 0, "texWatermark": 6,
354
+ "canvasBuffer": 3, "decodedImages": 4, "wasmMemory": 3,
355
+ "workerCount": 2, "jsError": 0.5, "fps": 5, "longTask": 1,
356
+ "fcp": 4, "lcp": 10,
357
+ "zipSize": 5, "unpackedSize": 3, "singleImage": 1, "uncompressedJs": 1
358
+ },
359
+ "scoreRanges": {
360
+ "heapGrowth": { "good": 4, "bad": 16, "threshold": 8 },
361
+ "workerCount": { "good": 4, "bad": 12, "threshold": 4 },
362
+ "jsError": { "good": 0, "bad": 5, "threshold": 0 },
363
+ "fps": { "good": 50, "bad": 20, "threshold": 30 },
364
+ "longTask": { "good": 500, "bad": 3000, "threshold": 500 }
365
+ },
366
+ "loadingScoreRanges": {
367
+ "fcp": { "p10": 1800, "median": 3000 },
368
+ "lcp": { "p10": 2500, "median": 4000 }
369
+ },
370
+ "size": { "zip": { "idealMB": 2, "acceptMB": 5, "maxMB": 10 }, "singleImageKB": 300, "uncompressedJsKB": 1024 },
371
+ "blocking": {
372
+ "textureWatermark": true,
373
+ "contextLost": true, "jsFatal": false, "crash": true
374
+ },
375
+ "minSamplesForSlope": 30,
376
+ "minSamplesForScoring": 30,
377
+ "minEffectiveMs": 30000,
378
+ "minInteractionsForScoring": 1,
379
+ "evaluationDurationMs": 120000,
380
+ "autoStopOnLeaveMs": 30000,
381
+ "auditModeHeapLimitBytes": 3221225472
382
+ }
383
+ }
384
+ ]
385
+ ```
386
+
387
+ 以上是完整默认观察策略。阈值是种子值,需结合实际样本分布、内部复测与设备数据校准。
388
+
389
+ ## 8. APM 上报链路
390
+
391
+ ```text
392
+ guest 探针
393
+ → 插件 telemetry(metadata / stage / sample / paint / incident)
394
+ → Runtime audit-controller 扁平化
395
+ → MessageChannel auditTelemetry
396
+ → 宿主 Remote Client tracker
397
+ → APM 批量请求
398
+ ```
399
+
400
+ `measurement_name` 沿用 `miniapp_simulator_event`。事件包括 `miniapp_audit_session`、`miniapp_audit_paint`、`miniapp_audit_incident`、`miniapp_audit_package`、`miniapp_audit_result`,由模拟器内部自动采集和上报。运行样本仅在本地处理,评分只消费 collecting;刷新用 `attempt_no` 区分。首个 FCP 和 LCP 收口事件立即 flush,终态重复携带首屏值、状态、单项得分和阈值。
401
+
402
+ `measurement_data` 是扁平 string/number 字段。布尔语义统一用 number 0/1,原始 timeline 不塞入单条数组或大 JSON。宿主按 10 条或 10 秒批量发送,终态、页面隐藏和销毁时 flush;请求使用 `Content-Type: application/json`、`Biz-Type: apm_fe`、`Batch: true`。上报异常与小工具业务和评分隔离。
403
+
404
+ | 字段组 | 登记字段 |
405
+ | --- | --- |
406
+ | 关联 | `review_task_id`、`audit_session_id`、`attempt_no`、`simulator_session_id`、`app_id`、`package_version`、`package_hash` |
407
+ | 版本 | `policy_version`、`rules_version`、`probe_version`、`runtime_version`、`sdk_version` |
408
+ | 过程 | `stage`、`phase`、`reason_code`、`stop_reason`、`auto_stop_on_leave_ms`、`sample_seq`、`session_elapsed_ms`、`collecting_elapsed_ms` |
409
+ | 原始指标 | `heap_bytes`、`tex_bytes`、`buf_bytes`、`canvas_bytes`、`img_bytes`、`wasm_bytes`、`workers`、`fps`、长任务、JS 异常和交互次数 |
410
+ | 首屏 | `fcp_ms`、`lcp_ms`、`fcp_status`、`lcp_status`、`lcp_finalized`(0/1)、`paint_finalize_reason`、`paint_timing_source`、`score_fcp`、`score_lcp`、`fcp_threshold_ms`、`lcp_threshold_ms` |
411
+ | 质量标志 0/1 | `complete`、`sufficient`、`probe_saturated`、`blocking_passed`、`enforced`、`tag_mismatch` |
412
+ | 终态 | `status`、`failure_code`、`score_total`、分类得分、`coverage_ratio`、`effective_ms`、`sample_count`、派生指标 |
413
+
414
+ 完整字段契约保留在仓库 `packages/miniapp-simulator/docs/audit-data-collection.codex.md`。Mars 传入审核任务和包版本,用于关联队列项与评测证据;评测数据上报不依赖 Mars 提供人工审核结论。
415
+
416
+ 动态调参流程为:按形态/版本查询 APM 终态与事故 → 导出实测指标和完整策略 → 分析候选阈值 → 对比分数分布、风险命中与采集完整性 → 选取内部样本实际复测 → 发布 Apollo 完整策略。误报和漏报需要带明确复测结论的样本检验,不能由分数分布自动推断。HTTP 请求成功只证明链路发出并收到响应,APM 落入与可检索性还需实际查询确认。
417
+
418
+ ## 9. 验证范围与上线前置项
419
+
420
+ | 验证 | 证据 |
421
+ | --- | --- |
422
+ | 安全 hook | `src/probe/probe.test.ts`:返回值、this、异常、元数据透传 |
423
+ | 配对记账 | 同一 fixture:上传、覆盖写、删除、水位回落、mipmap、压缩纹理 |
424
+ | 规则 | `src/rules/rules.test.ts`:P95、稳定期回归、覆盖度、三态、卡口、饱和降级 |
425
+ | reload 会话 | `src/collector/session.test.ts` 与 `src/index.test.ts`:丢弃旧 Realm 时序并重采 |
426
+ | 协议与注入 | `tests/remote-capabilities.test.ts`:v8 capability、五 RPC、security 末位 |
427
+ | 资源拆分 | `build:remote` / `build:remote:dev` 的 `verify-remote-audit-split.mjs`:Runtime 不含探针、规则标识;独立 ESM 必含这些实现标识 |
428
+ | Runtime 卡死 | `tests/remote-audit-watchdog.test.ts`:`RUNTIME_HUNG` 结构化终态 |
429
+ | 策略 | `policy-scope.test.ts`:priority 选择整份策略、外层结构与 last-known-good 行为 |
430
+ | 真实包 | 仓库 `packages/miniapp-simulator/docs/audit-local-validation.codex.md`:20 个唯一 ZIP 的逐包操作、覆盖度、指标与三态;运行截图及摘要见在线《质量评测》主文档 |
431
+
432
+ 生产接入前确认:
433
+
434
+ - Runtime、SW 与独立 audit 插件产物已发布,release 的 `auditPluginUrl` 和协议 v8 能力可用。
435
+ - Apollo 中配置并验证完整 `miniappSimulatorAuditPolicies`;实际会话保留策略、规则和探针版本。
436
+ - Mars 传入任务/包标识,消费评测报告并展示宿主 watchdog 终态。
437
+ - 在真实队列连续处理至少 20 个包,执行可见主要操作,每包完整刷新,确认保存、切换、卡死恢复与结果追溯。
438
+ - 抓取实际 APM 请求并查询样本/事故/结果,确认字段类型和关联完整;默认保持 `enforce: false`。
439
+
440
+ ## 10. 后续能力
441
+
442
+ 1.1 期游戏增强方案保留在一期评分原文档 §15,状态为待开发、待验证。目标包括识别证据、玩法阶段、有效操作、绘制提交节奏、上下文恢复和分阶段资源变化。Electron 受控复测见在线子文档 [质量评测 · 二期:Electron 内存精测方案](https://docs.xiaohongshu.com/doc/c4506705958aeabeb8ebd8e271e85c98)。
443
+
444
+ ## 11. 一期远程模拟器的内存与显存采集局限
445
+
446
+ ### 11.1 运行环境、API 与权限边界
447
+
448
+ “远程模拟器”通过服务加载 Runtime,小工具和探针仍运行在审核员的桌面浏览器中。这里采到的是浏览器可暴露的 JS 堆读数及探针估算的资源水位,不是服务端、手机进程或 GPU 驱动的完整内存数据。手机外观、视口和 DPR 模拟不会复现手机的内存容量、WebView、GPU、驱动和系统回收策略。
449
+
450
+ **一期的基础限制是普通 Web 页面的观测能力:浏览器没有提供通用、可靠、可直接归属于单个小工具的进程总内存、真实物理显存及剩余显存查询 API。评测插件运行在页面权限内,不能因为部署了远程 Runtime 就获得操作系统或 GPU 驱动的诊断权限。**
451
+
452
+ - **浏览器暴露的数据有限。** `performance.memory` 提供受浏览器实现限制的 JS 堆读数。[`measureUserAgentSpecificMemory()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/measureUserAgentSpecificMemory) 可以在部分浏览器估算 Web 应用内存,但需要安全上下文及跨源隔离,结果口径也依赖浏览器;一期未接入,不能假定 Mars 的嵌入链路具备调用条件,更不能将其等同于完整进程或物理显存读数。
453
+ - **显存采用资源记账估算。** 页面能够观察部分 WebGL 分配和释放调用,不能直接读取驱动最终如何分配、缓存、共享和回收资源。增加采样时长、调整评分阈值或补充 hook,都不能把浏览器未暴露的信息变成精确实测值。
454
+ - **开发工具能力不等于页面 API。** DevTools/CDP、Electron 主进程与系统诊断工具运行在不同的权限和接入条件下。例如 [Electron `app.getAppMetrics()`](https://www.electronjs.org/docs/latest/api/app#appgetappmetrics) 提供应用相关进程的资源统计,[Electron Debugger](https://www.electronjs.org/docs/latest/api/debugger) 可连接调试协议;一期普通网页不能直接调用这些主进程能力。二期需要受控客户端接入,并继续验证进程、页面与资源的归属。
455
+
456
+ 需要分别记录“平台没有暴露”“当前探针尚未覆盖”“本轮数据缺失”三类边界。平台权限限制决定可观测上限;实现覆盖不足仍需补齐,不能把所有漏采都归因于浏览器,也不能因技术栈存在限制而把缺失项视为通过。
457
+
458
+ 一期用这些数据识别异常高水位、持续增长与反复操作后的资源积累,为样本排序和专项复测提供线索;它不能证明小工具没有内存泄漏,也不能据此承诺移动端不会 OOM。
459
+
460
+ ### 11.2 各项指标能测到什么
461
+
462
+ | 指标 | 当前采集口径 | 局限与正确解读 |
463
+ | --- | --- | --- |
464
+ | JS 堆 | 每秒读取 guest 的 `performance.memory.usedJSHeapSize`。面板取有效样本最大值;P95 单独上报。 | 该接口非标准、已标记弃用,支持受浏览器限制;它不是进程总内存,也不是严格归属于当前小工具的独占内存。共享堆可能混入宿主或同堆内容,独立 Worker、跨站 iframe 等又可能漏计;DOM、浏览器原生资源和 GPU 占用不能由该值完整表达。 |
465
+ | 基线与增长率 | 基线由 Runtime 在会话开始时读取;增长率丢弃前 20% 样本后,对 guest 堆时序做线性回归,默认至少需要 30 个稳定期样本。 | Runtime 基线仅作环境信息,不参与峰值计算。斜率仍受共享堆、GC 时机、缓存预热和业务阶段影响。短时正增长是疑点,不是泄漏定论,也不能线性外推 OOM 时间。 |
466
+ | WebGL 图形资源(面板“显存水位”) | 对纹理、Buffer、Renderbuffer 的已接入分配/更新/删除调用记账,按尺寸、格式、多重采样数及 mipmap 等估算字节数,再提取水位。 | 这是应用层图形资源估算,不是实际物理显存或可用显存。格式转换、驱动对齐、缓存、延迟释放、着色器与合成资源等不由账本完整覆盖;mipmap 和部分格式使用近似。压缩纹理上传字节数也不等于 GPU 驱动实际驻留量。 |
467
+ | Canvas 与图片 | 已跟踪 Canvas 按像素宽 × 高 × 4 估算;已跟踪 `img` 按自然宽 × 高 × 4 估算,DOM 移除或弱引用失效时销账。 | 不是浏览器实际后备缓冲或解码缓存总量;CSS 背景图、ImageBitmap、视频解码帧等没有完整覆盖。DOM 移除不证明底层资源即时释放,GC 与驱动回收也不与记账同步。各项可能存在共享、重叠或漏计,不能直接相加称为“小工具总内存”。 |
468
+ | WASM 与 Worker | WASM 统计被构造 hook 捕获的 `Memory.buffer.byteLength`,最多保留 64 个引用;Worker 记录被捕获的创建与显式 `terminate` 余额。 | WASM 模块内部创建/导出的 Memory 和 Worker 内资源未保证覆盖;强引用也可能延长已捕获 Memory 的存活。Worker 数量不是内存大小,自行退出等路径可能造成计数偏差。不能用这些值证明后台资源已全部释放。 |
469
+ | 探针覆盖与容量 | 探针在小工具页面启动时安装;图形、Canvas、图片注册表各有 20,000 条上限。 | 只覆盖安装成功且已接入的调用路径;未注入的子执行环境、Worker 内 OffscreenCanvas、WebGPU 等不在当前完整观测范围内。达到上限后读数可能偏低;当前显存与画布评分会标为不可用,图片等其他读数仍需结合饱和标志判断,不能把低值或 0 当作安全证明。 |
470
+ | OOM 与运行中断 | 记录堆保护触发、`webglcontextlost`、失联和 watchdog 超时等证据。 | 保护阈值是模拟器配置,不是手机 OOM 阈值。上下文丢失或失联不直接证明 OOM,也可能来自其他运行故障;真正进程退出时,最后一次采样和上报可能来不及完成。没有收到异常不等于没有异常。 |
471
+
472
+ ### 11.3 采样时长、队列与环境影响
473
+
474
+ - **一秒快照不是连续内存监测。** 主线程阻塞、后台节流会延迟采样;两次快照之间的瞬时峰值可能漏掉。采样最大值可能遗漏两次采样之间和预热期的瞬时峰值,P95 仅用于分布分析。
475
+ - **短会话不能覆盖长时累积。** 常规队列的几十秒至两分钟只能反映已执行路径;重复关卡、场景切换、持续播放和资源反复创建释放引发的问题,需要单独延长观察并重复操作。实际操作过主要功能也不代表所有重场景均已覆盖。
476
+ - **整页刷新隔离评测会话,不保证物理资源瞬间归零。** 浏览器进程、缓存、GC 与 GPU 资源回收由浏览器和系统管理;不能把下一包的读数视为独立新进程的精确占用,也不能拼接不同包的时序计算泄漏。
477
+ - **环境与探针会影响结果。** 浏览器版本、其他标签页、桌面负载和调试工具影响读数;hook、记账和采样也有自身开销。结果应在相近环境、相同操作路径和策略版本下比较,并保留重复测量差异。
478
+
479
+ ### 11.4 报告与后续复测的使用原则
480
+
481
+ 报告按“JS 堆参考值”“图形资源估算”“采集缺失/饱和/中断”解释数据,不把估算数包装成真机精确占用。已识别的不可用指标应说明原因;有数值也需要结合采集覆盖和实际场景判断。APM 按环境、包版本、策略版本和有效采样时长分组分析,不混用不同设备的内存预算。
482
+
483
+ 一期保持 `enforce: false`:内存分数和风险提示用于观察、排序与后续抽样,不阻断现有审核提交或队列流转。对持续增长、反复切换后不回落、桌面正常但移动端有反馈的包,使用固定操作路径做长会话复测,并以代表性真机验证。Electron/CDP 可补充受控环境的进程与堆诊断,不能替代真机验证,也不天然提供单个小工具精确的物理显存归因。
484
+
485
+ 浏览器能力参考:[MDN:Performance.memory](https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory) 说明接口支持和共享/独立堆的统计偏差;[MDN:WebGL best practices](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#estimate_a_per-pixel_vram_budget) 说明 WebGL 缺少可移植的系统显存容量查询,需要估算资源字节并管理预算。上述具体采集范围以一期探针实现为准。