@skillbuilder/miniapp-simulator 0.2.4 → 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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 目标与边界
4
4
 
5
- 小工具是开发者自带构建产物的 Web 内容,形态包括工具、2D/3D 游戏和音视频。评测需要在 Mars 远程模拟器中识别内存增长、图形资源高水位、运行异常、主线程卡顿和包体问题,并给审核员可解释、可复核的证据。
5
+ 小工具是开发者自带构建产物的 Web 内容,评测形态包括工具、2D 游戏和 3D 游戏。评测需要在 Mars 远程模拟器中识别内存增长、图形资源高水位、运行异常、主线程卡顿和包体问题,并给审核员可解释、可复核的证据。
6
6
 
7
7
  一期采用 Web 侧动态采集,默认 `enforce: false`,用于观察和人工复核。它不能等价替代真机 OOM 结论:浏览器 JS 堆不包含原生 RSS/PSS、GPU 驱动、媒体解码器和系统内存压力。二期将 Web 风险信号与真机进程、崩溃和机型数据关联后再校准自动卡口。
8
8
 
@@ -53,8 +53,8 @@ status === scored
53
53
 
54
54
  | 指标 | 口径 | 权重 | 当前用途 |
55
55
  | ----------- | -------------------------------------------------------- | ---: | ------------------------------- |
56
- | 峰值 JS 堆 | `performance.memory` 稳定期峰值相对会话基线 | 6 | 分数;识别异常高占用 |
57
- | 堆增长速率 | 稳定期样本最小二乘斜率,MB/min | 7 | 分数 + 硬风险,默认阈值 8MB/min |
56
+ | 峰值 JS 堆 | guest `performance.memory` 有效样本最大值 | 6 | 分数;识别异常高占用 |
57
+ | JS 堆变化速率 | 排除前 20% 样本后的最小二乘斜率,MB/min | 0 | 观察、上报和复测选样,不单独判定泄漏 |
58
58
  | 显存水位 | WebGL texture/buffer/renderbuffer 存活字节配对记账的 P95 | 6 | 分数 + profile 硬风险 |
59
59
  | Canvas 缓冲 | 存活 canvas 像素后备缓冲峰值 | 3 | 分数 |
60
60
  | 解码图片 | 存活图片像素字节峰值 | 4 | 分数 |
@@ -85,15 +85,20 @@ FCP/LCP 使用 Lighthouse mobile 的对数正态评分曲线作为种子曲线
85
85
 
86
86
  不支持 API、首屏前已隐藏或没有合格绘制时显示 N/A,不以 0ms 补齐。Canvas/WebGL 画面不属于 LCP 主内容,游戏中的 DOM 标题也不能证明游戏核心场景已经可用;可玩性由审核员通过实际操作检查。FCP 首次实测和 LCP 收口独立触发 APM flush,即使有效时长不足,也保留首屏数据。看板按 `policy_version`、设备/环境和形态分组,使用已收口且实测的 LCP 计算 P75,不混入缺失或失效 attempt。
87
87
 
88
- 运行过程相对权重为 FPS 5 > 长任务 1 > JS 异常 0.5。JS 异常完整累计并保留代表性事故证据,默认 `blocking.jsFatal: false`。错误数量不直接证明功能损坏;功能是否受影响需结合真实操作反馈、失联/崩溃等证据判断。APM 分别上报 `fps_median`、`fps_p5`,以及三项的实际得分和权重,按策略版本分析。
88
+ 运行过程相对权重为 FPS 5 > 长任务 1 > JS 异常 0.5。JS 异常完整累计并保留代表性事故证据,默认 `blocking.jsFatal: false`。错误数量不直接证明功能损坏;功能是否受影响需结合真实操作反馈、失联/崩溃等证据判断。APM 使用 `name=fps_median/fps_p5` 和 `value` 记录帧率,单位明确为 fps;得分和权重直接填写 `score/weight`,按策略版本分析。
89
89
 
90
90
  ### 静态包
91
91
 
92
- | 指标 | 口径 | 权重 |
93
- | ---------- | -------------------------- | ---: |
94
- | 解压总体积 | ZIP 内全部文件解压后字节和 | 4 |
95
- | 最大单图 | 单个图片文件最大字节数 | 3 |
96
- | 未压缩 JS | 单个未压缩脚本最大字节数 | 3 |
92
+ | 指标 | 口径 | 权重 |
93
+ | --- | --- | ---: |
94
+ | ZIP 压缩体积 | `loadZip` 收到的 ZIP 二进制 `byteLength` | 5 |
95
+ | 解压总体积 | ZIP 内全部文件解压后字节和 | 3 |
96
+ | 最大单图 | 单个图片文件最大字节数 | 1 |
97
+ | 未压缩 JS | 单文件超过 `uncompressedJsKB` 的 JS 字节数之和 | 1 |
98
+
99
+ ZIP 使用统一评分线:≤2MiB 得 100 分,5MiB 得 60 分,≥10MiB 得 0 分,两个区间内线性插值;由 `strategy.size.zip` 配置。解压体积由 `strategy.profiles.<形态>.size` 独立配置,工具类默认 2/4/9MiB,2D 游戏 6/12/24MiB,3D 游戏 12/20/32MiB,分别对应 100/60/0 分。上述数值为产品初始预算,按 APM 实测分布校准。
100
+
101
+ 面板按 ZIP、解压体积、单图、JS 顺序展示;包体权重合计 10,四项由 `strategy.weights` 配置。APM 使用 `name=zip_size/unpacked_size`,分别直接填写 `size`、`score`、`weight`。MiB 为 1024² 字节,评分使用原始字节数,面板显示时取小数。
97
102
 
98
103
  包体指标用于质量排序。无法解析或超过加载安全限制的 ZIP 由加载链路返回 `loadFailed`,不生成伪分数。
99
104
 
@@ -124,9 +129,10 @@ source: host-watchdog
124
129
  | `tool` | 32 | 48 | 32 | 48 | 32 | 2 / 4 / 9 |
125
130
  | `game2d` | 96 | 96 | 64 | 96 | 64 | 6 / 12 / 24 |
126
131
  | `game3d` | 200 | 48 | 32 | 32 | 64 | 12 / 20 / 32 |
127
- | `video` | 180 | 96 | 64 | 64 | 32 | 4 / 8 / 16 |
128
132
 
129
- 形态由静态指纹和运行能力共同推断;申报形态不一致时保留 `tagMismatch` 供人工复核。`game2d` 和 `video` 样本不足,不开启自动卡口。
133
+ 形态由静态指纹和运行能力共同推断;申报形态不一致时保留 `tagMismatch` 供人工复核。`game2d` 样本不足,不开启自动卡口。包内媒体文件作为资源特征记录,业务形态仍使用工具类或游戏类。
134
+
135
+ 框架指纹单独记录 PixiJS、Phaser、Cocos、Unity、Three.js、Babylon.js、PlayCanvas、LayaAir、Egret。文件名为候选,脚本标识与特有 API 共同命中或完整构建结构命中为强指纹;支持改名脚本及多个框架,面板显示包内框架,APM 保留命中规则与扫描完整性。指纹用于识别技术栈,业务玩法及 2D/3D 维度需要独立证据。
130
136
 
131
137
  ## 策略配置
132
138
 
@@ -138,7 +144,7 @@ source: host-watchdog
138
144
  "priority": 0,
139
145
  "scope": { "businessLines": ["mars"] },
