@skillbuilder/miniapp-simulator 0.2.1 → 0.2.4

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 (41) 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 +422 -0
  12. package/docs/audit-local-validation.codex.md +165 -0
  13. package/docs/audit-score-design.codex.md +221 -0
  14. package/docs/audit-score-design.md +885 -0
  15. package/docs/audit-score-tech-plan.md +448 -0
  16. package/docs/audit-tech-design.codex.md +468 -0
  17. package/docs/electron-review.md +223 -12
  18. package/docs/quality-evaluation-redoc.xml +163 -0
  19. package/docs/remote-usage.md +8 -3
  20. package/lib/client-BDt3WDu_.mjs +1118 -0
  21. package/lib/{index-DI2oETZ_.mjs → index-7MeYFlk_.mjs} +210 -182
  22. package/lib/index.js +1265 -1115
  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 +56 -0
  27. package/lib/types/remote/audit-leave-monitor.d.ts +12 -0
  28. package/lib/types/remote/audit-telemetry.d.ts +23 -0
  29. package/lib/types/remote/audit-watchdog.d.ts +14 -0
  30. package/lib/types/remote/client.d.ts +25 -1
  31. package/lib/types/remote/jsapi-config.d.ts +6 -2
  32. package/lib/types/remote/load-timing.d.ts +5 -0
  33. package/lib/types/remote/protocol.d.ts +151 -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/sw/service-worker-host.d.ts +15 -0
  37. package/lib/types/tracker/default-tracker.d.ts +8 -0
  38. package/lib/types/types/options.d.ts +24 -0
  39. package/lib/workbench.js +1 -1
  40. package/package.json +1 -1
  41. package/lib/client-BlQ9j4FW.mjs +0 -775