140
146
  "strategy": {
141
- "policyVersion": "phase1-fps-priority-v6-2026-10-23",
147
+ "policyVersion": "phase1-zip-priority-v9-2026-10-29",
142
148
  "enforce": false,
143
149
  "minCoverage": 0.6,
144
150
  "defaultProfile": "tool",
@@ -157,14 +163,9 @@ source: host-watchdog
157
163
  "peakHeapMB": 200, "textureMB": 48, "canvasMB": 32,
158
164
  "imageMB": 32, "wasmMB": 64,
159
165
  "size": { "idealMB": 12, "acceptMB": 20, "maxMB": 32 }
160
- },
161
- "video": {
162
- "peakHeapMB": 180, "textureMB": 96, "canvasMB": 64,
163
- "imageMB": 64, "wasmMB": 32,
164
- "size": { "idealMB": 4, "acceptMB": 8, "maxMB": 16 }
165
166
  }
166
167
  },
167
- "size": { "singleImageKB": 300, "uncompressedJsKB": 1024 },
168
+ "size": { "zip": { "idealMB": 2, "acceptMB": 5, "maxMB": 10 }, "singleImageKB": 300, "uncompressedJsKB": 1024 },
168
169
  "minSamplesForSlope": 30,
169
170
  "minEffectiveMs": 30000,
170
171
  "minSamplesForScoring": 30,
@@ -173,13 +174,14 @@ source: host-watchdog
173
174
  "autoStopOnLeaveMs": 30000,
174
175
  "auditModeHeapLimitBytes": 3221225472,
175
176
  "weights": {
176
- "peakHeap": 6, "heapGrowth": 7, "texWatermark": 6,
177
+ "peakHeap": 6, "heapGrowth": 0, "texWatermark": 6,
177
178
  "canvasBuffer": 3, "decodedImages": 4, "wasmMemory": 3,
178
179
  "workerCount": 2, "jsError": 0.5, "fps": 5, "longTask": 1,
179
180
  "fcp": 4, "lcp": 10,
180
- "unpackedSize": 4, "singleImage": 3, "uncompressedJs": 3
181
+ "zipSize": 5, "unpackedSize": 3, "singleImage": 1, "uncompressedJs": 1
181
182
  },
182
183
  "scoreRanges": {
184
+ "heapGrowth": { "good": 4, "bad": 16, "threshold": 8 },
183
185
  "workerCount": { "good": 4, "bad": 12, "threshold": 4 },
184
186
  "jsError": { "good": 0, "bad": 5, "threshold": 0 },
185
187
  "fps": { "good": 50, "bad": 20, "threshold": 30 },
@@ -190,7 +192,6 @@ source: host-watchdog
190
192
  "lcp": { "p10": 2500, "median": 4000 }
191
193
  },