@@ -0,0 +1,422 @@
1
+ # 小工具体验评测 APM 数据采集与指标校准
2
+
3
+ ## 1. 目标与边界
4
+
5
+ Mars 审核队列中的评测数据全部进入现有 APM,不建设 `/simulator/audit/raw`、评测数据库或对象存储。
6
+
7
+ 每个队列项产生一组可关联的 APM 事件,用于:
8
+
9
+ - 监控评测覆盖率、异常率、分数分布和采集链路健康度;
10
+ - 按评测形态和版本重建指标时序,回放候选阈值;
11
+ - 分析指标分布、风险命中和采集完整性,为内部复测选取样本并校准阈值;
12
+ - 二期按 `app_id + package_hash` 关联真机 OOM、崩溃和机型数据。
13
+
14
+ APM 中不上传 ZIP 文件内容、完整文件清单、DOM、输入内容、屏幕坐标、网络正文、完整错误堆栈或截图二进制。静态包只上报评分所需的聚合指标;截图继续使用现有附件链路,自动截图关闭不影响评测数据上报。
15
+
16
+ ## 2. APM 事件模型
17
+
18
+ 所有事件沿用现有:
19
+
20
+ ```text
21
+ measurement_name = miniapp_simulator_event
22
+ ```
23
+
24
+ 通过 `event_name` 区分评测数据:
25
+
26
+ | `event_name` | 频率 | 内容 |
27
+ | ------------------------------ | ---------------: | -------------------------------------------------- |
28
+ | `miniapp_audit_session` | 每个阶段一次 | 会话启动、探针就绪、采集开始、刷新失效、结束和中断 |
29
+ | `miniapp_audit_sample` | 采集期间 1 Hz | 单个原始指标快照 |
30
+ | `miniapp_audit_paint` | 首屏值变化及收口 | guest FCP、LCP 候选与收口,不等待预热 |
31
+ | `miniapp_audit_incident` | 事件发生时 | context lost、JS fatal、熔断、失联和探针饱和 |
32
+ | `miniapp_audit_package` | 每个评测会话一次 | 包体和静态能力聚合指标 |
33
+ | `miniapp_audit_result` | 每个评测会话一次 | 三态报告、得分、覆盖度、硬风险和关键派生指标 |
34
+
35
+ 每条 `measurement_data` 都是扁平标量,只使用 string 和 number,不把 timeline 或报告数组序列化成一个大 JSON 字符串。布尔语义统一编码为 number:`1` 表示 true/是,`0` 表示 false/否。1 Hz 样本逐点进入 `miniapp_audit_sample`,现有 tracker 再按 10 条或 10 秒合并为 APM 批量请求。
36
+
37
+ 按最长 120 秒评测估算,每个会话约产生 120 条 sample、4~10 条会话/异常/结果事件,对应约 13 个批量请求。上线前需要根据 Mars 日审核量确认 APM 事件配额和保留周期。
38
+
39
+ ## 3. 会话标识
40
+
41
+ | 字段 | 生成方 | 生命周期 | 用途 |
42
+ | ---------------------- | -------------- | ------------------ | ---------------------------- |
43
+ | `review_task_id` | Mars | 一个审核队列项 | 关联队列项及其评测证据 |
44
+ | `audit_session_id` | Runtime | 一次评测 | 关联全部评测 APM 事件 |
45
+ | `attempt_no` | Runtime | 一次 guest Realm | 区分手动刷新、重试和失效时序 |
46
+ | `simulator_session_id` | Remote Client | 一个远程模拟器连接 | 关联加载性能与评测数据 |
47
+ | `app_id` | Mars | 应用 | 聚合同一应用的历史表现 |
48
+ | `package_version` | Mars | 被审核包版本 | 对齐审核产物 |
49
+ | `package_hash` | Client/Runtime | ZIP 或文件表内容 | 防止版本名复用,关联真机结果 |
50
+
51
+ `audit_session_id` 使用 UUID,不能只使用时间戳。所有评测事件都携带以上标识;`attempt_no + sample_seq` 在会话内唯一。
52
+
53
+ 审核员 ID 沿用 APM 的 `context_userId`,不重复写入 `measurement_data`。审核员昵称和自由文本备注不进入 APM。
54
+
55
+ ## 4. 公共字段
56
+
57
+ 所有评测事件都携带:
58
+
59
+ ```ts
60
+ interface AuditApmCommonFields {
61
+ business_line: 'mars';
62
+ review_task_id: string;
63
+ audit_session_id: string;
64
+ attempt_no: number;
65
+ simulator_session_id: string;
66
+ app_id: string;
67
+ package_version: string;
68
+ package_hash: string;
69
+ app_type: 'tool' | 'game2d' | 'game3d' | 'video';
70
+ policy_version: string;
71
+ rules_version: string;
72
+ probe_version: string;
73
+ runtime_version: string;
74
+ sdk_version: string;
75
+ }
76
+ ```
77
+
78
+ 会话 ID、任务 ID、应用 ID 和包哈希只用于单次追踪与数据关联,不作为 APM 看板的 group-by 维度。看板只按状态、形态、版本、风险类型和结构化原因码聚合。
79
+
80
+ ### 4.1 APM 字段登记
81
+
82
+ 沿用 APM 已有顶层信封字段,不新增 measurement:
83
+
84
+ - `measurement_name = miniapp_simulator_event`;
85
+ - `clientTime`、`context_sdkSessionId`、`context_pageSessionId`、`context_sdkSeqId`;
86
+ - `context_nameTracker`、`context_platform`、`context_accountGeographicZone`;
87
+ - `context_artifactName`、`context_artifactVersion`、`context_feSdkVersion`、`context_userId`;
88
+ - `context_route`、`context_matchedPath`、`context_userAgent`。
89
+
90
+ 需要在 `measurement_data` 登记的 string/keyword 字段:
91
+
92
+ ```text
93
+ event_name, business_line, renderer, app_id, simulator_session_id,
94
+ review_task_id, audit_session_id, package_version, package_hash,
95
+ runtime_version, sdk_version, app_type, policy_version, rules_version, probe_version,
96
+ stage, phase, reason_code, stop_reason, kind, source, detail,
97
+ declared_app_type, detected_app_type,
98
+ status, failure_code, primary_blocking_code, truncated_reason_code,
99
+ fcp_status, lcp_status, paint_finalize_reason, paint_timing_source,
100
+ paint_finalize_event, lcp_element_tag
101
+ ```
102
+
103
+ 需要登记的 0/1 number 标志字段(`1 = true/是`,`0 = false/否`):
104
+
105
+ ```text
106
+ automatic, complete, sufficient, probe_saturated,
107
+ blocking_passed, enforced, tag_mismatch,
108
+ has_webgl, has_canvas2d, has_wasm, has_worker, has_video,
109
+ lcp_finalized
110
+ ```
111
+
112
+ 需要登记的 number 字段:
113
+
114
+ ```text
115
+ timestamp, attempt_no, stage_seq, sample_seq, incident_seq,
116
+ session_elapsed_ms, collecting_elapsed_ms, evaluation_duration_ms, arming_ms, auto_stop_on_leave_ms,
117
+ heap_bytes, tex_bytes, buf_bytes, canvas_bytes, img_bytes, wasm_bytes, workers,
118
+ fps, long_task_count, long_task_max_ms, js_error_count, net_pending, interaction_count,
119
+ zip_bytes, unpacked_bytes, file_count, largest_image_bytes,
120
+ largest_uncompressed_js_bytes, wasm_file_count, worker_file_count, media_file_count,
121
+ score_total, score_memory, score_stability, score_performance, score_experience, score_package,
122
+ coverage_ratio, effective_ms, sample_count, first_interaction_ms,
123
+ reload_count, infrastructure_error_count, blocking_failure_count,
124
+ peak_heap_bytes, heap_growth_mb_per_min, tex_watermark_bytes, buf_watermark_bytes,
125
+ max_workers, fps_median, fps_p5, js_error_total,
126
+ score_fps, score_long_task, score_js_error, weight_fps, weight_long_task, weight_js_error,
127
+ fcp_ms, lcp_ms, score_fcp, score_lcp, fcp_threshold_ms, lcp_threshold_ms,
128
+ lcp_finalized_at_ms, lcp_element_size
129
+ ```
130
+
131
+ `detail` 只用于故障明细检索,不建立 group-by 索引;最大 200 字。ID 和哈希字段只用于精确查询和关联,不做高基数聚合。金额、输入内容、文件路径、完整堆栈和截图内容不登记。
132
+
133
+ ### 4.2 请求格式示例
134
+
135
+ 单次 HTTP 请求是数组批次,Header 固定为 `Content-Type: application/json`、`Biz-Type: apm_fe`、`Batch: true`。终态会立即 flush,其他事件达到 10 条或等待 10 秒发送。以下为字段结构示例,数值不作为真实包实测结果。
136
+
137
+ ```json
138
+ [
139
+ {
140
+ "clientTime": 1791958348000,
141
+ "context_sdkSessionId": "<tracker-session-uuid>",
142
+ "context_pageSessionId": "<page-session-uuid>",
143
+ "context_sdkSeqId": 52,
144
+ "context_nameTracker": "wapT",
145
+ "context_platform": "PC",
146
+ "context_accountGeographicZone": "cn",
147
+ "context_artifactName": "skillbuilder-miniapp",
148
+ "context_artifactVersion": "0.1.10",
149
+ "context_feSdkVersion": "1.3.13",
150
+ "context_userId": "<reviewer-id>",
151
+ "measurement_name": "miniapp_simulator_event",
152
+ "measurement_data": {
153
+ "event_name": "miniapp_audit_result",
154
+ "timestamp": 1791958348000,
155
+ "business_line": "mars",
156
+ "sdk_version": "0.1.10",
157
+ "renderer": "iframe-service-worker",
158
+ "app_id": "playground-test-app",
159
+ "simulator_session_id": "<simulator-session-uuid>",
160
+ "review_task_id": "playground-review-task",
161
+ "audit_session_id": "c179ed94-806f-46b1-8a2e-0da05c8361c8",
162
+ "attempt_no": 1,
163
+ "package_version": "playground-sample-v1",
164
+ "package_hash": "<sha256>",
165
+ "runtime_version": "development",
166
+ "app_type": "tool",
167
+ "policy_version": "phase1-fps-priority-v3-2026-10-19",
168
+ "rules_version": "1.2.0",
169
+ "probe_version": "1.1.1",
170
+ "status": "scored",
171
+ "blocking_passed": 1,
172
+ "score_total": 100,
173
+ "coverage_ratio": 1,
174
+ "effective_ms": 47000,
175
+ "sample_count": 48,
176
+ "interaction_count": 1,
177
+ "peak_heap_bytes": 3222161,
178
+ "heap_growth_mb_per_min": 0.4698531312,
179
+ "fps_p5": 120,
180
+ "long_task_count": 0,
181
+ "js_error_total": 0
182
+ }
183
+ }
184
+ ]
185
+ ```
186
+
187
+ ## 5. 事件字段
188
+
189
+ ### 5.1 `miniapp_audit_session`
190
+
191
+ | 字段 | 类型 | 说明 |
192
+ | -------------------- | ------- | ----------------------------------------------------------------------------------------------------- |
193
+ | `stage` | string | `started`、`probe_ready`、`collecting`、`attempt_invalidated`、`stop_requested`、`finishing`、`finished`、`interrupted` |
194
+ | `stage_seq` | number | 会话阶段递增序号 |
195
+ | `session_elapsed_ms` | number | 相对评测启动时间 |
196
+ | `reason_code` | string | 刷新、中断、失败或停止的稳定原因码;自动结束为 `pointer_leave_timeout`,手动为 `manual`,最长时限为 `duration_limit` |
197
+ | `auto_stop_on_leave_ms` | number | `started` 携带本次离开自动结束延迟,默认 30000;0 表示关闭 |
198
+ | `interaction_count` | number | 到达 guest 的真实操作累计值 |
199
+ | `reload_count` | number | 本会话刷新次数 |
200
+
201
+ 同一队列项中手动刷新时,发送 `attempt_invalidated`,随后 `attempt_no + 1`。不同 Realm 的样本不能拼接;失效 attempt 仍保留在 APM,供分析刷新原因和启动风险。
202
+
203
+ ### 5.2 `miniapp_audit_sample`
204
+
205
+ 每秒发送一个扁平样本:
206
+
207
+ ```ts
208
+ interface AuditSampleApmFields {
209
+ sample_seq: number;
210
+ session_elapsed_ms: number;
211
+ phase: 'arming' | 'collecting';
212
+ collecting_elapsed_ms?: number;
213
+ heap_bytes?: number;
214
+ tex_bytes: number;
215
+ buf_bytes: number;
216
+ canvas_bytes: number;
217
+ img_bytes: number;
218
+ wasm_bytes: number;
219
+ workers: number;
220
+ fps?: number;
221
+ long_task_count: number;
222
+ long_task_max_ms: number;
223
+ js_error_count: number;
224
+ net_pending: number;
225
+ interaction_count: number;
226
+ }
227
+ ```
228
+
229
+ `performance.memory` 不可用时省略 `heap_bytes`,不能写成 0。arming 与 collecting 都上报;评分只消费 collecting,分析可以单独查看启动尖峰和启动期 OOM。
230
+
231
+ ### 5.2.1 `miniapp_audit_paint`
232
+
233
+ | 字段 | 类型 | 说明 |
234
+ | --- | --- | --- |
235
+ | `fcp_ms` / `lcp_ms` | number | 相对 guest 导航的毫秒;缺失时省略,不填 0 |
236
+ | `fcp_status` / `lcp_status` | string | pending、measured、unsupported、hidden;终态无探针证据时为 not_reported |
237
+ | `lcp_finalized` | number | 1 已收口,0 仍是候选 |
238
+ | `paint_finalize_reason` | string | interaction、hidden、pagehide、audit-stop |
239
+ | `paint_finalize_event` | string | 触发 LCP 收口的输入事件:touchstart、pointerdown、mousedown、keydown、input、change、click、scroll;非输入收口时省略 |
240
+ | `lcp_finalized_at_ms` | number | 首次收口相对 guest 导航的毫秒;后续操作或停止评测不延长截止时间 |
241
+ | `lcp_element_tag` | string | 候选元素标签,例如 img、p、h1;不包含文本、选择器或 URL |
242
+ | `lcp_element_size` | number | 浏览器报告的候选可见面积,单位为平方 CSS 像素;缺失时省略 |
243
+ | `paint_timing_source` | string | guest-navigation |
244
+ | `session_elapsed_ms` | number | 当前事件相对评测启动时间,与导航绘制时钟区分 |
245
+
246
+ 首个实测 FCP 和 LCP 收口要求 tracker 立即 flush,LCP 候选变化按现有批量策略发送,observer 的相同快照去重。停止时 guest 再返回一次收口确认。首屏数据独立于 3 秒预热与 30 秒有效采样门槛,短会话或 unscorable 也可观测。停止时最多等待 150ms 排空探针待处理记录;未确认收口的 LCP 不计分。
247
+
248
+ `miniapp_audit_result` 同时携带这些字段,另带 `score_fcp`、`score_lcp`、`fcp_threshold_ms`、`lcp_threshold_ms`,用于按策略版本分析曲线与总分影响。运行过程上报 `fps_median` 与 `fps_p5`,并记录 `score_fps`、`score_long_task`、`score_js_error` 和 `weight_fps`、`weight_long_task`、`weight_js_error`。权重取自实际报告,包括 Apollo 生效后的数值;本地默认依次为 5、1、0.5。JS 异常数量和事故证据完整上报,默认不因单条 JS 异常触发硬卡口。以上新增 measurement 字段均为数值,缺失时省略。
249
+
250
+ 探针 `1.1.1` 在捕获阶段处理首次输入,包括模拟器转发的 `input/change`,先固定 LCP 截止时间,再排空截止前的候选;搜索、分类切换和分页后的候选不进入首屏评分。`miniapp_audit_paint` 与 `miniapp_audit_result` 共用上述绘制字段,用于核对采集值、收口依据与最终分数。
251
+
252
+ 看板以最终有效 attempt 的 result 为去重入口,FCP 筛选 measured,LCP 筛选 measured 且 `lcp_finalized = 1`,再计算 P50/P75/P95 和缺失率,避免把候选事件重复计入分布。按策略、规则、探针版本、形态和浏览器/设备环境分组。初始良好边界为 FCP ≤1800ms、LCP ≤2500ms,约 90 分;控制点 3000/4000ms 为 50 分。桌面模拟器不代表移动端 field Web Vitals;Canvas/WebGL 不属于 LCP,游戏核心可用性仍需交互确认。
253
+
254
+ ### 5.3 `miniapp_audit_incident`
255
+
256
+ | 字段 | 类型 | 说明 |
257
+ | -------------------- | ------ | --------------------------------------------------------------------------------------- |
258
+ | `incident_seq` | number | 会话内递增序号 |
259
+ | `session_elapsed_ms` | number | 相对评测启动时间 |
260
+ | `phase` | string | `arming` 或 `collecting` |
261
+ | `kind` | string | `contextLost`、`jsFatal`、`infraError`、`guestLost`、`simulatorGuard`、`probeSaturated` |
262
+ | `source` | string | `app`、`simulator-sdk` 或 `audit-probe` |
263
+ | `detail` | string | 最多 200 字的脱敏摘要 |
264
+
265
+ JS 异常累计值仍在 sample/result 中上报;incident 只保留首三次代表性错误,避免同一异常刷爆 APM。
266
+
267
+ ### 5.4 `miniapp_audit_package`
268
+
269
+ 静态包只上报当前评分和阈值分析需要的聚合值:
270
+
271
+ - `zip_bytes`、`unpacked_bytes`、`file_count`;
272
+ - `largest_image_bytes`、`largest_uncompressed_js_bytes`;
273
+ - `wasm_file_count`、`worker_file_count`、`media_file_count`;
274
+ - `has_webgl`、`has_canvas2d`、`has_wasm`、`has_worker`、`has_video`,均为 0/1 number;
275
+ - `declared_app_type`、`detected_app_type`。
276
+
277
+ 不逐文件上报路径和大小。APM 可以重放已有静态指标的阈值,但无法用历史数据新增依赖完整文件清单的规则;新增这类规则时需要先补对应聚合字段,再积累样本。
278
+
279
+ ### 5.5 `miniapp_audit_result`
280
+
281
+ | 分类 | 字段 |
282
+ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
283
+ | 终态 | `status`、`failure_code`、`blocking_passed`、`enforced`、`stop_reason`、`auto_stop_on_leave_ms` |
284
+ | 分数 | `score_total`、`score_memory`、`score_stability`、`score_performance`、`score_experience`、`score_package` |
285
+ | 质量 | `coverage_ratio`、`effective_ms`、`complete`、`sufficient`、`sample_count`、`interaction_count`、`first_interaction_ms`、`reload_count`、`probe_saturated` |
286
+ | 关键指标 | `peak_heap_bytes`、`heap_growth_mb_per_min`、`tex_watermark_bytes`、`buf_watermark_bytes`、`canvas_bytes`、`img_bytes`、`wasm_bytes`、`max_workers`、`fps_median`、`fps_p5`、`long_task_count`、`long_task_max_ms`、`js_error_total` |
287
+ | 流畅度与稳定性评分 | `score_fps`、`score_long_task`、`score_js_error`;实际权重 `weight_fps`、`weight_long_task`、`weight_js_error` |
288
+ | 风险 | `blocking_failure_count`、`primary_blocking_code`、`infrastructure_error_count`、`truncated_reason_code` |
289
+
290
+ 分类分统一换算为 0~100:`got / max * 100`;分类没有适用项时省略该字段,不能按 0 分上报。逐项数组和 N/A 文本不进入 APM,改为稳定的计数、原因码和关键指标字段。
291
+
292
+ `stop_reason` 区分 `manual`、`pointer_leave_timeout`、`duration_limit`、`interrupted`。正常 Runtime 终态携带该字段;宿主生成的失联/销毁失败报告仍通过 `failure_code` 判断,不把缺失的停止原因当作手动结束。按停止方式关联有效时长、样本数和证据不足率,可评估 30 秒是否合适。自动结束不伪造输入次数或功能完成状态,短会话仍上报 `unscorable`。
293
+
294
+ `score_stability` 为独立的稳定性分类,当前仅包含低权重 JS 异常。面板展示顺序为运行性能、包体积、稳定性、内存。长任务默认按 500ms 满分、3000ms 零分的线性曲线评分,权重为 1;原始次数和最长时长继续保留,单次几百毫秒阻塞不作为硬风险。
295
+
296
+ ## 6. 上报链路
297
+
298
+ ```text
299
+ guest 探针
300
+ → Runtime AuditSession
301
+ → MessageChannel 发送扁平 audit telemetry
302
+ → Remote Client 宿主 tracker
303
+ → APM 批量上报
304
+ ```
305
+
306
+ 评测样本由宿主 tracker 发送,不由 Runtime 直接发送。宿主与 guest/Runtime 不共用被测主线程:Runtime 卡死后,宿主仍可把已经收到但尚未满批的 sample 冲刷到 APM,并发送 `RUNTIME_HUNG` 终态。
307
+
308
+ 现有 tracker 的 10 条/10 秒批量策略保持不变:
309
+
310
+ - 收到终态、页面隐藏或模拟器销毁时立即 `flush()`;
311
+ - 使用 `fetch(..., { keepalive: true })` 保全页面离开前的批次;
312
+ - 检查 HTTP `response.ok`,网络错误和 5xx 最多进行 3 次内存重试;
313
+ - 重试使用短退避且不阻塞审核结算;
314
+ - 每个批次完成后通过 `onTrackerDelivery({ ok, status, attempts, eventCount })` 暴露投递回执,回调不包含事件正文;
315
+ - 队列进入下一项前先提交当前会话终态,再完整重载模拟器;
316
+ - APM 关闭、拒绝或最终重试失败时静默降级,不改变审核结果。
317
+
318
+ 不建设跨页面补传库。APM 作为唯一数据通道时,必须确认评测事件不被服务端采样、字段可检索、保留周期覆盖指标校准周期,并有足够事件配额。否则丢失的数据无法从其他存储恢复。
319
+
320
+ ## 7. 队列内时序
321
+
322
+ ```text
323
+ Mars 进入队列项
324
+ → 传入 review_task_id / app_id / package_version
325
+ → Remote Client 计算 package_hash,创建 simulator_session_id
326
+ → Runtime 创建 audit_session_id
327
+ → miniapp_audit_session(stage=started)
328
+ → 探针 document-start 安装
329
+ → arming / collecting 样本按 1 Hz 发给宿主并进入 APM
330
+ → 审核员真实操作主要功能并检查结果
331
+ → 异常实时发送 miniapp_audit_incident
332
+ → 离开整个模拟器容器(含留白与评测面板) 30 秒、点击“停止评测”,或达到最长时钟
333
+ → miniapp_audit_package + miniapp_audit_result
334
+ → 宿主 flush APM 批次
335
+ → 完整重载模拟器,处理下一队列项
336
+ ```
337
+
338
+ 自动截图关闭时,以上时序不变。截图失败或缺失不影响六类评测 APM 事件。
339
+
340
+ 停止仅结束采集,`complete` 表示正常收口,不表示主要功能已完成或审核通过。使用证据记录真实输入次数、首次操作时间、有效采集时长和样本数;主要功能是否完成由审核员按 SOP 判断。六类事件及离开自动结束均由模拟器内部处理,不要求 Mars 提供人工审核结论或调用停止接口。倒计时内移回模拟器容器内会取消,区域外持续真实输入会延后;到期生成报告后宿主立即 flush。若先刷新或销毁,则按中断链路尽力上报已收到的数据,不保证完整终态送达;上报失败不阻塞审核流转。
341
+
342
+ ## 8. 指标校准
343
+
344
+ ### 8.1 数据重建
345
+
346
+ 通过 `audit_session_id + attempt_no` 查询 `miniapp_audit_sample`,按 `sample_seq` 排序即可重建时序。使用 `miniapp_audit_result` 确认当时的版本、终态和证据质量,并关联同一会话的首屏、包体和事故事件。
347
+
348
+ 进入阈值分布的样本必须满足:
349
+
350
+ - `status = scored`;
351
+ - 真实操作数、有效时长与样本数满足策略;
352
+ - 采集完整、覆盖度达标、版本字段齐全;
353
+ - 同一分析批次使用兼容的 probe/rules 口径。
354
+
355
+ `unscorable` 和 `loadFailed` 单独分析,不混入正常分数分布,也不能从故障率中删除。
356
+
357
+ ### 8.2 调整步骤
358
+
359
+ 1. 按 app type 计算关键指标的 P50、P75、P90、P95、P99 和缺失率;
360
+ 2. 按高低分、风险类型和证据不足原因选取内部复测样本,实际操作并检查功能与体验;
361
+ 3. 从 APM 导出 sample 事件,用候选策略离线重算;
362
+ 4. 对比候选策略的分数分布、风险命中、缺失率和有效采集时长;使用有明确复测结论的内部样本检验误报和漏报;
363
+ 5. 生成新的 `policyVersion`,以 `enforce: false` 灰度到指定业务线或 app;
364
+ 6. 覆盖一个完整校准周期后,再决定是否扩大试跑范围;一期保持观察,不作为审核强卡口。
365
+
366
+ 样本量不足的 profile 只展示分布,不自动调整阈值。指标分布和评分不能代替真实功能结论,也不能单独证明误报率或漏报率;内部复测结论需由分析人员整理,不由模拟器自动推断。分析任务只能产出候选策略,不能直接写 Apollo。
367
+
368
+ ### 8.3 首批看板
369
+
370
+ - 每日评测量、三态比例、有效采集率;
371
+ - 评测会话耗时、有效采集时长、首次操作时间、真实交互次数和零交互占比;
372
+ - 各 app type 的关键指标分位数和缺失率;
373
+ - 硬风险命中率、风险类型分布和分数分布;
374
+ - JS 异常、context lost、Runtime hung 和资源熔断趋势;
375
+ - `policy_version / rules_version / probe_version` 覆盖情况;
376
+ - APM 请求成功率、批量大小、重试率和丢弃事件数。
377
+
378
+ ## 9. 代码落点
379
+
380
+ | 位置 | 改造内容 |
381
+ | ----------------------------------------------------------- | -------------------------------------------------------------------- |
382
+ | `packages/miniapp-audit-plugin/src/collector/session.ts` | 保留 arming/collecting 样本;为 sample、incident 和 attempt 分配序号 |
383
+ | `packages/miniapp-audit-plugin/src/index.ts` | 增加逐样本、事件和阶段的 telemetry 回调 |
384
+ | `packages/miniapp-simulator/src/remote/audit-controller.ts` | 生成 UUID、会话字段和 package 扁平摘要 |
385
+ | `packages/miniapp-simulator/src/remote/runtime.ts` | 通过 MessageChannel 把 audit telemetry 发送给宿主 |
386
+ | `packages/miniapp-simulator/src/remote/client.ts` | 消费 telemetry、扁平化 result 并交给 APM;看门狗发送卡死终态 |
387
+ | `packages/miniapp-simulator/src/remote/load-timing.ts` | 从只支持 load stage 扩展为内部通用自定义点位上报 |
388
+ | `packages/miniapp-simulator/src/tracker/default-tracker.ts` | 检查响应、有限重试和终态 flush |
389
+ | Mars 接入层 | 传入队列/包身份,消费评测报告 |
390
+
391
+ 模拟器服务端不新增评测数据接口和存储,只继续提供 Runtime、插件与动态策略。
392
+
393
+ ## 10. 验收标准
394
+
395
+ - 使用 `audit_session_id` 能在 APM 查到 session、sample、paint、incident、package 和 result;
396
+ - 六类评测事件由模拟器内部产生和上报,不依赖人工审核结论回调;
397
+ - 连续处理多个队列项时,样本、attempt、包版本和评测结果不会串包;
398
+ - 手动刷新后的新 Realm 使用新的 `attempt_no`,时序不与失效 attempt 拼接;
399
+ - Runtime 卡死后,宿主仍会冲刷已收到的样本并发送 `RUNTIME_HUNG` result;
400
+ - 关闭自动截图后,全部评测 APM 事件仍正常产生;
401
+ - APM 中没有大 JSON、文件清单、输入内容、错误全文或截图二进制;
402
+ - 从 APM 导出的 sample 能使用指定策略版本重算,并与原 result 对账;
403
+ - APM 的事件配额、无采样配置、字段检索和保留周期满足一次完整校准;
404
+ - APM 上报失败不阻塞审核队列,成功率、重试和丢弃量可监控。
405
+
406
+ ## 11. 当前实现状态
407
+
408
+ 模拟器已实现以下数据链路:
409
+
410
+ - arming 和 collecting 的 1 Hz 原始样本、异常、会话阶段、包体摘要和三态结果通过 MessageChannel 送到宿主;
411
+ - 宿主 tracker 分别上报 `miniapp_audit_session`、`miniapp_audit_sample`、`miniapp_audit_paint`、`miniapp_audit_incident`、`miniapp_audit_package` 和 `miniapp_audit_result`;
412
+ - `auditContext` 接收 `reviewTaskId`、`packageVersion` 和可选制品哈希;未传哈希时由 Remote Client 对 ZIP 或稳定排序后的文件表计算 SHA-256;
413
+ - 终态立即 flush,Runtime 卡死、会话销毁和老 Runtime 无内部 telemetry 时由宿主补终态;
414
+ - APM 请求检查 HTTP 状态,网络错误和 5xx 最多尝试三次,4xx 不重试;
415
+ - `onTrackerDelivery` 返回批次是否成功、HTTP 状态、尝试次数和事件数,Playground 会把回执显示在事件区;
416
+ - 模拟器服务端没有评测数据接口或存储。
417
+
418
+ 业务接入和数据运营需要完成:
419
+
420
+ - Mars 创建模拟器时传入真实队列项和包版本,并消费评测报告;
421
+ - 确认六类事件在 APM 侧关闭采样、字段可检索、配额和保留周期满足校准周期;
422
+ - 建设 APM 数据导出、时序重建、阈值回放、请求成功率和分析看板。
@@ -0,0 +1,165 @@
1
+ # 小工具体验评测本地验证
2
+
3
+ ## 验证结论
4
+
5
+ 一期可在本地模拟 Mars 队列逐包采集、真实操作、停止评测和结算,并产出 `scored`、`unscorable` 或 `loadFailed` 三态报告。2026-10-16 输入目录共 13 个 ZIP:已有 5 个首屏验证记录,本轮检查其余 8 个;其中 7 个实际操作后出分,森林游戏仅到选关地图,受浏览器操作工具限制,未完成主循环验证。不能将这 13 个包表述为全部通过当前策略实操验收。
6
+
7
+ 当前策略为 `enforce: false`。报告用于观察、排序和人工复核,不作为自动放行结论。出现 `contextLost`、持续堆增长、显存超线或 guest crash 时,面板统一显示红色“需复核”;总分不能覆盖硬性风险。JS 异常默认权重为 0.5,`blocking.jsFatal: false`,保留异常数量与事故证据。
8
+
9
+ 审核队列的平均处理时间约为 1 分钟,不是固定 1 分钟倒计时。可评分的最低证据是 30 秒有效采集、30 个快照、至少 1 次真实输入,并满足最低指标覆盖度、采集完整且无基础设施异常;策略最长时钟为 2 分钟。审核员按既有节奏实际体验,离开整个模拟器容器(含留白与评测面板) 30 秒后自动结束并上报,也可手动停止。证据不足时保留数据,不为等分延长审核。OOM、泄漏、晚发 JS 异常等疑点可进入专项复测。
10
+
11
+ ## 离开区域自动结束验证(2026-10-21)
12
+
13
+ Mars 策略配置 `autoStopOnLeaveMs: 30000`;默认策略版本 `phase1-fps-priority-v6-2026-10-23`。离开自动结束、移回取消、真实输入延后、重复停止、guest reload、销毁及禁用分支通过定向自动化验证;联动测试确认正常停止产生一份终态报告,保留 `unscorable`,并映射 `stop_reason` 与 `auto_stop_on_leave_ms`。
14
+
15
+ 在 5286/5284 模拟器整页刷新后加载真实 `瀑布流页面.zip`,搜索 `124917` 显示 1 张封面。边界使用完整的 `miniapp-simulator-host`,覆盖设备、蓝色留白和评测侧栏。点击手机右侧蓝色留白后,采集从第 15 秒持续到第 28 秒,未显示离开倒计时;再次停在留白处,采集从第 94 秒持续到第 107 秒。
16
+
17
+ 会话 `05b5c521-4526-48c8-b5f4-2627babcf5a1` 在 120 秒上限正常结束:有效采集约 115 秒、116 样本、1 次输入,报告 `scored`。策略版本 `phase1-fps-priority-v4-2026-10-21`,报告携带 `stopReason: duration_limit` 和 `autoStopOnLeaveMs: 5000`,终态附近 `trackerDelivery` 为 HTTP 200。不代表 APM 后台已完成字段登记或入库验证。
18
+
19
+ 模拟器定向测试 44 项、插件测试 69 项通过,两个包的类型检查、模拟器 npm 构建和远程开发构建通过。自动化点击容器外部后,浏览器的实际 `:hover` 状态仍在容器内,因此完整的移出、返回链路尚不能据此视为浏览器验收通过。真实 Mars 联调需覆盖:手机、留白与面板之间移动继续采集,移到外层审核表单开始倒计时,30 秒内返回取消,连续离开到期只上报一次,以及到期前切换队列按中断处理。
20
+
21
+ ![真实包在容器内持续采集](./assets/audit-auto-stop-config-2026-10-21.jpg)
22
+
23
+ ## LCP 输入边界验证(2026-10-20)
24
+
25
+ 使用 `/Users/wangchen1/Downloads/小工具测试包/瀑布流页面.zip`,每轮完整刷新 5286/5284 模拟器,开启 Mars 模式、加载真实 ZIP 并点击开始审核。策略 `phase1-fps-priority-v3-2026-10-19`,规则 `1.2.0`,探针 `1.1.1`。
26
+
27
+ | 操作路径 | FCP / LCP | 首次收口事件 / 导航时间 | 有效采集 / 样本 / 输入 | 结果 |
28
+ | --- | --- | --- | --- | --- |
29
+ | 先搜索 124101 命中 1 张,清空搜索,切换古风分类,翻到第 2/2 页,停止评测 | 44ms / 52ms | input / 7730ms | 51s / 52 / 2 | scored,100 分 |
30
+ | 先点击古风分类,搜索 124917 命中 1 张,清空搜索,恢复全部 32 张,停止评测 | 32ms / 40ms | touchstart / 7118.5ms | 70s / 71 / 3 | scored,100 分 |
31
+
32
+ 两轮候选元素均为 `img`;首次输入固定截止时间,后续业务重绘不改变首屏 LCP,停止评测保留首次收口原因。数值为本机浏览器的 guest 导航观测,不代表移动端实测结果。输入次数按 200ms 去重,不等于操作步骤数。
33
+
34
+ 自动化验证覆盖 `input/change/click` 单独触发、截止前待派发候选、截止后候选、迟到的较早记录,以及停止请求保留首次截止时间。插件 69 项、上报及宿主链路 24 项测试通过;插件和模拟器类型检查、远程开发构建通过。
35
+
36
+ 两轮终态投递均返回 HTTP 200。`miniapp_audit_paint` 和 `miniapp_audit_result` 的字段映射已核对,包含 `lcp_finalized_at_ms`、`paint_finalize_event`、`lcp_element_tag`、`lcp_element_size`;APM 后台入库和字段可检索性未在此验证中确认。
37
+
38
+ 第一轮会话:`bf0fe988-395b-4e00-ae2c-c9ddeb5301b1`;第二轮会话:`c5811fff-02ea-4cb4-8078-df80e53b76ab`。
39
+
40
+ ![瀑布流真实操作与 LCP 报告](./assets/audit-lcp-input-2026-10-20.jpg)
41
+
42
+ ## 面板与长任务验证(2026-10-16)
43
+
44
+ 当前默认策略为 `phase1-balanced-v2-2026-10-16`,规则版本 `1.1.2`,探针版本 `1.1.0`。分类汇总与明细按运行性能、包体积、稳定性、内存排列,稳定性当前只有 JS 异常,单独上报 `score_stability`。
45
+
46
+ 长任务权重为 1,≤500ms 为 100 分,500~3000ms 线性递减,≥3000ms 为 0 分。定向核验:500ms 为 100 分、567ms 为 97 分、1000ms 为 80 分、3000ms 为 0 分。
47
+
48
+ 真实 `生化危机.zip` 实操记录(策略 `phase1-balanced-2026-10-16`、规则 `1.1.1`):已执行开始剧情、跳过演出、移动、转向、举枪、攻击和放下;有效采集 38s、39 个样本、7 次操作。最长任务 598ms 得 96 分;稳定性汇总为 1/1,内存汇总为 26/26。报告为 scored、99 分,命中 `contextLost`,面板保留需复核与硬风险提示。
49
+
50
+ ![真实游戏的分类顺序与长任务评分](./assets/audit-balanced-panel-2026-10-16.jpg)
51
+
52
+ ## FCP/LCP 真实包验证(2026-10-16)
53
+
54
+ 环境:独立 worktree,页面 `http://127.0.0.1:5286/simulator?server=http://127.0.0.1:5284/simulator-dev/`。策略 `phase1-paint-2026-10-16`,规则/探针 `1.1.0`。逐包完整刷新模拟器、加载目录中的真实 ZIP 并执行实际功能;下表不包含 SDK 示例包。
55
+
56
+ | ZIP | 实际操作 | 有效采集 / 样本 / 操作 | FCP / LCP | 结果 |
57
+ | --- | --- | --- | --- | --- |
58
+ | 编钟模拟器.zip | 进入五音章节,敲青钟、赤钟,画面和文字切换 | 44s / 45 / 3 | 13252ms / N/A | scored,74 分;没有 LCP 候选,未补零 |
59
+ | 像素旅行游戏.zip | 开始闯关、选天坛关卡与领队、关闭教学、瞄准并发射弹珠 | 50s / 51 / 6 | 224ms / 324ms | scored,91 分;LCP 首次操作收口 |
60
+ | mbti算命.zip | 完成 28 题并进入 ESTJ 结果页 | 78s / 79 / 29 | 128ms / 128ms | scored,89 分;首屏项均为 100 分 |
61
+ | webgl-中秋赏月.zip | 启程、打开夜色选择、填写测试愿望并进入封灯步骤 | 77s / 78 / 7 | 228ms / 228ms | scored,94 分;WebGL 内存与首屏均有读数 |
62
+ | 生化危机.zip | 开始剧情、跳过演出、移动、转向、举枪、攻击和放下 | 43s / 44 / 8 | 128ms / 128ms | scored,95 分;命中 contextLost,需复核 |
63
+
64
+ FCP/LCP 是本地已装载包的 guest 导航绘制值,不包含 ZIP 上传与解压。编钟包本次 FCP 在进入章节附近出现,LCP 没有原生候选;该值仅作为当次观测,不据此认定包普遍存在 13 秒首屏问题。游戏封面/文字的 LCP 不代表 Canvas 游戏主循环就绪,核心场景仍由真实操作确认。
65
+
66
+ WebGL 包已核对真实发送路径的 APM 请求体:`measurement_name=miniapp_simulator_event`,`event_name=miniapp_audit_paint`;result 同时携带 `fcp_ms/lcp_ms`、状态、`lcp_finalized: 1`、`score_fcp/score_lcp: 100`、`fcp_threshold_ms: 1800`、`lcp_threshold_ms: 2500` 和版本字段。所有布尔语义均为 0/1。首屏发送与终态发送均观测到 HTTP 200;APM 平台落入和可检索性需通过平台查询确认。
67
+
68
+ [真实 APM 请求摘录](./assets/audit-paint-webgl-apm-excerpt-2026-10-16.json)
69
+
70
+ ![真实 WebGL 包首屏评分](./assets/audit-paint-webgl-2026-10-16.jpg)
71
+
72
+ ![真实 3D 游戏首屏评分与需复核状态](./assets/audit-paint-game3d-2026-10-16.jpg)
73
+
74
+ ## 审核员操作 SOP
75
+
76
+ ```text
77
+ 进入队列项
78
+ → 完整重载模拟器,隔离上一个包的 Realm、计时器和资源
79
+ → 点击开始审核,先安装 document-start 探针再加载小工具
80
+ → 真实使用主要功能:输入、选择、跳转、计算、播放或游戏操作
81
+ → 检查结果页、主要功能输出或游戏中的操作响应
82
+ → 移向 Mars 审核表单;离开整个模拟器容器(含留白与评测面板) 30 秒后自动结束并上报
83
+ → 30 秒内移回可继续评测;也可点击“停止评测”,最长 2 分钟自动结算
84
+ → 查看三态结果、硬性风险和证据质量
85
+ → 保存结果并进入下一队列项,重新完整重载模拟器
86
+ ```
87
+
88
+ 只打开页面、等待计时或仅点击“开始审核”不构成有效评测。没有可操作控件的静态诊断包应得到 `unscorable`,而不是为了出分伪造一次点击。
89
+
90
+ 审核员无需额外确认停止。手机、内部留白和评测面板之间移动不会自动结束,鼠标在外但仍用键盘操作小工具时会延后倒计时;停止不代表功能检查通过,点击数也不自动证明业务流程完成。样本、时长、交互或覆盖度不足时保留数据和具体原因,审核员可按现有业务规则提交并继续下一项。自动结束与上报不依赖 Mars 接口;倒计时到期前就刷新时按中断处理。报告等待或投递失败不得阻塞队列。
91
+
92
+ ## 剩余 8 个 ZIP 实操验证(2026-10-16)
93
+
94
+ 目录:`/Users/wangchen1/Downloads/小工具测试包/`。环境同本页 5286/5284 模拟器;策略 `phase1-balanced-v2-2026-10-16`、规则 `1.1.2`、探针 `1.1.0`。每包均完整刷新宿主模拟器,再选择真实 ZIP、开始审核;除标明的森林 PC 复试外,设备为 iPhone 15 Pro。测试期间不调整策略。
95
+
96
+ | ZIP | 实际操作与覆盖范围 | 有效秒 / 样本 / 输入 | FCP / LCP(ms) | 结果 |
97
+ | --- | --- | --- | --- | --- |
98
+ | 拜月亮.zip | 地点与角色、摆案、五类供品、点烛起香、念祝词与叩拜;流程到第 5/9 阶段,未走完全部仪式 | 115 / 116 / 12 | 132 / 168 | scored,100 分;2 分钟上限自动结算 |
99
+ | 深夜读信人-windows黑屏.zip | 打开来信、点击“83天”线索、逐段读完一封信、进入隐私编辑、暂缓保存草稿,返回案头 | 56 / 57 / 8 | 72 / 140 | scored,98 分;解码图片约 24MB,未公开信件 |
100
+ | 九霄宗门模拟器.zip | 创建测试家族、派遣 4 名游手、族议、灵田升 2 级、任命薛鸿为掌门、查看弟子 | 67 / 68 / 6 | 104 / 140 | scored,100 分;出现 SDK 剪贴板桥异常,见下表 |
101
+ | jianshouji-minitool-20260730-v10.zip | 用户确认协议后创建虚构角色、指定“我”、生成 1 条虚构聊天、命名保存,退出后在草稿箱核对作品 | 62 / 63 / 15 | 104 / 104 | scored,100 分;未点击对外发布 |
102
+ | 瀑布流页面.zip | 搜索 124101 命中 1 张、古风分类 9 张、全部 32 张逐页翻到第 4/4 页并返回、空分类与恢复全部 | 41 / 42 / 10 | 96 / 4196 | scored,87 分;LCP 单项 45 分,长任务 62ms 不扣分 |
103
+ | 答题错题页面滚动.zip | 填写测试姓名、逐题完成 15 题、进入成绩单(5 对 10 错)、End 键使 body 滚动 1149.5px 查看错题 | 67 / 68 / 18 | 96 / 96 | scored,100 分;业务答题成绩 34 分不是质量评分 |
104
+ | forest-fire-water-rafloop-fix.zip | 竖屏点击开始到选关地图;完整刷新 PC 复试;嵌套缩放 iframe 的坐标操作被浏览器工具拒绝,未进入关卡 | 97 / 98 / 1;PC 41 / 42 / 1 | 96 / N/A;PC 140 / N/A | 两次 unscorable,核心场景未确认;不计为已完成游戏实操 |
105
+ | 诗云星图-试用版.zip | 显示全部、搜索李白(11 关系)、点击结果、开始/停止巡游、自动播放并看到诗人高亮切换、停止 | 50 / 51 / 5 | 160 / 160 | scored,100 分;31 次长任务,最长 433ms;FPS P5 21.5 |
106
+
107
+ “捡手机”接受协议前另有一次 34 秒、35 样本、0 输入、未确认核心场景的试跑,结果为 `unscorable`。它验证证据门槛,不计入实际创作完成数。输入数按探针去重口径记录,不等于所有点击和按键次数。
108
+
109
+ 7 个出分报告均未观察到业务 JS 异常,未命中硬风险。这个样本集不能用于证明 JS 异常权重已完成实证校准,也不能证明不存在晚发异常或手机 OOM。各次终态附近的 `trackerDelivery` 均记录 HTTP 200;该回执不能代替 APM 平台落库查询。
110
+
111
+ [本轮 10 次有留存的报告、操作路径与回执](./assets/audit-remaining-results-2026-10-16.json)(8 个包,含协议门槛试跑和森林两种设备;JSON 中 `report.versions` 保留版本)。
112
+
113
+ ![星图实际运行与评分](./assets/audit-poetry-2026-10-16.jpg)
114
+
115
+ ## 指标与评分待处理项
116
+
117
+ 本轮 7 个可评分样本中,5 个为 100 分、1 个 98 分、1 个 87 分。首屏评分能区分较慢的 LCP,但当前总分主要反映所测窗口内的技术预算,不等于全部功能、持续流畅性或长时内存安全验收。
118
+
119
+ | 优先级 | 实测证据与代码口径 | 调整方向 |
120
+ | --- | --- | --- |
121
+ | 高 | 星图是诗人关系可视化,报告识别为 `game3d`、`tagMismatch=true`;`rules/applicable.ts` 将声明 tool 且检测到 WebGL 的包改判 game3d。7.6MB 包因采用游戏 profile 的 12MB 满分阈值得 100 分 | WebGL 能力与业务类型分别判断;支持关系图、地图等非游戏场景,避免仅按技术栈放宽包体和内存预算 |
122
+ | 高 | 5/7 个出分包的 `peakHeapBytes=0`。`rules/derive.ts` 使用 `max(0, heapP95-baseline)`;基线在 Runtime 读取、样本在 guest 读取。校正结果为 0 不代表实际不占内存 | 面板明确基线校正口径;终态同时保留原始 P95、基线与校正值,验证跨 Realm/共享进程及 GC 的影响,再用于阈值校准 |
123
+ | 高 | 星图 `fpsP5=21.5`、`fpsMedian=30`;`rules/evaluate.ts` 面板项使用中位数,但 `panel/copy.ts` 描述为 P5 | 统一面板标签、取值和说明;APM 的 P5 与中位数明确区分;FPS 权重保持 0 时仅作观察 |
124
+ | 中 | 星图约 50 秒内有 31 次长任务,最长 433ms,长任务单项仍为 100 分;当前只按最长一次计分 | 保持长任务权重 1;结合次数、持续交互场景观察,评估补充累计阻塞时长/占比,不能只凭最长任务合格认定持续流畅 |
125
+ | 中 | 九霄等包控制台出现 `installPageClipboardBridge` 的 `Cannot redefine property: clipboard`;探针按精确特征忽略该 SDK 错误,业务 JS 与基础设施错误计数均为 0 | SDK 问题单独诊断上报,不扣业务 JS 分;已知可忽略错误保留计数,避免基础设施故障完全不可见 |
126
+ | 中 | 拜月亮在第 5/9 阶段达到 2 分钟上限;`quality.complete=true` 但业务流程未全部覆盖 | 区分采集完整和业务路径完成;复杂包记录阶段覆盖,必要时追加专项长跑,不延长所有队列项 |
127
+ | 待补测 | 森林两次均未进入关卡,LCP 无原生候选;首屏 96ms 不能代表游戏就绪 | 使用可操作该画布的浏览器或人工选关后,补测移动、跳跃和关卡交互;在此之前不做该游戏评分合理性的结论 |
128
+
129
+ 内存短窗口正斜率不是泄漏的充分证据,面板中的“长会话必然 OOM”“唯一真正可信”表述不适合作为审核结论。以上为待实施项,本轮未改评分代码或评分策略。
130
+
131
+ ## 一期已覆盖的指标
132
+
133
+ | 维度 | 当前采集与处理 |
134
+ | ---------- | ------------------------------------------------------------------------------------------ |
135
+ | JS 内存 | 峰值堆、稳定段最小二乘增长速率;样本不足时不计算斜率 |
136
+ | 图形内存 | WebGL texture/buffer/renderbuffer 配对记账水位、Canvas 后备缓冲、图片解码内存 |
137
+ | 运行稳定性 | JS error/unhandled rejection、`webglcontextlost`、guest crash、Runtime hang |
138
+ | 运行性能 | Guest FCP/LCP、rAF 窗口 FPS P5 与中位数、采集期最长单次 Long Task 与次数 |
139
+ | 能力资源 | WASM Memory、存活 Worker 数;不适用项明确为 N/A,不进入总分分母 |
140
+ | 包体 | 解压总体积、最大单图、单个未压缩 JS |
141
+ | 证据质量 | 有效时长、样本数、真实操作次数、首次操作时间、覆盖度、采集是否完整、探针是否饱和 |
142
+
143
+ 一期无法用桌面浏览器直接证明真实手机 OOM,`performance.memory` 也只覆盖 JS 堆。因此一期输出风险信号,不把“桌面未 OOM”写成“移动端安全”。二期需要关联真机 RSS/PSS、GPU 驱动占用、系统内存告警、进程终止/崩溃原因和低中高端机型分层样本。
144
+
145
+ ## 一期上线前置项
146
+
147
+ | 前置项 | 验收结果 |
148
+ | --------- | ------------------------------------------------------------------------------------------- |
149
+ | 逐包隔离 | 队列切换必须整页重载模拟器,不能复用上一个 guest Realm |
150
+ | 有效使用 | 至少 1 次输入到达 guest;审核员按 SOP 检查主要功能,静态无交互包不出分 |
151
+ | 异常收口 | Runtime 12 秒无进度产生 `loadFailed:RUNTIME_HUNG`;熔断和失联产生 `unscorable` 或结构化失败 |
152
+ | 结论显示 | 硬风险统一为“需复核”,总分红色显示,不允许只按分数放行 |
153
+ | APM 上报 | 上报任务、应用、包版本、策略版本、1 Hz 时序、FCP/LCP、包体摘要、操作证据、三态终态和风险 |
154
+ | Mars 集成 | 在真实审核队列完成连续 20 包回归,确认切换、超时、刷新和结果保存不阻塞审核员 |
155
+ | 策略状态 | 保持 `enforce: false`,完成真机相关性与阈值回放后再评估自动卡口 |
156
+
157
+ ## 验证命令
158
+
159
+ ```bash
160
+ pnpm --dir packages/miniapp-audit-plugin test
161
+ pnpm --dir packages/miniapp-audit-plugin typecheck
162
+ pnpm --dir packages/miniapp-simulator exec vitest run tests/remote-audit-watchdog.test.ts tests/remote-audit-lazy-load.test.ts
163
+ pnpm --dir packages/miniapp-simulator typecheck
164
+ pnpm --dir packages/miniapp-simulator build:remote:dev
165
+ ```