192
194
  "blocking": {
193
- "heapGrowthMBPerMin": 8,
194
195
  "textureWatermark": true,
195
196
  "contextLost": true,
196
197
  "jsFatal": false,
@@ -216,6 +217,6 @@ source: host-watchdog
216
217
  - 12 秒进度静默看门狗、3GiB 熔断和会话中断收口;
217
218
  - 20 个本地 ZIP 的逐包真实交互结果,详见 [audit-local-validation.codex.md](./audit-local-validation.codex.md)。
218
219
 
219
- 一期接入 Mars 队列时,模拟器内部采集的评测数据统一上报 APM:审核任务、应用、包版本、策略/规则/探针版本、开始/首次操作/结算时间、1 Hz 指标时序、FCP/LCP 首屏事件、包体摘要、交互次数、三态终态和硬风险。事件契约、异常收口和指标校准流程详见 [audit-data-collection.codex.md](./audit-data-collection.codex.md)。
220
+ 一期接入 Mars 队列时,模拟器内部采集的评测数据统一上报 APM:审核任务、应用、包版本、策略/规则/探针版本、开始/首次操作/结算时间、FCP/LCP 首屏事件、包体摘要、交互次数、三态终态和硬风险。事件契约、异常收口和指标校准流程详见 [audit-data-collection.codex.md](./audit-data-collection.codex.md)。
220
221
 
221
222
  二期输入:真机 RSS/PSS、GPU 驱动内存、媒体解码内存、系统内存告警、进程终止与崩溃原因、机型/系统/WebView 版本。通过同包同脚本的 Web 与真机对照,计算误报/漏报和分机型阈值,再决定哪些硬风险可以从“需复核”升级为自动卡口。
@@ -4,7 +4,7 @@
4
4
 
5
5
  本文覆盖一期远程模拟器评测及其 1.1 期游戏增强。1.1 期的识别模型、专项指标、审核流程和验收要求见 §15,实施状态为待开发、待验证;当前已实现的采集与默认策略见 [一期评分与采集设计](./audit-score-design.codex.md)。
6
6
 
7
- 小工具形态跨度极大:传统工具类(算命、计算器)、2D/3D 游戏、视频类并存,产物由开发者自己的
7
+ 小工具的评测形态包括工具类(算命、计算器)、2D 游戏和 3D 游戏,产物由开发者自己的
8
8
  构建链路产出(webpack / vite / Unity / Cocos),不在平台控制范围内。线上频繁出现 Android / iOS
9
9
  侧 OOM,问题直接暴露给用户。
10
10
 
@@ -13,7 +13,7 @@
13
13
  目标:
14
14
 
15
15
  - 在**远程模拟器**链路上提供按需触发的体验评测能力,产出评分与优化建议。
16
- - 优先解决 OOM:建立内存、显存、泄漏斜率等此前完全没有覆盖的指标。
16
+ - 优先解决 OOM:建立内存、显存及 JS 堆变化趋势指标。
17
17
  - 阈值与准入规则全部由 Apollo 下发,先采集、后调控,调整阈值与开启卡口都不发版。
18
18
  - 采集、打分、上报失败时静默降级,不阻塞、不抛错、不改变小工具本身的运行结果。
19
19
 
@@ -108,7 +108,7 @@ t=0 完成,不会漏任何调用**。
108
108
  ```ts
109
109
  interface MiniAppFeatureTags {
110
110
  source: "declared" | "scanned" | "merged";
111
- appType?: "tool" | "game2d" | "game3d" | "video";
111
+ appType?: "tool" | "game2d" | "game3d";
112
112
  features: string[]; // ["webgl","wasm","worker","canvas2d","audio",...]
113
113
  evidence?: Record<string, string>; // 扫到的证据(文件:特征串),仅审计用
114
114
  }
@@ -171,7 +171,7 @@ WebGL、Canvas、WASM 或某个引擎的存在,只证明技术能力。业务
171
171
  | worker | `new Worker`、`.worker.js` |
172
172
  | canvas2d | `getContext` 邻近的 `"2d"` |
173
173
  | audio | `AudioContext`、`new Audio`、包内音频文件 |
174
- | video | `<video`、包内视频文件 |
174
+ | video(资源特征) | `<video`、包内视频文件;业务形态使用工具类或游戏类 |
175
175
 
176
176
  ## 4. 评分项清单
177
177
 
@@ -199,8 +199,8 @@ OOM 的正面战场,本方案单独建类并给最高权重。
199
199
 
200
200
  | 指标 | 判定标准 | 权重 | 采集层 | Canvas 类 |
201
201
  | --- | --- | --- | --- | --- |
202
- | 峰值 JS 堆 | P95 采样 − 会话基线 ≤ profile 预算,见 §5.4 | 6 | 【obs】`performance.memory` 采样 | ✅ |
203
- | 堆增长斜率 | 稳定期 ≤ 8MB/min(泄漏检测,不需基线校正) | 7 | 时序线性回归 | ✅ |
202
+ | 峰值 JS 堆 | guest 有效采样最大值与 profile 预算比较,P95 单独上报 | 6 | 【obs】`performance.memory` 采样 | ✅ |
203
+ | JS 堆变化速率 | 排除前 20% 样本的变化趋势,仅观察并上报;不能单独认定泄漏 | 0 | 时序线性回归 | ✅ |
204
204
  | 估算显存 | 纹理 + buffer 累计 ≤ profile 预算 | 6 | 【hook】`texImage2D` / `texStorage2D` / `bufferData` / `renderbufferStorage` | ✅ |
205
205
  | Canvas 后备缓冲 | 总和 ≤ profile 预算 | 3 | 【hook】canvas width/height + `getContext` | ✅ |
206
206
  | 解码图片字节 | 总和 ≤ profile 预算 | 4 | 【hook】`load` 事件算 `naturalW × naturalH × 4` | ⚠️ |
@@ -233,9 +233,10 @@ OOM 的正面战场,本方案单独建类并给最高权重。
233
233
 
234
234
  | 指标 | 判定标准 | 权重 | 采集层 |
235
235
  | --- | --- | --- | --- |
236
- | 解压后总体积 | 按 profile 分档线性扣分,见 §9.3 | 4 | 【包】`RawParseResult.files` 求和 |
237
- | 单图资源体积 | ≤ 200KB,超限建议走 CDN | 3 | 【包】 |
238
- | 未压缩 JS | 单个 JS 未压缩且 >500KB | 3 | 【包】特征检测 |
236
+ | ZIP 压缩体积 | 2/5/10MiB 对应 100/60/0 分,区间线性评分 | 5 | 【包】`loadZip` 二进制 `byteLength` |
237
+ | 解压后总体积 | 按 profile 分档线性扣分,见 §9.3 | 3 | 【包】`RawParseResult.files` 求和 |
238
+ | 单图资源体积 | ≤300KiB 满分,≥1,200KiB 零分,中间线性评分 | 1 | 【包】 |
239
+ | 未压缩 JS | 单文件超过 1MiB 的 JS 体积求和,按 0–4MiB 线性评分 | 1 | 【包】字节统计 |
239
240
  | 使用 HTTPS | 全部请求 https | 0 | 【hook】 |
240
241
  | CSS 使用率 | 单个样式表未使用部分 ≤ 2KB | 0 | 【scan】`document.styleSheets` |
241
242
  | 定时器回收 | 卸载后无存活 timer | 0 | 【hook】`setTimeout` / `setInterval` |
@@ -250,7 +251,7 @@ OOM 的正面战场,本方案单独建类并给最高权重。
250
251
  | 内存与稳定性 | **35** | OOM 是主要矛盾,本方案新增 8 项 |
251
252
  | 体验 | 25 | 交互与视觉类检查 |
252
253
  | 包与最佳实践 | 10 | 产物不可控,静态分析权重下调但不为 0 |
253
- | 硬卡口 | 二值 | 上下文丢失 / 崩溃 / 泄漏斜率,不参与打分 |
254
+ | 硬卡口 | 二值 | 显存预算告警 / 上下文丢失 / 崩溃,独立提示风险 |
254
255
 
255
256
  ## 5. 计分模型
256
257
 
@@ -272,40 +273,20 @@ if (覆盖度 < minCoverage) → 报告标记「不可评」,不出分
272
273
 
273
274
  | 层 | 内容 | 行为 |
274
275
  | --- | --- | --- |
275
- | 硬卡口(二值) | 峰值堆 / 显存 / 解码图片超档位预算、堆增长斜率超限、WebGL 上下文丢失、崩溃或 JS fatal、包体积超限 | 不通过直接拒绝 |
276
+ | 风险提示 | 显存预算告警、WebGL 上下文丢失、崩溃;普通 JS 异常低权重评分,JS 堆变化仅观察 | 一期 `enforce: false`,供人工复核 |
276
277
  | 体验评分(100 分制) | 上述四大类加权 | 呈现给审核员与开发者,不阻断 |
277
278
 
278
279
  ### 5.3 分档预算
279
280
 
280
281
  一期当前实现按 `appType` 选择 profile。1.1 期通过业务形态、玩法维度、渲染能力和审核确认选择 `evaluationProfile`(§15.2),预算数值由 Apollo 下发;单独检测到 WebGL 不触发 `game3d` 档位。
281
282
 
282
- ### 5.4 内存口径:会话基线校正
283
+ ### 5.4 JS 堆读数与趋势
283
284
 
284
- `performance.memory` 读的是所在 V8 堆的总量,而非单个 iframe 独占。远程链路下 guest iframe 与
285
- runtime iframe 同源,默认共用同一个堆,读数天然包含模拟器自身开销。
285
+ JS 堆峰值使用 guest 有效采样的最大读数,与所选 profile 的预算比较;同组样本的 P95 单独上报。`performance.memory` 的统计范围可能包含共享堆,Runtime 与 guest 的读数范围不保证一致。Runtime 基线仅作环境信息,不能据此算出小工具独占内存。
286
286
 
287
- **不通过进程隔离解决,改为把底噪从判定值中减去。**
287
+ JS 堆变化速率使用排除前 20% 样本后的最小二乘斜率,默认至少 30 个有效样本。该项默认权重为 0,用于观察和选择复测样本。缓存预热、图片处理和 GC 都可能带来短窗口正增长;判断泄漏需要结合重复操作后的多个窗口、净增长与回落,不能由一个斜率推断必然 OOM。
288
288
 
289
- 底噪不是常数。`src/sw/service-worker-host.ts` 的 `mount()` 在把 rawFiles transfer 给 SW 之前,
290
- 会先 `cloneMountInput()` 做一份完整深拷贝(`cloneRawFiles` 即 `bytes.slice(0)`)并常驻
291
- `mountSnapshots`——用于 SW 进程被浏览器回收后恢复挂载。因此 **runtime 侧长期持有一份完整的解压
292
- 后包体,底噪随包体积线性变化**,不能用固定常数近似。
293
-
294
- 取值方式:在 **SW mount 完成之后、guest 开始加载之前**读一次 `performance.memory`。该时刻 ZIP 已
295
- 解析、快照已落地、小工具尚未执行任何代码,读数即为本次会话的真实底噪,自动包含包体积影响。
296
-
297
- ```text
298
- 会话基线 baseline = mount 完成后、guest 加载前的 usedJSHeapSize
299
- 峰值堆判定 = P95(采样序列) - baseline vs profile.peakHeapMB
300
- 堆增长斜率 = 稳定期采样序列线性回归斜率
301
- ```
302
-
303
- 两点说明:
304
-
305
- - **峰值取 P95 或上包络,不取单次 max**。`performance.memory` 呈 GC 锯齿,单次最大值受 GC 时机
306
- 影响,会导致同一个包两次评测结果漂移,审核申诉时无法自证。
307
- - **堆增长斜率不需要基线校正**。斜率是变化率,任何常数偏移求导后为 0;runtime 与 SW 在 guest
308
- 运行期不做持续分配,对斜率无贡献。§4.2 中价值最高的泄漏检测项因此天然免疫该问题。
289
+ 一秒采样可能遗漏预热期和采样间隔内的瞬时峰值。报告展示的是本次采样读数,不能替代进程内存或真机内存测量。
309
290
 
310
291
  ## 6. Apollo 配置
311
292
 
@@ -357,7 +338,7 @@ interface AuditPolicyRule {
357
338
  ```
358
339
 
359
340
  - **在线**:runtime 内直接调用,即时出分,无服务端往返。
360
- - **离线**:从 APM 导出的 `miniapp_audit_sample` 时序在 Node 中用同一包的不同版本重算,用于阈值定标。
341
+ - **离线分析**:从 APM 导出终态实测指标、得分和生效权重,按运行环境、形态与策略版本分析分布,用于阈值定标。完整 timeline 的规则重算使用本地复测样本。
361
342
 
362
343
  报告必须同时落 `policyVersion` 与 `rulesVersion`,否则线上调整阈值后历史报告无法解释,申诉时
363
344
  无从对账。
@@ -372,76 +353,42 @@ interface AuditPolicyRule {
372
353
  | `event_name` | 内容 | 用途 |
373
354
  | --- | --- | --- |
374
355
  | `miniapp_audit_session` | 会话阶段、刷新、中断和里程碑 | 队列流程与完整性 |
375
- | `miniapp_audit_sample` | 1 Hz 原始指标快照 | 时序重建与阈值回放 |
376
356
  | `miniapp_audit_paint` | FCP、LCP 候选与收口 | 首屏体验分析与评分校准 |
377
357
  | `miniapp_audit_incident` | context lost、JS fatal、熔断和失联 | 硬风险分析 |
378
358
  | `miniapp_audit_package` | 包体与静态能力聚合值 | 静态指标校准 |
379
359
  | `miniapp_audit_result` | 三态、得分、覆盖度和派生指标 | 大盘与结果分析 |
380
360
 
381
- 所有点位使用 `audit_session_id + attempt_no` 关联。timeline 不作为数组塞入单条 APM 数据,每个采样点
382
- 独立上报;宿主 tracker 负责批量发送,Runtime 卡死后仍能冲刷已经收到的样本。完整事件契约见
361
+ 所有点位使用 `audit_session_id + attempt_no` 关联。业务上报位置显式填写 `time/memory/size/score/weight/ratio/count`,额外单位的观测值明确填写 `value/unit`;状态使用 `result/stage`。本地保持 1 Hz 采集,APM 记录终态派生指标;宿主 tracker 批量发送,Runtime 卡死后仍能冲刷已经收到的记录。完整事件契约见
383
362
  [audit-data-collection.codex.md](./audit-data-collection.codex.md)。
384
363
 
385
- 六类事件均由模拟器内部采集和上报,不依赖 Mars 提供人工审核结论。指标分布、风险命中和采集完整性用于筛选候选阈值;误报和漏报通过有明确复测结论的内部样本验证。
364
+ 五类事件均由模拟器内部采集和上报,不依赖 Mars 提供人工审核结论。指标分布、风险命中和采集完整性用于筛选候选阈值;误报和漏报通过有明确复测结论的内部样本验证。
386
365
 
387
366
  ## 9. 体积分析
388
367
 
389
- ### 9.1 两把尺子:安全阀与质量线是嵌套关系,不是并列关系
368
+ ### 9.1 采集口径
390
369
 
391
- `src/sw/parse-raw.ts` 中的包限制是**安全阀**而非质量度量:
370
+ ZIP 压缩体积取 `loadZip` 收到的二进制数据 `byteLength`;解压后总体积取 `RawParseResult.files` 的文件字节数之和,复用加载链路解析结果。两种体积独立计分、独立展示、独立上报。包体积优先评价 ZIP,其次评价解压体积,再定位大图与大 JS 文件。
392
371
 
393
- ```ts
394
- maxZipBytes → 压缩后字节,默认 50MB
395
- maxSingleFileBytes → 解压后字节,默认 15MB
396
- maxScriptBytes → 解压后字节,默认 15MB
397
- // 超限行为:failLimit() → throw MiniAppSimulatorError("PACKAGE_LIMIT_EXCEEDED")
398
- ```
372
+ 评分使用原始字节数,面板 MiB 按 1024² 字节换算。解压体积表示资源展开规模,不是运行内存。JS 项统计单文件超过配置阈值的脚本体积,不据此断言源码是否经过压缩。
399
373
 
400
- 两者的定位、量级与行为完全不同:
374
+ ### 9.2 配置与上报
401
375
 
402
- | | package-limits(安全阀) | 评分体积线(质量度量) |
403
- | --- | --- | --- |
404
- | 语义 | 能不能跑 | 跑得好不好 |
405
- | 量级 | 50MB 压缩包 / 15MB 单文件 | 4MB 解压后即开始扣分(tool 档) |
406
- | 口径 | `maxZipBytes` 比**压缩后** | 比**解压后总体积** |
407
- | 触发条件 | 必须离谱到威胁浏览器才拦 | 常规偏大即扣分 |
408
- | 行为 | 二值,抛错,包加载失败 | 分档线性扣分,包正常运行 |
409
- | 配置位置 | 既有 `package-limits` Apollo key | 新增 `auditPolicy` Apollo key |
376
+ Apollo `miniappSimulatorAuditPolicies` 的 `strategy.size.zip` 配置 ZIP 评分线,`strategy.profiles.<形态>.size` 配置解压体积评分线;`strategy.weights` 配置各项权重。默认包体权重为 `zipSize: 5`、`unpackedSize: 3`、`singleImage: 1`、`uncompressedJs: 1`,合计 10。
410
377
 
411
- 一个解压后 4MB 的包压缩后约 1.5MB,距 50MB 的阀门还差三十倍——**评分线远在安全阀之内,两者
412
- 天然嵌套,正常情况下评分扣分早已发生而安全阀从未触发**。
378
+ 包体使用 `name=zip_size/unpacked_size`,原始字节数、得分和权重分别直接填写 `size`、`score`、`weight`,通过策略与规则版本关联。
413
379
 
414
- **评分阈值不得写入 package-limits**:一旦超限包加载失败,审核员连报告都看不到,等同于评测流程
415
- 自毁。两套配置必须完全独立。
380
+ 加载安全限制由 `package-limits` 配置控制,超限返回加载失败。评分预算用于质量排序,评测一期 `enforce: false`。
416
381
 
417
- ### 9.2 数据源可复用
382
+ ### 9.3 分档线性评分
418
383
 
419
- `RawParseResult` 已返回 `files: Array<{ path: string; size: number }>`(路径 + 解压后字节),
420
- 体积分析所需的全部信息(解压后总体积、类型分布、大图清单、未压缩 JS 检测)均可由此计算,
421
- **零额外解包开销**。
422
-
423
- 注意 `maxZipBytes` 比较的是压缩后体积,而质量口径应看解压后总体积(与内存和加载相关),两者
424
- 语义不同,需各自独立取数。
425
-
426
- ### 9.3 体积评分采用分档线性扣分
427
-
428
- 小工具形态跨度过大,二值判定会把 3D 游戏一刀切死,因此改为按 profile 分档线性扣分:
429
-
430
- ```text
431
- size <= idealMB → 100 分
432
- size <= acceptMB → 100 → 60 线性插值
433
- size <= maxMB → 60 → 0 线性插值
434
- size > maxMB → 0 分
435
- ```
436
-
437
- | profile | idealMB | acceptMB | maxMB |
438
- | --- | --- | --- | --- |
439
- | tool | 4 | 10 | 20 |
440
- | game2d | 10 | 25 | 40 |
441
- | game3d | 20 | 50 | 80 |
384
+ | 指标 / 形态 | idealMB / 100 分 | acceptMB / 60 分 | maxMB / 0 分 |
385
+ | --- | ---: | ---: | ---: |
386
+ | ZIP 压缩体积 / 所有形态 | 2 | 5 | 10 |
387
+ | 解压后体积 / tool | 2 | 4 | 9 |
388
+ | 解压后体积 / game2d | 6 | 12 | 24 |
389
+ | 解压后体积 / game3d | 12 | 20 | 32 |
442
390
 
443
- 上述数值为一期占位值,`enforce: false` 期间仅参与出分不参与拒绝,两周后按真实分布校准(§13)。
444
- 即便是 game3d 档的 maxMB(80MB 解压后),压缩后通常仍远低于 package-limits 的 50MB 压缩包阀门。
391
+ 小于等于 idealMB 得 100 分,idealMB 到 acceptMB 由 100 分线性降至 60 分,acceptMB 到 maxMB 由 60 分线性降至 0 分,大于等于 maxMB 得 0 分。单位为 MiB。这些是产品初始评分预算,通过真实包分布校准。
445
392
 
446
393
  ## 10. 评分面板 UI
447
394
 
@@ -693,7 +640,7 @@ function safeHook(target, key, onCall) {
693
640
 
694
641
  | 能力 | 当前实现 | 1.1 期交付 |
695
642
  | --- | --- | --- |
696
- | 识别 | 文件名引擎指纹、`tool/game2d/game3d/video`、运行期资源补标 | 业务形态、玩法维度、渲染能力、引擎、证据与置信等级分别记录 |
643
+ | 识别 | 九类框架的文件名/源码指纹、Unity/Cocos 构建指纹、`tool/game2d/game3d`、运行期资源补标 | 业务形态、玩法维度、渲染能力、引擎、证据与置信等级分别记录 |
697
644
  | 使用证据 | 有效时长、样本数、操作数与首次操作时间 | 游戏阶段、有效玩法时长、连续/离散操作、主要玩法体验记录 |
698
645
  | 流畅度 | 探针独立 rAF 节拍、最长 Long Task | 业务循环/绘制提交节奏、间隔分位数、长间隔比例、主循环疑似冻结 |
699
646
  | 资源 | 图形资源水位、Canvas、图片、WASM、Worker | 绘制调用和上传强度、分阶段水位、重复玩法后资源变化 |
@@ -708,7 +655,7 @@ function safeHook(target, key, onCall) {
708
655
 
709
656
  ```ts
710
657
  interface GameIdentification {
711
- experienceType: "tool" | "game" | "video" | "interactive-content" | "unknown";
658
+ experienceType: "tool" | "game" | "interactive-content" | "unknown";
712
659
  gameplayDimension: "2d" | "3d" | "mixed" | "unknown";
713
660
  renderModes: Array<"dom" | "canvas2d" | "webgl1" | "webgl2" | "webgpu">;
714
661
  engineCandidates: Array<{
@@ -720,7 +667,7 @@ interface GameIdentification {
720
667
  identificationVersion: string;
721
668
  scanComplete: boolean;
722
669
  reviewerConfirmed: boolean;
723
- evaluationProfile: "tool" | "game2d" | "game3d" | "video" | "unconfirmed";
670
+ evaluationProfile: "tool" | "game2d" | "game3d" | "unconfirmed";
724
671
  }
725
672
  ```
726
673
 
@@ -816,7 +763,7 @@ fetch/XHR 的 pending 和失败状态需真正接线;字段未接线时明确
816
763
 
817
764
  ### 15.6 APM 与 Apollo
818
765
 
819
- 复用 §8 的 APM 六类事件。所有事件关联 `audit_session_id + attempt_no`,增加 `audit_feature_version` 与 `identification_version`;不同指标来源、浏览器环境、策略版本、玩法和目标刷新率分组分析。
766
+ 复用 §8 的 APM 五类事件。所有事件关联 `audit_session_id + attempt_no`,增加 `audit_feature_version` 与 `identification_version`;不同指标来源、浏览器环境、策略版本、玩法和目标刷新率分组分析。
820
767
 
821
768
  | 事件 | 新增字段 |
822
769
  | --- | --- |
@@ -826,7 +773,7 @@ fetch/XHR 的 pending 和失败状态需真正接线;字段未接线时明确
826
773
  | incident | `game_phase`、`context_id`、`incident_code`、恢复间隔及事故归属来源 |
827
774
  | result | `active_play_ms`、`gameplay_interaction_count`、`hold_interaction_ms`、`core_flow_completed`、提交间隔分位数、长间隔数与分母、疑似冻结、场景资源变化及 N/A 原因码 |
828
775
 
829
- APM 的 `measurement_data` 只放数字,包括布尔 0/1;阶段、来源、引擎与原因码进入字符串维度。快照只传扁平标量,长间隔和总间隔数量都上报,便于重算比例;不传逐帧数组、代码、输入文本或坐标。重复玩法差值来自同源同场景锚点,不同场景不强行计算。
776
+ APM 业务上报显式指定公共数值字段,布尔语义使用 count=0/1;阶段、来源、引擎与原因码使用固定字符串维度。快照只传扁平标量,长间隔和总间隔数量都上报,便于重算比例;不传逐帧数组、代码、输入文本或坐标。重复玩法差值来自同源同场景锚点,不同场景不强行计算。
830
777
 
831
778
  Apollo 沿用 `miniappSimulatorAuditPolicies`,每条命中策略整份覆盖本地默认。下面展示完整 `strategy` 中拟新增的 `gameEnhancement` 段;发布配置时应把它与通用字段一起组成完整策略。字段为方案设计,需完成代码接线后生效。
832
779
 
@@ -156,10 +156,10 @@ pluginManifest(@Query() query: { businessLine?: string; protocolVersion?: string
156
156
  ### 3.6 APM 评测事件流
157
157
 
158
158
  评测数据复用现有 tracker,不新增服务端上报接口。measurement_name 保持
159
- `miniapp_simulator_event`,通过 `event_name` 区分 session、sample、paint、incident、package 和
159
+ `miniapp_simulator_event`,通过 `event_name` 区分 session、paint、incident、package 和
160
160
  result,均由模拟器内部采集和上报。
161
161
 
162
- 1 Hz timeline 按采样点拆成扁平的 `miniapp_audit_sample`,不作为数组塞入单条 `measurement_data`。
162
+ 1 Hz timeline 在本地参与评分,APM 上报终态派生指标和诊断摘要。
163
163
  Runtime 通过 MessageChannel 把 telemetry 交给宿主 tracker;宿主按 10 条或 10 秒批量发送,并在终态、
164
164
  页面隐藏和销毁时立即 flush。具体字段见
165
165
  [audit-data-collection.codex.md](./audit-data-collection.codex.md)。
@@ -349,9 +349,11 @@ export const RULES_VERSION: string;
349
349
 
350
350
  ### 6.3 体积分析
351
351
 
352
- 直接消费 `RawParseResult.files: Array<{ path, size }>`(解压后字节),**零额外解包**。
352
+ ZIP 压缩体积直接取 `loadZip` 输入二进制数据的 `byteLength`;解压后体积、单图与 JS 资源统计消费 `RawParseResult.files: Array<{ path, size }>`,复用加载链路的解析结果。
353
353
 
354
- 与 `parse-raw.ts` 的 `DEFAULT_MINIAPP_PACKAGE_LIMITS` 严格分离:后者是安全阀(50MB 压缩包 / 15MB 单文件,超限 `throw PACKAGE_LIMIT_EXCEEDED` 包加载失败),评分线是质量度量(4MB 解压后即开始扣分,分档线性)。**评分阈值绝不能写进 package-limits**,否则超限时审核员连报告都看不到。
354
+ 包体指标按 ZIP、解压体积、单图、JS 排列,默认权重 5/3/1/1。ZIP 评分线由 `strategy.size.zip` 配置,默认 2/5/10MiB 对应 100/60/0 分;解压体积由各形态的 `size` 配置。区间内线性评分。APM 包摘要和评测结果保留压缩、解压的原始字节数,结果包含各自得分与权重。
355
+
356
+ 加载安全限制由 `package-limits` 独立控制;质量评分使用 `miniappSimulatorAuditPolicies`,一期 `enforce: false`。
355
357
 
356
358
  ### 6.4 Vue 面板
357
359
 
@@ -166,11 +166,30 @@ deriveMetrics → healTags / isApplicable → evaluateItems → buildReport
166
166
  | `evaluateItems` | 每项的实测值、阈值、分数、pass/fail/na |
167
167
  | `buildReport` | 覆盖度、硬卡口、总分、报告三态 |
168
168
 
169
- 堆峰值用 P95,减少 GC 锯齿带来的偶发尖峰。堆增长率丢弃前 20% 启动期样本,再对稳定期做线性回归,单位为 MB/min;样本数低于策略下限时该项为 `na`。斜率不依赖堆基线,是内存趋势卡口的主要依据。
169
+ JS 堆峰值取有效采样的最大读数,P95 单独进入 APM。Runtime 和 guest 的堆统计范围不保证一致,因此峰值使用 guest 原始读数。该值不代表进程总内存或小工具独占内存。
170
170
 
171
- 峰值堆有基线时取 `max(0, P95 - baseline)`,没有基线时使用 P95 原值。当前基线由会话开始时的读取回调提供,不应将其描述为已验证的 guest 独占内存。远程 Runtime/guest 的堆读数受共享环境、GC 和浏览器实现影响;增长趋势也是风险信号,不能单独确定泄漏或移动端 OOM。
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/)。多证据分类是本方案的保守推断规则,不属于引擎官方游戏分类标准。
172
192
 
173
- 当前静态扫描通过文件名和路径识别 Unity、Three/Babylon/PlayCanvas、Phaser/Pixi/Cocos/Laya、WASM、Worker 和视频候选。运行期显存、Canvas、WASM、Worker 证据补充能力标签;现有规则会把申报为 `tool` 且实测到 WebGL 的包映射为 `game3d`,保留 `tagMismatch`。这是当前识别局限:WebGL 不证明 3D 或游戏;hash bundle、原生 Canvas 游戏和非游戏 3D 内容需要人工确认。1.1 期拟分别记录业务形态、玩法维度、渲染能力和引擎证据。
174
193
 
175
194
  ### 4.4 报告三态和卡口
176
195
 
@@ -180,7 +199,9 @@ deriveMetrics → healTags / isApplicable → evaluateItems → buildReport
180
199
  | `unscorable` | 采集截断、基础设施异常或有效证据不足,需要人工复核 |
181
200
  | `loadFailed` | 包无法启动或 Runtime/guest 主线程卡死,不出分 |
182
201
 
183
- 默认卡口候选为堆增长率、显存水位、`webglcontextlost`、JS fatal 和 guest crash。峰值堆、解压总体积、图片与脚本体积进入评分,不作为加载安全上限;真正无法加载的大包由既有 `package-limits` 处理。
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` 处理。
184
205
 
185
206
  总分按已采到且适用的指标权重归一化。覆盖度为“已采到适用项权重 / 当前能力集适用项权重”;能力不适用项不进分母,适用但缺数据的项进分母、不进分子。上下文丢失与 guest 失联是稳定性风险,不自动等同于真实 OOM 或进程崩溃。风险命中时面板显示“需复核”,总分同步使用风险色。
186
207
 
@@ -307,7 +328,7 @@ Runtime 把服务端返回的 audit 对象作为完整策略直接交给规则
307
328
  "priority": 0,
308
329
  "scope": { "businessLines": ["mars"] },
309
330
  "strategy": {
310
- "policyVersion": "phase1-fps-priority-v6-2026-10-23",
331
+ "policyVersion": "phase1-zip-priority-v9-2026-10-29",
311
332
  "enforce": false,
312
333
  "minCoverage": 0.6,
313
334
  "defaultProfile": "tool",
@@ -326,21 +347,17 @@ Runtime 把服务端返回的 audit 对象作为完整策略直接交给规则
326
347
  "peakHeapMB": 200, "textureMB": 48, "canvasMB": 32,
327
348
  "imageMB": 32, "wasmMB": 64,
328
349
  "size": { "idealMB": 12, "acceptMB": 20, "maxMB": 32 }
329
- },
330
- "video": {
331
- "peakHeapMB": 180, "textureMB": 96, "canvasMB": 64,
332
- "imageMB": 64, "wasmMB": 32,
333
- "size": { "idealMB": 4, "acceptMB": 8, "maxMB": 16 }
334
350
  }
335
351
  },
336
352
  "weights": {
337
- "peakHeap": 6, "heapGrowth": 7, "texWatermark": 6,
353
+ "peakHeap": 6, "heapGrowth": 0, "texWatermark": 6,
338
354
  "canvasBuffer": 3, "decodedImages": 4, "wasmMemory": 3,
339
355
  "workerCount": 2, "jsError": 0.5, "fps": 5, "longTask": 1,
340
356
  "fcp": 4, "lcp": 10,
341
- "unpackedSize": 4, "singleImage": 3, "uncompressedJs": 3
357
+ "zipSize": 5, "unpackedSize": 3, "singleImage": 1, "uncompressedJs": 1
342
358
  },
343
359
  "scoreRanges": {
360
+ "heapGrowth": { "good": 4, "bad": 16, "threshold": 8 },
344
361
  "workerCount": { "good": 4, "bad": 12, "threshold": 4 },
345
362
  "jsError": { "good": 0, "bad": 5, "threshold": 0 },
346
363
  "fps": { "good": 50, "bad": 20, "threshold": 30 },
@@ -350,9 +367,9 @@ Runtime 把服务端返回的 audit 对象作为完整策略直接交给规则
350
367
  "fcp": { "p10": 1800, "median": 3000 },
351
368
  "lcp": { "p10": 2500, "median": 4000 }
352
369
  },
353
- "size": { "singleImageKB": 300, "uncompressedJsKB": 1024 },
370
+ "size": { "zip": { "idealMB": 2, "acceptMB": 5, "maxMB": 10 }, "singleImageKB": 300, "uncompressedJsKB": 1024 },
354
371
  "blocking": {
355
- "heapGrowthMBPerMin": 8, "textureWatermark": true,
372
+ "textureWatermark": true,
356
373
  "contextLost": true, "jsFatal": false, "crash": true
357
374
  },
358
375
  "minSamplesForSlope": 30,
@@ -380,7 +397,7 @@ guest 探针
380
397
  → APM 批量请求
381
398
  ```
382
399
 
383
- `measurement_name` 沿用 `miniapp_simulator_event`。事件包括 `miniapp_audit_session`、`miniapp_audit_sample`、`miniapp_audit_paint`、`miniapp_audit_incident`、`miniapp_audit_package`、`miniapp_audit_result`,由模拟器内部自动采集和上报。arming 和 collecting 样本都上报,评分只消费 collecting;刷新用 `attempt_no` 区分。首个 FCP 和 LCP 收口事件立即 flush,终态重复携带首屏值、状态、单项得分和阈值。
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,终态重复携带首屏值、状态、单项得分和阈值。
384
401
 
385
402
  `measurement_data` 是扁平 string/number 字段。布尔语义统一用 number 0/1,原始 timeline 不塞入单条数组或大 JSON。宿主按 10 条或 10 秒批量发送,终态、页面隐藏和销毁时 flush;请求使用 `Content-Type: application/json`、`Biz-Type: apm_fe`、`Batch: true`。上报异常与小工具业务和评分隔离。
386
403
 
@@ -396,7 +413,7 @@ guest 探针
396
413
 
397
414
  完整字段契约保留在仓库 `packages/miniapp-simulator/docs/audit-data-collection.codex.md`。Mars 传入审核任务和包版本,用于关联队列项与评测证据;评测数据上报不依赖 Mars 提供人工审核结论。
398
415
 
399
- 动态调参流程为:按形态/版本查询 APM 样本与事故 → 导出时序和完整策略 → 用相同版本规则回放候选阈值 → 对比分数分布、风险命中与采集完整性 → 选取内部样本实际复测 → 发布 Apollo 完整策略。误报和漏报需要带明确复测结论的样本检验,不能由分数分布自动推断。HTTP 请求成功只证明链路发出并收到响应,APM 落入与可检索性还需实际查询确认。
416
+ 动态调参流程为:按形态/版本查询 APM 终态与事故 → 导出实测指标和完整策略 → 分析候选阈值 → 对比分数分布、风险命中与采集完整性 → 选取内部样本实际复测 → 发布 Apollo 完整策略。误报和漏报需要带明确复测结论的样本检验,不能由分数分布自动推断。HTTP 请求成功只证明链路发出并收到响应,APM 落入与可检索性还需实际查询确认。
400
417
 
401
418
  ## 9. 验证范围与上线前置项
402
419
 
@@ -444,8 +461,8 @@ guest 探针
444
461
 
445
462
  | 指标 | 当前采集口径 | 局限与正确解读 |
446
463
  | --- | --- | --- |
447
- | JS 堆 | 每秒读取 guest 的 `performance.memory.usedJSHeapSize`。面板高水位取样本 P95,有基线时计算 `max(0, P95 - baseline)`。 | 该接口非标准、已标记弃用,支持受浏览器限制;它不是进程总内存,也不是严格归属于当前小工具的独占内存。共享堆可能混入宿主或同堆内容,独立 Worker、跨站 iframe 等又可能漏计;DOM、浏览器原生资源和 GPU 占用不能由该值完整表达。 |
448
- | 基线与增长率 | 基线由 Runtime 在会话开始时读取;增长率丢弃前 20% 样本后,对 guest 堆时序做线性回归,默认至少需要 30 个稳定期样本。 | Runtime 基线和 guest 样本的堆统计范围不保证相同,相减可能失真;显示 0 不等于零占用。斜率不减基线,但仍受共享堆、GC 时机、缓存预热和业务阶段影响。短时正增长是疑点,不是泄漏定论,也不能线性外推 OOM 时间。 |
464
+ | JS 堆 | 每秒读取 guest 的 `performance.memory.usedJSHeapSize`。面板取有效样本最大值;P95 单独上报。 | 该接口非标准、已标记弃用,支持受浏览器限制;它不是进程总内存,也不是严格归属于当前小工具的独占内存。共享堆可能混入宿主或同堆内容,独立 Worker、跨站 iframe 等又可能漏计;DOM、浏览器原生资源和 GPU 占用不能由该值完整表达。 |
465
+ | 基线与增长率 | 基线由 Runtime 在会话开始时读取;增长率丢弃前 20% 样本后,对 guest 堆时序做线性回归,默认至少需要 30 个稳定期样本。 | Runtime 基线仅作环境信息,不参与峰值计算。斜率仍受共享堆、GC 时机、缓存预热和业务阶段影响。短时正增长是疑点,不是泄漏定论,也不能线性外推 OOM 时间。 |
449
466
  | WebGL 图形资源(面板“显存水位”) | 对纹理、Buffer、Renderbuffer 的已接入分配/更新/删除调用记账,按尺寸、格式、多重采样数及 mipmap 等估算字节数,再提取水位。 | 这是应用层图形资源估算,不是实际物理显存或可用显存。格式转换、驱动对齐、缓存、延迟释放、着色器与合成资源等不由账本完整覆盖;mipmap 和部分格式使用近似。压缩纹理上传字节数也不等于 GPU 驱动实际驻留量。 |
450
467
  | Canvas 与图片 | 已跟踪 Canvas 按像素宽 × 高 × 4 估算;已跟踪 `img` 按自然宽 × 高 × 4 估算,DOM 移除或弱引用失效时销账。 | 不是浏览器实际后备缓冲或解码缓存总量;CSS 背景图、ImageBitmap、视频解码帧等没有完整覆盖。DOM 移除不证明底层资源即时释放,GC 与驱动回收也不与记账同步。各项可能存在共享、重叠或漏计,不能直接相加称为“小工具总内存”。 |
451
468
  | WASM 与 Worker | WASM 统计被构造 hook 捕获的 `Memory.buffer.byteLength`,最多保留 64 个引用;Worker 记录被捕获的创建与显式 `terminate` 余额。 | WASM 模块内部创建/导出的 Memory 和 Worker 内资源未保证覆盖;强引用也可能延长已捕获 Memory 的存活。Worker 数量不是内存大小,自行退出等路径可能造成计数偏差。不能用这些值证明后台资源已全部释放。 |
@@ -454,7 +471,7 @@ guest 探针
454
471
 
455
472
  ### 11.3 采样时长、队列与环境影响
456
473
 
457
- - **一秒快照不是连续内存监测。** 主线程阻塞、后台节流会延迟采样;两次快照之间的瞬时峰值可能漏掉。P95 表示高水位,不是绝对最大值,也不覆盖预热期被排除的堆样本。
474
+ - **一秒快照不是连续内存监测。** 主线程阻塞、后台节流会延迟采样;两次快照之间的瞬时峰值可能漏掉。采样最大值可能遗漏两次采样之间和预热期的瞬时峰值,P95 仅用于分布分析。
458
475
  - **短会话不能覆盖长时累积。** 常规队列的几十秒至两分钟只能反映已执行路径;重复关卡、场景切换、持续播放和资源反复创建释放引发的问题,需要单独延长观察并重复操作。实际操作过主要功能也不代表所有重场景均已覆盖。
459
476
  - **整页刷新隔离评测会话,不保证物理资源瞬间归零。** 浏览器进程、缓存、GC 与 GPU 资源回收由浏览器和系统管理;不能把下一包的读数视为独立新进程的精确占用,也不能拼接不同包的时序计算泄漏。
460
477
  - **环境与探针会影响结果。** 浏览器版本、其他标签页、桌面负载和调试工具影响读数;hook、记账和采样也有自身开销。结果应在相近环境、相同操作路径和策略版本下比较,并保留重复测量差异。
@@ -138,7 +138,7 @@ idle → preparing → attached → warming → measuring → finalizing → fin
138
138
 
139
139
  ## 6. APM 上报与字段登记
140
140
 
141
- 二期沿用现有 APM 事件,不新增自有 `/audit/raw` 接口。`miniapp_audit_session`、`miniapp_audit_sample`、`miniapp_audit_incident`、`miniapp_audit_package`、`miniapp_audit_result` 通过 `executionEnv: electron` 与一期区分。所有 `measurement_data` 只放数字;布尔值用 `0/1`。
141
+ 二期沿用现有 APM 事件,不新增自有 `/audit/raw` 接口。`miniapp_audit_session`、`miniapp_audit_incident`、`miniapp_audit_package`、`miniapp_audit_result` 通过 `executionEnv: electron` 与一期区分。所有 `measurement_data` 只放数字;布尔值用 `0/1`。
142
142
 
143
143
  每次结果必须登记:
144
144
 
@@ -39,10 +39,10 @@
39
39
  <p>资源评价顺序为:<b>ZIP 压缩体积 → 解压后总体积 → 最大单图与大 JS 文件</b>。ZIP 体积是包体评分的首要指标、资源维度中的最高权重项,解压后体积作为次要评分项,两项分别展示、分别上报。小工具 ZIP 包上限为 10MB,该上限约束压缩包,不是解压后体积或运行内存。</p>
40
40
  <table header-row="true"><colgroup><col width="155"/><col width="240"/><col width="230"/><col width="230"/></colgroup><tbody>
41
41
  <tr><td><p>指标</p></td><td><p>统计口径</p></td><td><p>评分定位与依据</p></td><td><p>产品解释与建议</p></td></tr>
42
- <tr><td><p><b>ZIP 压缩体积</b></p></td><td><p>实际 ZIP 文件的字节数;所有业务形态统一遵守 10MB 包上限。</p></td><td><p>包体评分第一位。交付规范建议控制在 2MB 以内;评分曲线在有效包范围内按体积设置,并通过试跑校准。10MB 是业务上限,不直接等同于零分线。</p></td><td><p>优先评价交付包负担,检查素材压缩、重复依赖与无用文件。包小不代表展开后的资源也小,需结合解压体积判断。</p></td></tr>
43
- <tr><td><p>解压后总体积</p></td><td><p>解压后包内文件字节数求和,与 ZIP 字节数独立记录。</p></td><td><p>包体评分第二位。根据真实包分布和资源用途校准预算,不套用 ZIP 的 10MB 上限。</p></td><td><p>用于识别压缩后较小、展开后仍较重的资源包;排查冗余素材与重复依赖。它不是运行期内存占用。</p></td></tr>
44
- <tr><td><p>最大单图</p></td><td><p>包内最大图片文件大小,不是解码后的像素内存。</p></td><td><p>≤ 300KiB 为 100 分;300–1,200KiB 线性扣分;≥ 1,200KiB 为 0 分。权重 3。</p></td><td><p>检查尺寸、格式与压缩;结合首屏用途判断是否需要优化,不能只看文件大就判功能失败。</p></td></tr>
45
- <tr><td><p>大 JS 文件</p></td><td><p>统计单文件超过 1MiB 的 JS 体积之和。面板“未压缩 JS”字段实际采用此启发式口径,并未检查源码是否压缩。</p></td><td><p>没有超线文件为 100 分;超线文件总量按 0–4MiB 线性计分,达到 4MiB 为 0 分。权重 3。</p></td><td><p>引导拆包与依赖治理;不能据此认定开发者没有压缩代码。</p></td></tr>
42
+ <tr><td><p><b>ZIP 压缩体积</b></p></td><td><p>实际 ZIP 文件的字节数;所有业务形态统一遵守 10MB 包上限。</p></td><td><p>包体评分第一位,权重 5。初始评分线为 ≤2MiB 得 100 分,5MiB 得 60 分,≥10MiB 得 0 分,区间内线性评分;由 Apollo 的 size.zip 配置并通过试跑校准。</p></td><td><p>优先评价交付包负担,检查素材压缩、重复依赖与无用文件。包小不代表展开后的资源也小,需结合解压体积判断。</p></td></tr>
43
+ <tr><td><p>解压后总体积</p></td><td><p>解压后包内文件字节数求和,与 ZIP 字节数独立记录。</p></td><td><p>包体评分第二位,权重 3。工具类 2/4/9MiB、2D 游戏 6/12/24MiB、3D 游戏 12/20/32MiB 分别对应 100/60/0 分,区间线性评分;由 Apollo 各形态的 size 配置。</p></td><td><p>用于识别压缩后较小、展开后仍较重的资源包;排查冗余素材与重复依赖。它不是运行期内存占用。</p></td></tr>
44
+ <tr><td><p>最大单图</p></td><td><p>包内最大图片文件大小,不是解码后的像素内存。</p></td><td><p>≤ 300KiB 为 100 分;300–1,200KiB 线性扣分;≥ 1,200KiB 为 0 分。权重 1。</p></td><td><p>检查尺寸、格式与压缩;结合首屏用途判断是否需要优化,不能只看文件大就判功能失败。</p></td></tr>
45
+ <tr><td><p>大 JS 文件</p></td><td><p>统计单文件超过 1MiB 的 JS 体积之和。面板“未压缩 JS”字段实际采用此启发式口径,并未检查源码是否压缩。</p></td><td><p>没有超线文件为 100 分;超线文件总量按 0–4MiB 线性计分,达到 4MiB 为 0 分。权重 1。</p></td><td><p>引导拆包与依赖治理;不能据此认定开发者没有压缩代码。</p></td></tr>
46
46
  </tbody></table>
47
47
  <p>资源预算方法参考 <a href="https://web.dev/articles/performance-budgets-101">web.dev:Performance budgets</a>。10MB ZIP 上限和 2MB 优化建议来自小工具交付规范;具体评分分档由本产品试跑数据校准,不作为行业统一阈值。APM 保留两种体积的原始字节数,展示时明确标注 ZIP 或解压后体积;MiB、KiB 按 1024 进制计算。资源用途用于解释负担,不能仅凭 WebGL 特征认定内容是 3D 游戏并放宽包上限。</p>
48
48
  <p><b>微信参考:</b>微信的图片大小规则关注原图像素面积是否超过显示面积 × 设备像素比²,图片缓存规则关注 HTTP 缓存,资源冗余规则关注不可访问页面和未使用样式。这些做法支持从“资源是否有效使用”解释成本。本方案依次评价 ZIP 体积、解压体积和单项资源;图片尺寸利用率、缓存和资源使用率可作为后续诊断方向。微信的像素面积规则与本方案的文件字节阈值分别衡量不同成本。</p>
package/docs/tracker.md CHANGED
@@ -53,8 +53,9 @@ createMiniAppSimulator({
53
53
  });
54
54
  ```
55
55
 
56
- - **批量上报**:打点先进缓冲,满 10 条或最多 10s 后合并成一次请求发送;页面隐藏(`visibilitychange`)
57
- 与 `destroy()` 时立即冲刷,避免关闭前丢数据。
56
+ - **批量上报**:打点先进缓冲,满 10 条或最多 10s 后触发发送。每个请求最多 10 条,50 条分为
57
+ 5 个请求,尾批按实际条数发送。同一 SDK 模块内的 tracker 共用有序队列;一批完成(含重试)后发送下一批。
58
+ 每次尝试最多等待 5 秒;网络错误、超时及 5xx 最多尝试三次,4xx 结束该批。页面隐藏(`visibilitychange`)与 `destroy()` 时立即冲刷。
58
59
  - **本地开发**:`localhost` / `127.0.0.1` 默认上报到测试 APM;需要只看 console 时显式传 `debug: true`。
59
60
 
60
61
  ## 字段说明