@harmonyos-arkts/d2h 0.1.11 → 0.1.13

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 (126) hide show
  1. package/.claude-plugin/marketplace.json +15 -13
  2. package/.claude-plugin/plugin.json +13 -7
  3. package/AGENTS.md +5 -5
  4. package/README.md +212 -212
  5. package/agents/android-to-hmos-00-orchestrator.md +318 -318
  6. package/agents/d2h.md +157 -157
  7. package/dist/bin/install-opencode.js +30 -30
  8. package/docs/environment-check.md +86 -86
  9. package/install-opencode.sh +160 -160
  10. package/opencode/agents.json +16 -16
  11. package/opencode/vscode-d2h-package.json +7 -7
  12. package/opencode/vscode-d2h-plugin.mjs +29 -29
  13. package/package.json +54 -54
  14. package/schemas/android-source-manifest.schema.json +25 -25
  15. package/schemas/checkpoint-provenance.schema.json +67 -67
  16. package/schemas/final-acceptance.schema.json +29 -29
  17. package/schemas/managed-evidence-index.schema.json +15 -15
  18. package/schemas/managed-evidence.schema.json +114 -114
  19. package/schemas/migration-config.schema.json +51 -51
  20. package/schemas/migration-report-index.schema.json +39 -39
  21. package/schemas/migration-report-item.schema.json +51 -51
  22. package/schemas/migration-report-summary.schema.json +25 -25
  23. package/schemas/migration-status.schema.json +202 -202
  24. package/schemas/preflight.schema.json +60 -60
  25. package/schemas/source-order-audit.schema.json +16 -16
  26. package/schemas/source-provenance-event.schema.json +19 -19
  27. package/schemas/spec-app-shard.schema.json +45 -45
  28. package/schemas/spec-fact-corrections-shard.schema.json +13 -13
  29. package/schemas/spec-features-shard.schema.json +43 -43
  30. package/schemas/spec-index.schema.json +26 -26
  31. package/schemas/spec-interactions-shard.schema.json +40 -40
  32. package/schemas/spec-page.schema.json +98 -98
  33. package/schemas/spec-pages-index.schema.json +1 -1
  34. package/schemas/spec-unresolved-shard.schema.json +1 -1
  35. package/schemas/task-envelope.schema.json +109 -109
  36. package/schemas/task-plan.schema.json +341 -341
  37. package/scripts/check-environment.mjs +320 -320
  38. package/skills/android2hmos_resources_convert/SKILL.md +180 -180
  39. package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -230
  40. package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -175
  41. package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -513
  42. package/skills/arkts-app-identity/SKILL.md +238 -238
  43. package/skills/arkts-i18n/SKILL.md +496 -496
  44. package/skills/arkts-i18n/evals/evals.json +84 -84
  45. package/skills/arkts-i18n/references/code-examples.md +302 -302
  46. package/skills/arkts-i18n/references/common-pitfalls.md +391 -391
  47. package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -604
  48. package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -348
  49. package/skills/arkts-i18n/references/language-codes.md +104 -104
  50. package/skills/arkts-i18n/references/resource-file-structure.md +775 -775
  51. package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -242
  52. package/skills/arkts-i18n/references/v1-compat.md +244 -244
  53. package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -174
  54. package/skills/arkts-icon-sizing/SKILL.md +211 -211
  55. package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -131
  56. package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -88
  57. package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -179
  58. package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -119
  59. package/skills/arkts-mvvm-architecture/SKILL.md +613 -613
  60. package/skills/d2h-hypium-forge/SKILL.md +309 -309
  61. package/skills/d2h-hypium-forge/references/functional-spec-template.md +140 -140
  62. package/skills/d2h-hypium-forge/references/official-api.md +135 -135
  63. package/skills/d2h-hypium-forge/references/pitfalls.md +98 -98
  64. package/skills/d2h-hypium-forge/references/testability-template.md +71 -71
  65. package/skills/d2h-hypium-forge/scripts/_common_template.py +659 -659
  66. package/skills/d2h-hypium-forge/scripts/analyze_results.py +165 -165
  67. package/skills/d2h-hypium-forge/scripts/assert_audit.py +107 -107
  68. package/skills/d2h-hypium-forge/scripts/collect_runtime_pages.py +169 -169
  69. package/skills/d2h-hypium-forge/scripts/forge_report.py +325 -325
  70. package/skills/d2h-hypium-forge/scripts/inject_resource_ids.py +114 -114
  71. package/skills/d2h-hypium-forge/scripts/lint_asserts.py +80 -80
  72. package/skills/d2h-hypium-forge/scripts/lint_locate.py +60 -60
  73. package/skills/d2h-hypium-forge/scripts/merge_appgraph.py +249 -249
  74. package/skills/d2h-hypium-forge/scripts/precondition.py +140 -140
  75. package/skills/d2h-hypium-forge/scripts/preinject_ids.py +232 -232
  76. package/skills/d2h-hypium-forge/scripts/probe.py +197 -197
  77. package/skills/d2h-hypium-forge/scripts/run_direct.py +823 -823
  78. package/skills/d2h-hypium-forge/scripts/run_parallel.py +121 -121
  79. package/skills/d2h-hypium-forge/scripts/scan_features.py +255 -255
  80. package/skills/d2h-hypium-forge/scripts/upgrade_asserts.py +66 -66
  81. package/skills/d2h-hypium-forge/scripts/validate_scenarios.py +84 -84
  82. package/skills/d2h-page-develop/SKILL.md +112 -112
  83. package/skills/d2h-page-develop/agents/openai.yaml +7 -7
  84. package/skills/d2h-page-spec-generate/SKILL.md +206 -206
  85. package/skills/d2h-page-spec-generate/scripts/build_batch_report.py +227 -227
  86. package/skills/d2h-utg-ai-generate/SKILL.md +128 -128
  87. package/skills/d2h-utg-ai-generate/references/click-paths-schema.json +76 -76
  88. package/skills/d2h-utg-ai-generate/references/extraction-guide.md +34 -34
  89. package/skills/d2h-utg-ai-generate/references/facts-contract.md +96 -96
  90. package/skills/d2h-utg-ai-generate/references/page-schema.json +61 -61
  91. package/skills/d2h-utg-ai-generate/references/utg-schema.json +89 -89
  92. package/skills/d2h-utg-ai-generate/scripts/build_page_json.py +314 -314
  93. package/skills/d2h-utg-ai-generate/scripts/build_utg.py +123 -123
  94. package/skills/d2h-utg-ai-generate/scripts/capture_from_page_json.ts +280 -280
  95. package/skills/d2h-utg-ai-generate/scripts/utg_common.py +524 -524
  96. package/skills/d2h-utg-ai-generate/scripts/utg_html.py +489 -489
  97. package/skills/d2h-utg-ai-generate/scripts/validate_utg.py +69 -69
  98. package/skills/d2h-utg-ai-generate/scripts/vendor/D3-LICENSE +13 -13
  99. package/skills/d2h-utg-ai-generate/scripts/vendor/d3-7.9.0.min.js +2 -2
  100. package/skills/d2h-utg-ai-generate/tests/test_pipeline.py +369 -369
  101. package/skills/d2h-utg-static-generate/SKILL.md +73 -73
  102. package/skills/d2h-utg-static-generate/references/discoveries.md +38 -38
  103. package/skills/d2h-utg-static-generate/scripts/generate.py +499 -499
  104. package/skills/d2h-utg-static-generate/tests/test_static.py +335 -335
  105. package/skills/d2h-utg-static-generate/tests/verify-offline-html.py +60 -60
  106. package/skills/d2h-utg-static-generate/tests/verify-produced-fixture.py +131 -131
  107. package/skills/harmonyos-migration-playbook/SKILL.md +83 -83
  108. package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -7
  109. package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -24
  110. package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -53
  111. package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -45
  112. package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -23
  113. package/skills/harmonyos-migration-playbook/references/ui-automation.md +53 -53
  114. package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -38
  115. package/skills/maintaining-migration-report/SKILL.md +144 -144
  116. package/skills/maintaining-migration-report/references/final-acceptance.md +48 -48
  117. package/skills/maintaining-migration-report/references/source-provenance.md +66 -66
  118. package/skills/managing-d2h-toolchain/SKILL.md +24 -24
  119. package/skills/managing-d2h-toolchain/environment-requirements.json +16 -16
  120. package/skills/managing-d2h-toolchain/scripts/manage-toolchain.mjs +98 -98
  121. package/skills/managing-d2h-toolchain/toolchain.json +16 -16
  122. package/skills/native-library-substitution/SKILL.md +384 -384
  123. package/skills/native-library-substitution/references/native-library-substitution.json +56905 -56905
  124. package/skills/native-library-substitution/references/native-library-substitution.md +163 -163
  125. package/skills/preparing-migration-workspace/SKILL.md +131 -131
  126. package/skills/reviewing-migration-process/SKILL.md +62 -62
@@ -1,309 +1,309 @@
1
- ---
2
- name: d2h-hypium-forge
3
- description: 鸿蒙应用/元服务测试用例设计与 hypium 执行(两阶段流水线第二段,部署于 .opencode 迁移 agent)。宿主 agent 代码理解产出功能场景库(功能/说明/是否已转化)→ 本 skill 按场景测试目的选断言范式、LLM 白盒精写 Python 脚本 → 裸跑执行与失败四簇分析。适用于任何 ArkTS 工程的功能测试自动化。触发词:hypium-forge、功能用例设计、测试用例生成执行、覆盖度评估、鸿蒙 UI 测试全流程。
4
- ---
5
-
6
- # d2h-hypium-forge — LLM 白盒精写 → 裸跑执行
7
-
8
- ## 体系边界(先分清)
9
-
10
- 本 skill **只服务 Python 版 Hypium**(devicetest 框架,`testcases/*.py`,UiDriver 经 uitest RPC 驱动设备)。
11
- **ArkTS @ohos/hypium 是另一条链路,不在本 skill 范围**——模拟器上应用内 uitest napi 是 stub
12
- (`Driver.create()` 恒 null,ENV 实证),ArkTS 用例仅适用于真机且需独立工程配置;文档中两套
13
- API 同名不同构(BY 定位/断言语义有差异),混用必然执行失败。涉及 ArkTS 用例需求请走
14
- 单独链路(ohosTest 模板),禁止在本 skill 产物目录混建。
15
-
16
- ## 三条铁律
17
-
18
- 1. **必须先预置控件 ID(写脚本之前)**:`preinject_ids.py` 对全部交互点机械注入,
19
- 脚本只认 ids.json/既有 ID(BY.key)。文本定位(BY.text)慢 10~100 倍且有歧义。
20
- 2. **Level 0 前置自适应(冷启三件套,launch 原语内置,用例不得自带)**:
21
- 首启弹窗 dismiss(多文案轮询幂等)+ 首页 tab 归位(HOME_TAB 应用级配置,
22
- 冷启默认 tab 常与目标 tab 不一致)+ 语义锚等待(禁依赖注入 id 等 Loading 层控件)。
23
- 隐私弹窗常为内存态(冷启必弹)——每条冷启都必须 dismiss(TD-007)。
24
- 3. **权限和账号前置完成**:precondition.py 预授可授权限/登录策略/首启弹窗 dismiss,
25
- 绝不留给用例执行时撞。**登录态跨用例保持**:软隔离(stop_app/start_app)不清应用数据;
26
- `reset_data=True`(bm clean)只用于首启态用例且**须排尾/独立分组执行**——与登录用例
27
- 交错会反复清登录态导致重复登录注册(CloudReader TD-004 实证:交错执行重复注册 30 次)。
28
-
29
- ## 五步流水线
30
-
31
- 设 `$F = 本skill/scripts`,工程 `$P`,产物目录 `$P/.hypium_forge/`。
32
-
33
- ### 1. 功能场景库(测试设计的基础输入)
34
-
35
- 本 skill 是两阶段流水线的第二段:代码理解由宿主 agent(.opencode 迁移 agent)完成,
36
- 产出功能场景清单后调用本 skill 生成并执行测试。**场景库是测试设计的唯一基础**——
37
- 它的完备性与准确度决定下游全部产出的质量,宁可在此多花时间。
38
-
39
- **阶段一(宿主 agent)**——按 `references/functional-spec-template.md` 产出功能规格
40
- `functional_spec.md`,再翻译为 `scenarios.json`:
41
- ```json
42
- {"id":"WF-01","name":"搜索选中城市并收藏","domain":"位置管理",
43
- "desc":"用户在搜索页输入关键字,点选结果后返回管理页,列表新增该城市并提示收藏成功",
44
- "converted":true, "source_ref":"SearchActivity.java:181-195",
45
- "test_intent":"功能交互类"}
46
- ```
47
- 生成规则:
48
- - 场景单位 = **用户动宾行为**(禁 Activity/Fragment/surface 类名当场景);
49
- - `desc` 写清「输入→动作→**可观察业务效果**」,效果描述到可翻译成断言的程度;
50
- - 行为预期从源码推导(默认值/联动/守卫),不从直觉——直觉必错;
51
- - `converted` 与迁移报告对齐,未转化标 false 并跳过。
52
-
53
- **阶段二(本 skill)**——先过**场景库质量门禁**再消费:
54
- ```bash
55
- python3 $F/validate_scenarios.py scenarios.json # 不通过项入 scenarios_blocker.md
56
- ```
57
- 校验:①必备字段(id/name/desc/converted/test_intent)②desc 含可观察业务效果关键词
58
- (新增/删除/修改/保存/收藏/取消/成功/失败/提示/计数/状态/切换——纯"进入XX页"类导航词
59
- 无效果=拦截)③入口可定位(深层链路 desc 须写前置路径)④行为预期有源码/PRD 依据标注
60
- ⑤converted=false 必须带 reason(未迁移/能力缺失/暂不测试)。**blocker 场景不进用例生成**,
61
- 退回宿主 agent 修复。通过后:converted=true 逐场景设计用例
62
- (按 test_intent 选断言范式)→ 白盒精写 → 执行 → 分诊 → defects/报告。
63
- 用例与场景一一对应,无场景不写用例。
64
-
65
- ### 1.5 机械扫描(覆盖性评估的补充)
66
-
67
- ```bash
68
- python3 $F/scan_features.py $P --out $P/.hypium_forge/features.json
69
- ```
70
- 产出:页面(路由+@Entry)× 交互点 × 状态矩阵 + 权限/登录检测。有安卓迁移现场时
71
- `merge_appgraph.py --features ... --facts <android>/.migration/spec/facts` 注入权威行为语义
72
- (handler 链揭示联动场景)。
73
-
74
- **定位是补充而非基础**:features 条目是定位事实不是业务功能,机械扫描可靠性不足,
75
- 不得直接作为测试设计输入。两个用途:
76
- 1. **覆盖校验**:对照 scenarios.json,找出未被任何场景覆盖的页面/交互点 →
77
- 反馈宿主 agent 裁决「补场景」或「登记不测并说明理由」——遗漏即测试盲区;
78
- 2. **ID 预置清单**:交互点列表供步骤 3 preinject_ids.py 机械注入。
79
-
80
- 覆盖结论写入 `coverage.md`,**双口径分列(防机械冒充功能覆盖)**:
81
- - **场景覆盖度(主口径)** = 被采纳用例触达的场景数 / scenarios.json 中 converted=true 场景数;
82
- - **控件触达率(辅助口径)** = 已触达交互点 / 全部交互点——仅暴露机械盲区,**不得单独
83
- 作为功能覆盖率汇报**(把扫到的控件数当功能覆盖是自欺);
84
- coverage.md 逐条列出并按规则处置:
85
- - 未覆盖场景(如 WF-12, WF-19)→ 必须反馈宿主 agent 补场景或登记不测;
86
- - 未触达交互点(如 HomePage.ad_banner)→ 不一定补用例,人工裁决是否为
87
- 广告位/运营位/未迁移能力(判定理由随条目记录)。
88
- 未覆盖交互点及其裁决结果。
89
-
90
- ### 2. 运行时文本采集
91
- ```bash
92
- python3 $F/collect_runtime_pages.py --device <sn> --bundle <b> --out runtime_pages.json
93
- ```
94
- 脚本引用的控件文本**只允许**来自采集结果;断言锚定业务数据,禁止导航回声断言。
95
- **并行提速**:首页文本采集完成后即可开始写该页用例(其余页面采集继续)——采集与编写
96
- 流水线并行,禁串行等全量采集完再动笔。
97
- 启动参数门控应用:先 `aa start --ps k v` 预热,singleton 的 onNewWant 不重置状态可继续采集。
98
-
99
- ### 3. ID 预置(铁律①,先于脚本编写)——机械注入的边界与补强
100
- ```bash
101
- python3 $F/preinject_ids.py --features features.json --out ids.json # 交互点清单来自 §1.5
102
- # 之后 rebuild + uninstall&&install
103
- ```
104
- 批量注入 `tc_<page>_<n>`,产出 ids.json 映射表。脚本定位只认 ids.json/既有 ID(BY.key);
105
- 仅输入框用 BY.hint。
106
-
107
- **静态注入三盲区**(preinject 只扫静态代码,以下场景必然漏注):
108
- 1. 动态列表控件(ForEach 运行时生成,静态无挂载点)→ 用 `collect_runtime_pages.py`
109
- 的 dumpLayout 控件树交叉核对:dump 中 key 为空但语义可定位的条目,回填至
110
- runtime_pages.json 供 BY.text 兜底并登记例外;
111
- 2. 条件渲染分支(if/else 二选一支路)→ 场景变体执行前先 dump 该分支实况;
112
- 3. 异步加载页(网络数据到位才渲染)→ 依赖步骤 2 采集的等待锚,禁用盲等。
113
-
114
- **无 ID 例外审批制**:例外必须三件套——docstring 理由 + runtime_pages.json 中的实测
115
- 证据(该控件确实无 id 且文案唯一)+ lint_locate 通过;例外清单汇总进 coverage.md
116
- 「无 ID 例外」章节备查,审计时逐条复核(防例外变成 BY.text 后门)。
117
-
118
- ### 3.5 输入契约与空库守卫(宿主 agent ↔ 本 skill 的稳定边界)
119
-
120
- **只接受以下输入**:scenarios.json / features.json / ids.json / runtime_pages.json /
121
- _common_template.py / ArkTS 源码路径。**场景库为空或全部 converted=false 时直接报错
122
- 退出,不生成空用例集**(空跑=伪覆盖)。生成规则见步骤 4;执行后必须产出:
123
- testcases/*.py、results.json、coverage.md、summary.json、test-report.html、失败截图与 hilog 片段。
124
-
125
- ### 4. LLM 白盒精写 testcases/*.py(核心)
126
-
127
- 从 `$F/_common_template.py` 复制为 `testcases/_common.py`(launch 带 `--ps k v` 空格分隔、
128
- 3 轮重试探活、首启弹窗 dismiss、强断言原语全套——**写前必读**),逐条编写 `TCxx_<slug>.py`。
129
-
130
- **编写规则:**
131
-
132
- - **先读源码**:默认值/联动规则/时序以 ArkTS 源码为准——臆断默认值必返工;
133
- - **按场景测试目的选断言范式**(问"本场景要证明什么"再选型):
134
-
135
- | 场景类型 | 测试目的 | 正确断言范式 |
136
- |---|---|---|
137
- | 资源渲染类 | 图标/文案加载成功 | exist/equals 即目的本身 |
138
- | 功能交互类 | 操作产生业务效果 | 效果断言(值变化/计数增减/checked 翻转/absent) |
139
- | 配置生效类 | 设置改变全局行为 | 生效链断言(改设置→回业务页→断言输出变化) |
140
- | 导航流转类 | 状态迁移到目的地 | 终点效果(城市选中→顶栏变新城市名) |
141
- | 状态机类 | 状态转移规律 | 前置态断言→触发→后置态断言(成对) |
142
- | 数据输入类 | 输入被正确处理 | 输入→输出值断言(搜索词→结果计数) |
143
-
144
- - **强断言比例按 test_intent 分档**(全局硬门禁 ≥30% 整体维持 40~70%;比例是目的的
145
- 结果不是目标——**禁止加"页面存在/按钮可见"类弱断言凑比例**,单条低于档位下限报
146
- "弱断言风险"提示、超上限检查是否断言了实现细节):
147
-
148
- | test_intent | 强断言区间 | 说明 |
149
- |---|---|---|
150
- | 资源渲染 | 20~40% | 证明图标/文案/占位正确,exist/equals 即目的 |
151
- | 功能交互 | 50~70% | 必须断言业务效果,不能只断言按钮存在 |
152
- | 配置生效 | 60~80% | 改设置→回业务页→验证输出变化的完整生效链 |
153
- | 导航流转 | 40~60% | 终点页面 + 关键状态都要验证 |
154
- | 状态机 | 60~80% | 前置态、触发、后置态成对断言 |
155
- | 数据输入 | 50~70% | 输入→结果列表/计数/排序必须验证 |
156
-
157
- 核心判据:**断言必须证明场景声明的目的**——目的是"功能发生"时仅判存在 = 无效用例;
158
- - **Toast** 用 hilog 关键字轮询或 toast_text_seen(dump 轮询)、**剪贴板**用 hdc pasteboard、
159
- 对话框流转逐步断言;
160
- - **视口外控件**必须用 `touch(BY.text("xx"), scroll_target=BY.type("Scroll"))` 原生滚动查找,
161
- 禁止手工 swipe + find 循环(时序敏感且不稳定);
162
- - **深链路用例内显式 setup**:上游状态准备写成用例内步骤,禁止假设页面已就位;
163
- - **失败 = 信息**:产品缺陷保持 FAIL 暴露,绝不弱化断言换绿灯;用例自身错误可修(≤2 轮);
164
- - **用例数量由功能场景覆盖决定**,禁止固定条数或上限——每个 converted=true 场景至少
165
- 1 条用例真实触达;**不允许脱离场景新增用例、不允许把页面跳转当最终业务效果**;
166
- 文本断言只能来自 runtime_pages.json 实采,不能臆造文案;
167
- - **定位门禁**:`lint_locate.py --dir testcases --ids ids.json`,text 定位 >20% FAIL——
168
- LLM 常绕过预置 ID 用 text_click,唯机械门禁有效;
169
- - 每脚本 docstring 写清行为链与覆盖的功能场景 id;
170
- - 执行后产出 `coverage.md`(含「总覆盖度: N%」行)与 `summary.json`。
171
-
172
- ### 4.5 前置条件分组编排(共用前置,提升执行效率)
173
-
174
- 用例文件声明 `PRECOND`(缺省 anonymous),run_direct 按 **anonymous → logged_in →
175
- first_launch** 分组排序执行(破坏性递增,bm clean 类强制排尾不再回头破坏登录态)。
176
- 组内软隔离共享前置:`launch()` 默认 `reset_data=False`,preferences 保留 → 组首一次
177
- 完整 `ensure_login`,组内后续幂等复用。组首执行时 `FORGE_GROUP_FIRST=1`。
178
- results.json 每条附 `group` 字段可分组统计。**禁把 reset_data=True 用在 logged_in 组**。
179
-
180
- ### 5. 裸跑执行 + 四簇分析 + HTML 报告(重跑阶梯 = 硬规范)
181
-
182
- **执行命令阶梯(--all 仅限首轮与门禁升级,修复轮禁无脑全量;违反会被 runner 硬阻断)**:
183
- > TD-LADDER v3 防护(已内置于 run_direct.py):**所有"显式全量"(`--all` / `--acceptance` / 变相全量的 `--filter`)统一受管**,
184
- > 不再给 `--acceptance` 开后门。五条规则(任一命中即 exit 2 阻断并输出指引):
185
- > ① **R1** 上轮 results.json 存在 FAIL/SKIP → 必须先修(`--case`/`--retry-failed`),禁止全量;
186
- > ② **R2** 源码 `srcHash` 与上次全量相同 → 重复全量无新结论(仅拦 `--all`);
187
- > ③ **R2b** 上次已是**同源验收**(`lastMode=acceptance` 且 srcHash 未变)→ 拒绝重复验收;
188
- > ④ **R3** 本修复周期显式全量已达上限(默认 2,`FORGE_FULL_BUDGET` 可调)→ 归零条件是"跑出全绿";
189
- > ⑤ **L1** `--filter` 覆盖 >50% 用例 = 变相全量,按全量纳管;**L2** 连续两轮失败签名一致且源码未变 → 拒绝重跑(先 probe 定案)。
190
- > 免检:`--retry-failed` 因源码变更**自动升级**的全量(非显式,不占预算);`--force-all` 为显式逃生门。
191
- > droid2hmos CLI 的 `test harmony --runner python --all` 同步受 R1 防护(`--force-all` 放行)。
192
- > 实证(Grit):修复期 15 次全量 212min,其中 **`--acceptance` 被当日常全量跑 13 次 157min(占测试时间 65%)**;
193
- > `--filter TC0` 当"小全量"跑 7 次;同族失败连卡 5 轮。v3 的 R1 覆盖 / R3 计次 / L1 / R2b / L2 即由此固化。
194
- > 合法全量收敛为:首轮(无历史)+ 终局验收(全绿后一次)+ 逃生门。
195
- ```bash
196
- # 首轮全量(唯一常规使用 --all 的场景)
197
- python3 $F/run_direct.py --all --dir testcases --device <sn>
198
-
199
- # 修复轮阶梯(Grit 实证:15 次全量 212min,其中 5 轮卡同一 bug 空转 71min)
200
- python3 $F/run_direct.py --case <失败用例> --dir testcases --device <sn> # ① 修测试脚本后单条验证(1-4min)
201
- python3 $F/run_direct.py --retry-failed --dir testcases --device <sn> # ② 修产品代码后增量重跑(srcHash 门禁自动升级全量)
202
- python3 $F/run_direct.py --filter TC3 --dir testcases --device <sn> # ③ 同族用例子集回归(如 TC30/31/35)
203
-
204
- python3 $F/analyze_results.py --results results.json --runtime-pages runtime_pages.json
205
- python3 $F/forge_report.py # 产出 test-report.html(Allure 风格:概况/用例卡片/关键截图内嵌)
206
- ```
207
- 铁律:修 1 个用例 ≠ 重跑 24 个。run_direct 汇总后 FAIL>0 会直接打印下一步命令——照抄执行。
208
-
209
- **范围回归(touched,产品代码变更后的默认门禁策略)**:
210
- `--retry-failed` 检测到产品代码变更时不再一律全量——按 `results.meta.json` 的**逐文件指纹**
211
- diff 出改动集,若全部是 ids.json 可定位的 UI 文件,则只重跑 `FAIL/SKIP + 引用这些文件的用例`
212
- (用例源码中的 id 字面量 → ids.json 文件映射)。改动含共享逻辑(viewmodel/repository)或
213
- 范围超 60% → 自动退化全量。禁手动 `--all` 替代;`--regression-scope full` 仅用于里程碑。
214
-
215
- **热启动(组内软复用)**:组内非首条用例 `FORGE_WARM_LAUNCH=1`,`launch()` 探测 HOME_ANCHOR
216
- 已在位即跳过 stop/start/bootstrap(省 8-12s/例);组首条与 first_launch 组保持冷启;
217
- `--case` 单条验证默认热启动(`--cold` 强制冷启)。断言冷启行为的用例写 `launch(d, force=True)`。
218
- 单用例挂死由看门狗截断(`--case-timeout 240` 默认,超时记 FAIL 继续下一条)。
219
- 瞬态失败(watchdog/连接类)轮末自动复测一次(`--flake-retry 1` 默认,断言类失败不
220
- 自动复测防掩盖真缺陷)。**零进展检测**:失败签名集与上轮完全一致时 runner 直接亮牌
221
- 警告——停止重跑转 probe 定案(Grit 实证:同族失败连卡 5 轮全量空转 71min)。
222
- 调试迭代期可 `FORGE_WAIT_SCALE=0.5` 把断言超时减半加速失败复现;正式回归保持 1。
223
-
224
- **最终质量门(不可跳过)**:所有优化(范围回归/热启动/flake 复测)只加速迭代期;
225
- **交付/收尾前必须执行一次验收全量**:`run_direct.py --acceptance`(强制 --all + 全程截图 +
226
- `.acceptance` 标记永久保护,最近 2 次留存),FAIL=0 或全部 FAIL 已登记缺陷并经 probe 定案。
227
- 范围回归的 PASS 保留记录不作为最终结论——它只是迭代加速器,最终覆盖度以这次验收全量为准。
228
-
229
- **截图与 reports/ 留存(防体积失控——Grit 实证 5266 张/666MB)**:
230
- - 迭代期 `--shots fail`(默认):PASS 用例只留 `<case>_end.jpeg`,过程帧/分步图仅 FAIL 用例保留;
231
- 连续相同帧自动去重(静止页 8 帧→1 帧)。
232
- - **中间运行目录即用即弃**(`--keep-reports` 默认 0):每轮结束只保留本轮供当轮取证,
233
- 下一轮自动回收上一轮中间目录;需要跨轮对照时 `--keep-reports 3`。
234
- - **验收全程留存**:`--acceptance`(最终质量门专用)强制全量+全程截图,目录写 `.acceptance`
235
- 标记,prune 独立保护**最近 2 次**验收目录,永久作为交付证据。
236
- **取证一条命令(probe.py,禁手写 UiDriver 内联脚本——Grit 实证 69 次手写探针多花一半时间在代码生成上)**:
237
- ```bash
238
- python3 $F/probe.py texts|ids|find <id>|tree --grep x # 单次 dumpLayout 本地解析取证
239
- python3 $F/probe.py click <id|text> # uinput 坐标点击(Toggle 等 touch 失效场景可靠)
240
- python3 $F/probe.py shot / hilog JSAPP 400 / db-pull <pkg> / db-sql <db> '<sql>'
241
- ```
242
-
243
- 失败四簇:DEVICE(环境,重跑)/ DESIGN-ERROR(脚本定位/步骤错误,修脚本 ≤2 轮)/
244
- NAV-CONTEXT-LOSS(页面上下文丢失,hilog 定案)/ ASSERT-FAIL(**优先怀疑产品缺陷,保留 FAIL**)。
245
-
246
- **自愈边界(允许 vs 禁止,修≠换绿灯)**:
247
- - 允许:修正错误控件 ID;补缺失等待条件;修正滚动/输入/弹窗关闭方式;调整用例内前置步骤。
248
- - 禁止:强断言→弱断言;`equals`→`contains` 规避差异;固定等待无限加长;模糊 text 匹配
249
- 绕过定位失败;把产品缺陷标 SKIP/PASS;**删除失败断言而不是修复原因**。
250
- - **FAIL 现场证据自动附言**:run_direct 失败时 msg 自动附带当前页面 Text 摘要
251
- (`| 现场Text: …`)——下轮分诊直读证据,禁重复现场取证。
252
- - **原始失败记录保留**:修复重跑前 run_direct 自动把 results.json 滚动备份为
253
- results.prev.json——修复轨迹可审计(改了什么、为什么改),防"驯化测试"。
254
- - **分诊三律(TD-018,防定案实验失控)**:①**同消息聚类**——多例同错误消息(如
255
- text not found: X×N)视为同族,一次分析一次定案,禁逐条重复实验;②**同族定案实验
256
- ≤2 次**(清数据/冷启/对照类),超限即按现有证据登记缺陷转人工——保留 FAIL 是合规
257
- 产出,不是未完成;③**成功模式对照先行**——定案前先查同场景已 PASS 用例的断言
258
- 范式(如冷启持久化断言),判据升级优先于现场重取证。
259
- - **修复轮增量重试**:`run_direct.py --retry-failed` 只重跑 FAIL/SKIP 条(PASS 保留
260
- 原结果合并写出)——禁无谓全量重跑浪费设备时间(首轮 11 条全量 286s,增量仅重跑 2 条
261
- ~52s,省 80%)。**编写期自检批量做**:全部脚本写完统一 lint+语法批检,禁逐条单跑
262
- 调试(单条 LLM 往返间隔是隐形大头)。
263
- - **增量重试回归门禁(TD-019,防旧 PASS 掩盖回归)**:`run_direct.py` 自动对
264
- `entry/src/main/ets` 计算内容指纹(srcHash,存 `results.meta.json` 侧车)。增量重试前
265
- 重算比对:产品代码有变更 / 无上轮指纹 / 定位不到源码目录 → **自动升级全量重跑**
266
- (实证:Profiles TC09/TC14 PASS→FAIL 靠全量重跑才抓到,走增量通道会被旧 PASS 掩盖);
267
- 确认仅测试脚本变更可 `--force-incremental` 强制增量。顺带修复:--retry-failed 现写入
268
- results.json(旧版误写 results-single.json)。
269
-
270
- ## 度量与沉淀
271
-
272
- - 记录:用例数、覆盖度、耗时、首轮通过率、调试轮次、强断言占比。
273
- - 失败模式沉淀 testability-requirements.md(可测试性需求)。
274
-
275
- ## References(按需加载)
276
-
277
- | 文件 | 内容 | 何时读 |
278
- |---|---|---|
279
- | `references/official-api.md` | Hypium 官方 API 速查(BY 定位/模糊匹配/触摸/滑动/输入/断言 Checker/截图) | 编写脚本前查阅定位与断言 API 用法 |
280
- | `references/pitfalls.md` | 7 项目实证踩坑清单(swipe 百分比/BY.text 全匹配/press_back 语义/系统子窗口不可见/视口外滚动/DatePicker) | 遇到定位/滑动/输入/Toast 问题时查阅 |
281
- | `references/functional-spec-template.md` | 功能规格文档模板(9 段结构 + test_intent 选型表 + 边界异常) | 阶段一宿主 agent 产出规格时 |
282
- | `references/testability-template.md` | 可测试性需求模板 | 沉淀失败模式时 |
283
-
284
- ## 断言能力速查
285
-
286
- | 层 | 能力 |
287
- |---|---|
288
- | 存在/消失 | BY.key/id/text/hint/type + absent |
289
- | 属性状态 | BY.checked/selected/enabled/focused/clickable/scrollable |
290
- | 值断言 | Checker:equal/not_equal/contains/match_regexp/greater/less |
291
- | 几何关系 | getBounds()/中心点/相对定位 |
292
- | Toast/系统级 | hilog 轮询 / hdc pasteboard / 外部跳转 absent 级 |
293
- | 结构断言 | find_all_components 计数 |
294
- | 边界 | 颜色/像素 diff/性能时序(勿硬造) |
295
-
296
- ## 定位原语速查
297
-
298
- | 场景 | 原语 |
299
- |---|---|
300
- | 已注入 ID | `BY.key(tid)` |
301
- | 输入框 | `BY.hint('占位符')`(BY.text 命中 hint 节点静默失败) |
302
- | 底部 Tab | find_all(BY.text CONTAINS) 取最靠下 bounds 中心 |
303
- | 视口外 | `touch(BY.text("xx"), scroll_target=BY.type("Scroll"))` 原生滚动查找 |
304
- | mock 数据 | `hdc rport tcp:8787 tcp:8787` |
305
-
306
- ## 相关 skill
307
-
308
- - `hypium-id-test`:无业务语义 ID 冒烟(转化后即跑)
309
- - `hypium-test-gen`:白盒逐条精写(7 项目踩坑清单)
1
+ ---
2
+ name: d2h-hypium-forge
3
+ description: 鸿蒙应用/元服务测试用例设计与 hypium 执行(两阶段流水线第二段,部署于 .opencode 迁移 agent)。宿主 agent 代码理解产出功能场景库(功能/说明/是否已转化)→ 本 skill 按场景测试目的选断言范式、LLM 白盒精写 Python 脚本 → 裸跑执行与失败四簇分析。适用于任何 ArkTS 工程的功能测试自动化。触发词:hypium-forge、功能用例设计、测试用例生成执行、覆盖度评估、鸿蒙 UI 测试全流程。
4
+ ---
5
+
6
+ # d2h-hypium-forge — LLM 白盒精写 → 裸跑执行
7
+
8
+ ## 体系边界(先分清)
9
+
10
+ 本 skill **只服务 Python 版 Hypium**(devicetest 框架,`testcases/*.py`,UiDriver 经 uitest RPC 驱动设备)。
11
+ **ArkTS @ohos/hypium 是另一条链路,不在本 skill 范围**——模拟器上应用内 uitest napi 是 stub
12
+ (`Driver.create()` 恒 null,ENV 实证),ArkTS 用例仅适用于真机且需独立工程配置;文档中两套
13
+ API 同名不同构(BY 定位/断言语义有差异),混用必然执行失败。涉及 ArkTS 用例需求请走
14
+ 单独链路(ohosTest 模板),禁止在本 skill 产物目录混建。
15
+
16
+ ## 三条铁律
17
+
18
+ 1. **必须先预置控件 ID(写脚本之前)**:`preinject_ids.py` 对全部交互点机械注入,
19
+ 脚本只认 ids.json/既有 ID(BY.key)。文本定位(BY.text)慢 10~100 倍且有歧义。
20
+ 2. **Level 0 前置自适应(冷启三件套,launch 原语内置,用例不得自带)**:
21
+ 首启弹窗 dismiss(多文案轮询幂等)+ 首页 tab 归位(HOME_TAB 应用级配置,
22
+ 冷启默认 tab 常与目标 tab 不一致)+ 语义锚等待(禁依赖注入 id 等 Loading 层控件)。
23
+ 隐私弹窗常为内存态(冷启必弹)——每条冷启都必须 dismiss(TD-007)。
24
+ 3. **权限和账号前置完成**:precondition.py 预授可授权限/登录策略/首启弹窗 dismiss,
25
+ 绝不留给用例执行时撞。**登录态跨用例保持**:软隔离(stop_app/start_app)不清应用数据;
26
+ `reset_data=True`(bm clean)只用于首启态用例且**须排尾/独立分组执行**——与登录用例
27
+ 交错会反复清登录态导致重复登录注册(CloudReader TD-004 实证:交错执行重复注册 30 次)。
28
+
29
+ ## 五步流水线
30
+
31
+ 设 `$F = 本skill/scripts`,工程 `$P`,产物目录 `$P/.hypium_forge/`。
32
+
33
+ ### 1. 功能场景库(测试设计的基础输入)
34
+
35
+ 本 skill 是两阶段流水线的第二段:代码理解由宿主 agent(.opencode 迁移 agent)完成,
36
+ 产出功能场景清单后调用本 skill 生成并执行测试。**场景库是测试设计的唯一基础**——
37
+ 它的完备性与准确度决定下游全部产出的质量,宁可在此多花时间。
38
+
39
+ **阶段一(宿主 agent)**——按 `references/functional-spec-template.md` 产出功能规格
40
+ `functional_spec.md`,再翻译为 `scenarios.json`:
41
+ ```json
42
+ {"id":"WF-01","name":"搜索选中城市并收藏","domain":"位置管理",
43
+ "desc":"用户在搜索页输入关键字,点选结果后返回管理页,列表新增该城市并提示收藏成功",
44
+ "converted":true, "source_ref":"SearchActivity.java:181-195",
45
+ "test_intent":"功能交互类"}
46
+ ```
47
+ 生成规则:
48
+ - 场景单位 = **用户动宾行为**(禁 Activity/Fragment/surface 类名当场景);
49
+ - `desc` 写清「输入→动作→**可观察业务效果**」,效果描述到可翻译成断言的程度;
50
+ - 行为预期从源码推导(默认值/联动/守卫),不从直觉——直觉必错;
51
+ - `converted` 与迁移报告对齐,未转化标 false 并跳过。
52
+
53
+ **阶段二(本 skill)**——先过**场景库质量门禁**再消费:
54
+ ```bash
55
+ python3 $F/validate_scenarios.py scenarios.json # 不通过项入 scenarios_blocker.md
56
+ ```
57
+ 校验:①必备字段(id/name/desc/converted/test_intent)②desc 含可观察业务效果关键词
58
+ (新增/删除/修改/保存/收藏/取消/成功/失败/提示/计数/状态/切换——纯"进入XX页"类导航词
59
+ 无效果=拦截)③入口可定位(深层链路 desc 须写前置路径)④行为预期有源码/PRD 依据标注
60
+ ⑤converted=false 必须带 reason(未迁移/能力缺失/暂不测试)。**blocker 场景不进用例生成**,
61
+ 退回宿主 agent 修复。通过后:converted=true 逐场景设计用例
62
+ (按 test_intent 选断言范式)→ 白盒精写 → 执行 → 分诊 → defects/报告。
63
+ 用例与场景一一对应,无场景不写用例。
64
+
65
+ ### 1.5 机械扫描(覆盖性评估的补充)
66
+
67
+ ```bash
68
+ python3 $F/scan_features.py $P --out $P/.hypium_forge/features.json
69
+ ```
70
+ 产出:页面(路由+@Entry)× 交互点 × 状态矩阵 + 权限/登录检测。有安卓迁移现场时
71
+ `merge_appgraph.py --features ... --facts <android>/.migration/spec/facts` 注入权威行为语义
72
+ (handler 链揭示联动场景)。
73
+
74
+ **定位是补充而非基础**:features 条目是定位事实不是业务功能,机械扫描可靠性不足,
75
+ 不得直接作为测试设计输入。两个用途:
76
+ 1. **覆盖校验**:对照 scenarios.json,找出未被任何场景覆盖的页面/交互点 →
77
+ 反馈宿主 agent 裁决「补场景」或「登记不测并说明理由」——遗漏即测试盲区;
78
+ 2. **ID 预置清单**:交互点列表供步骤 3 preinject_ids.py 机械注入。
79
+
80
+ 覆盖结论写入 `coverage.md`,**双口径分列(防机械冒充功能覆盖)**:
81
+ - **场景覆盖度(主口径)** = 被采纳用例触达的场景数 / scenarios.json 中 converted=true 场景数;
82
+ - **控件触达率(辅助口径)** = 已触达交互点 / 全部交互点——仅暴露机械盲区,**不得单独
83
+ 作为功能覆盖率汇报**(把扫到的控件数当功能覆盖是自欺);
84
+ coverage.md 逐条列出并按规则处置:
85
+ - 未覆盖场景(如 WF-12, WF-19)→ 必须反馈宿主 agent 补场景或登记不测;
86
+ - 未触达交互点(如 HomePage.ad_banner)→ 不一定补用例,人工裁决是否为
87
+ 广告位/运营位/未迁移能力(判定理由随条目记录)。
88
+ 未覆盖交互点及其裁决结果。
89
+
90
+ ### 2. 运行时文本采集
91
+ ```bash
92
+ python3 $F/collect_runtime_pages.py --device <sn> --bundle <b> --out runtime_pages.json
93
+ ```
94
+ 脚本引用的控件文本**只允许**来自采集结果;断言锚定业务数据,禁止导航回声断言。
95
+ **并行提速**:首页文本采集完成后即可开始写该页用例(其余页面采集继续)——采集与编写
96
+ 流水线并行,禁串行等全量采集完再动笔。
97
+ 启动参数门控应用:先 `aa start --ps k v` 预热,singleton 的 onNewWant 不重置状态可继续采集。
98
+
99
+ ### 3. ID 预置(铁律①,先于脚本编写)——机械注入的边界与补强
100
+ ```bash
101
+ python3 $F/preinject_ids.py --features features.json --out ids.json # 交互点清单来自 §1.5
102
+ # 之后 rebuild + uninstall&&install
103
+ ```
104
+ 批量注入 `tc_<page>_<n>`,产出 ids.json 映射表。脚本定位只认 ids.json/既有 ID(BY.key);
105
+ 仅输入框用 BY.hint。
106
+
107
+ **静态注入三盲区**(preinject 只扫静态代码,以下场景必然漏注):
108
+ 1. 动态列表控件(ForEach 运行时生成,静态无挂载点)→ 用 `collect_runtime_pages.py`
109
+ 的 dumpLayout 控件树交叉核对:dump 中 key 为空但语义可定位的条目,回填至
110
+ runtime_pages.json 供 BY.text 兜底并登记例外;
111
+ 2. 条件渲染分支(if/else 二选一支路)→ 场景变体执行前先 dump 该分支实况;
112
+ 3. 异步加载页(网络数据到位才渲染)→ 依赖步骤 2 采集的等待锚,禁用盲等。
113
+
114
+ **无 ID 例外审批制**:例外必须三件套——docstring 理由 + runtime_pages.json 中的实测
115
+ 证据(该控件确实无 id 且文案唯一)+ lint_locate 通过;例外清单汇总进 coverage.md
116
+ 「无 ID 例外」章节备查,审计时逐条复核(防例外变成 BY.text 后门)。
117
+
118
+ ### 3.5 输入契约与空库守卫(宿主 agent ↔ 本 skill 的稳定边界)
119
+
120
+ **只接受以下输入**:scenarios.json / features.json / ids.json / runtime_pages.json /
121
+ _common_template.py / ArkTS 源码路径。**场景库为空或全部 converted=false 时直接报错
122
+ 退出,不生成空用例集**(空跑=伪覆盖)。生成规则见步骤 4;执行后必须产出:
123
+ testcases/*.py、results.json、coverage.md、summary.json、test-report.html、失败截图与 hilog 片段。
124
+
125
+ ### 4. LLM 白盒精写 testcases/*.py(核心)
126
+
127
+ 从 `$F/_common_template.py` 复制为 `testcases/_common.py`(launch 带 `--ps k v` 空格分隔、
128
+ 3 轮重试探活、首启弹窗 dismiss、强断言原语全套——**写前必读**),逐条编写 `TCxx_<slug>.py`。
129
+
130
+ **编写规则:**
131
+
132
+ - **先读源码**:默认值/联动规则/时序以 ArkTS 源码为准——臆断默认值必返工;
133
+ - **按场景测试目的选断言范式**(问"本场景要证明什么"再选型):
134
+
135
+ | 场景类型 | 测试目的 | 正确断言范式 |
136
+ |---|---|---|
137
+ | 资源渲染类 | 图标/文案加载成功 | exist/equals 即目的本身 |
138
+ | 功能交互类 | 操作产生业务效果 | 效果断言(值变化/计数增减/checked 翻转/absent) |
139
+ | 配置生效类 | 设置改变全局行为 | 生效链断言(改设置→回业务页→断言输出变化) |
140
+ | 导航流转类 | 状态迁移到目的地 | 终点效果(城市选中→顶栏变新城市名) |
141
+ | 状态机类 | 状态转移规律 | 前置态断言→触发→后置态断言(成对) |
142
+ | 数据输入类 | 输入被正确处理 | 输入→输出值断言(搜索词→结果计数) |
143
+
144
+ - **强断言比例按 test_intent 分档**(全局硬门禁 ≥30% 整体维持 40~70%;比例是目的的
145
+ 结果不是目标——**禁止加"页面存在/按钮可见"类弱断言凑比例**,单条低于档位下限报
146
+ "弱断言风险"提示、超上限检查是否断言了实现细节):
147
+
148
+ | test_intent | 强断言区间 | 说明 |
149
+ |---|---|---|
150
+ | 资源渲染 | 20~40% | 证明图标/文案/占位正确,exist/equals 即目的 |
151
+ | 功能交互 | 50~70% | 必须断言业务效果,不能只断言按钮存在 |
152
+ | 配置生效 | 60~80% | 改设置→回业务页→验证输出变化的完整生效链 |
153
+ | 导航流转 | 40~60% | 终点页面 + 关键状态都要验证 |
154
+ | 状态机 | 60~80% | 前置态、触发、后置态成对断言 |
155
+ | 数据输入 | 50~70% | 输入→结果列表/计数/排序必须验证 |
156
+
157
+ 核心判据:**断言必须证明场景声明的目的**——目的是"功能发生"时仅判存在 = 无效用例;
158
+ - **Toast** 用 hilog 关键字轮询或 toast_text_seen(dump 轮询)、**剪贴板**用 hdc pasteboard、
159
+ 对话框流转逐步断言;
160
+ - **视口外控件**必须用 `touch(BY.text("xx"), scroll_target=BY.type("Scroll"))` 原生滚动查找,
161
+ 禁止手工 swipe + find 循环(时序敏感且不稳定);
162
+ - **深链路用例内显式 setup**:上游状态准备写成用例内步骤,禁止假设页面已就位;
163
+ - **失败 = 信息**:产品缺陷保持 FAIL 暴露,绝不弱化断言换绿灯;用例自身错误可修(≤2 轮);
164
+ - **用例数量由功能场景覆盖决定**,禁止固定条数或上限——每个 converted=true 场景至少
165
+ 1 条用例真实触达;**不允许脱离场景新增用例、不允许把页面跳转当最终业务效果**;
166
+ 文本断言只能来自 runtime_pages.json 实采,不能臆造文案;
167
+ - **定位门禁**:`lint_locate.py --dir testcases --ids ids.json`,text 定位 >20% FAIL——
168
+ LLM 常绕过预置 ID 用 text_click,唯机械门禁有效;
169
+ - 每脚本 docstring 写清行为链与覆盖的功能场景 id;
170
+ - 执行后产出 `coverage.md`(含「总覆盖度: N%」行)与 `summary.json`。
171
+
172
+ ### 4.5 前置条件分组编排(共用前置,提升执行效率)
173
+
174
+ 用例文件声明 `PRECOND`(缺省 anonymous),run_direct 按 **anonymous → logged_in →
175
+ first_launch** 分组排序执行(破坏性递增,bm clean 类强制排尾不再回头破坏登录态)。
176
+ 组内软隔离共享前置:`launch()` 默认 `reset_data=False`,preferences 保留 → 组首一次
177
+ 完整 `ensure_login`,组内后续幂等复用。组首执行时 `FORGE_GROUP_FIRST=1`。
178
+ results.json 每条附 `group` 字段可分组统计。**禁把 reset_data=True 用在 logged_in 组**。
179
+
180
+ ### 5. 裸跑执行 + 四簇分析 + HTML 报告(重跑阶梯 = 硬规范)
181
+
182
+ **执行命令阶梯(--all 仅限首轮与门禁升级,修复轮禁无脑全量;违反会被 runner 硬阻断)**:
183
+ > TD-LADDER v3 防护(已内置于 run_direct.py):**所有"显式全量"(`--all` / `--acceptance` / 变相全量的 `--filter`)统一受管**,
184
+ > 不再给 `--acceptance` 开后门。五条规则(任一命中即 exit 2 阻断并输出指引):
185
+ > ① **R1** 上轮 results.json 存在 FAIL/SKIP → 必须先修(`--case`/`--retry-failed`),禁止全量;
186
+ > ② **R2** 源码 `srcHash` 与上次全量相同 → 重复全量无新结论(仅拦 `--all`);
187
+ > ③ **R2b** 上次已是**同源验收**(`lastMode=acceptance` 且 srcHash 未变)→ 拒绝重复验收;
188
+ > ④ **R3** 本修复周期显式全量已达上限(默认 2,`FORGE_FULL_BUDGET` 可调)→ 归零条件是"跑出全绿";
189
+ > ⑤ **L1** `--filter` 覆盖 >50% 用例 = 变相全量,按全量纳管;**L2** 连续两轮失败签名一致且源码未变 → 拒绝重跑(先 probe 定案)。
190
+ > 免检:`--retry-failed` 因源码变更**自动升级**的全量(非显式,不占预算);`--force-all` 为显式逃生门。
191
+ > droid2hmos CLI 的 `test harmony --runner python --all` 同步受 R1 防护(`--force-all` 放行)。
192
+ > 实证(Grit):修复期 15 次全量 212min,其中 **`--acceptance` 被当日常全量跑 13 次 157min(占测试时间 65%)**;
193
+ > `--filter TC0` 当"小全量"跑 7 次;同族失败连卡 5 轮。v3 的 R1 覆盖 / R3 计次 / L1 / R2b / L2 即由此固化。
194
+ > 合法全量收敛为:首轮(无历史)+ 终局验收(全绿后一次)+ 逃生门。
195
+ ```bash
196
+ # 首轮全量(唯一常规使用 --all 的场景)
197
+ python3 $F/run_direct.py --all --dir testcases --device <sn>
198
+
199
+ # 修复轮阶梯(Grit 实证:15 次全量 212min,其中 5 轮卡同一 bug 空转 71min)
200
+ python3 $F/run_direct.py --case <失败用例> --dir testcases --device <sn> # ① 修测试脚本后单条验证(1-4min)
201
+ python3 $F/run_direct.py --retry-failed --dir testcases --device <sn> # ② 修产品代码后增量重跑(srcHash 门禁自动升级全量)
202
+ python3 $F/run_direct.py --filter TC3 --dir testcases --device <sn> # ③ 同族用例子集回归(如 TC30/31/35)
203
+
204
+ python3 $F/analyze_results.py --results results.json --runtime-pages runtime_pages.json
205
+ python3 $F/forge_report.py # 产出 test-report.html(Allure 风格:概况/用例卡片/关键截图内嵌)
206
+ ```
207
+ 铁律:修 1 个用例 ≠ 重跑 24 个。run_direct 汇总后 FAIL>0 会直接打印下一步命令——照抄执行。
208
+
209
+ **范围回归(touched,产品代码变更后的默认门禁策略)**:
210
+ `--retry-failed` 检测到产品代码变更时不再一律全量——按 `results.meta.json` 的**逐文件指纹**
211
+ diff 出改动集,若全部是 ids.json 可定位的 UI 文件,则只重跑 `FAIL/SKIP + 引用这些文件的用例`
212
+ (用例源码中的 id 字面量 → ids.json 文件映射)。改动含共享逻辑(viewmodel/repository)或
213
+ 范围超 60% → 自动退化全量。禁手动 `--all` 替代;`--regression-scope full` 仅用于里程碑。
214
+
215
+ **热启动(组内软复用)**:组内非首条用例 `FORGE_WARM_LAUNCH=1`,`launch()` 探测 HOME_ANCHOR
216
+ 已在位即跳过 stop/start/bootstrap(省 8-12s/例);组首条与 first_launch 组保持冷启;
217
+ `--case` 单条验证默认热启动(`--cold` 强制冷启)。断言冷启行为的用例写 `launch(d, force=True)`。
218
+ 单用例挂死由看门狗截断(`--case-timeout 240` 默认,超时记 FAIL 继续下一条)。
219
+ 瞬态失败(watchdog/连接类)轮末自动复测一次(`--flake-retry 1` 默认,断言类失败不
220
+ 自动复测防掩盖真缺陷)。**零进展检测**:失败签名集与上轮完全一致时 runner 直接亮牌
221
+ 警告——停止重跑转 probe 定案(Grit 实证:同族失败连卡 5 轮全量空转 71min)。
222
+ 调试迭代期可 `FORGE_WAIT_SCALE=0.5` 把断言超时减半加速失败复现;正式回归保持 1。
223
+
224
+ **最终质量门(不可跳过)**:所有优化(范围回归/热启动/flake 复测)只加速迭代期;
225
+ **交付/收尾前必须执行一次验收全量**:`run_direct.py --acceptance`(强制 --all + 全程截图 +
226
+ `.acceptance` 标记永久保护,最近 2 次留存),FAIL=0 或全部 FAIL 已登记缺陷并经 probe 定案。
227
+ 范围回归的 PASS 保留记录不作为最终结论——它只是迭代加速器,最终覆盖度以这次验收全量为准。
228
+
229
+ **截图与 reports/ 留存(防体积失控——Grit 实证 5266 张/666MB)**:
230
+ - 迭代期 `--shots fail`(默认):PASS 用例只留 `<case>_end.jpeg`,过程帧/分步图仅 FAIL 用例保留;
231
+ 连续相同帧自动去重(静止页 8 帧→1 帧)。
232
+ - **中间运行目录即用即弃**(`--keep-reports` 默认 0):每轮结束只保留本轮供当轮取证,
233
+ 下一轮自动回收上一轮中间目录;需要跨轮对照时 `--keep-reports 3`。
234
+ - **验收全程留存**:`--acceptance`(最终质量门专用)强制全量+全程截图,目录写 `.acceptance`
235
+ 标记,prune 独立保护**最近 2 次**验收目录,永久作为交付证据。
236
+ **取证一条命令(probe.py,禁手写 UiDriver 内联脚本——Grit 实证 69 次手写探针多花一半时间在代码生成上)**:
237
+ ```bash
238
+ python3 $F/probe.py texts|ids|find <id>|tree --grep x # 单次 dumpLayout 本地解析取证
239
+ python3 $F/probe.py click <id|text> # uinput 坐标点击(Toggle 等 touch 失效场景可靠)
240
+ python3 $F/probe.py shot / hilog JSAPP 400 / db-pull <pkg> / db-sql <db> '<sql>'
241
+ ```
242
+
243
+ 失败四簇:DEVICE(环境,重跑)/ DESIGN-ERROR(脚本定位/步骤错误,修脚本 ≤2 轮)/
244
+ NAV-CONTEXT-LOSS(页面上下文丢失,hilog 定案)/ ASSERT-FAIL(**优先怀疑产品缺陷,保留 FAIL**)。
245
+
246
+ **自愈边界(允许 vs 禁止,修≠换绿灯)**:
247
+ - 允许:修正错误控件 ID;补缺失等待条件;修正滚动/输入/弹窗关闭方式;调整用例内前置步骤。
248
+ - 禁止:强断言→弱断言;`equals`→`contains` 规避差异;固定等待无限加长;模糊 text 匹配
249
+ 绕过定位失败;把产品缺陷标 SKIP/PASS;**删除失败断言而不是修复原因**。
250
+ - **FAIL 现场证据自动附言**:run_direct 失败时 msg 自动附带当前页面 Text 摘要
251
+ (`| 现场Text: …`)——下轮分诊直读证据,禁重复现场取证。
252
+ - **原始失败记录保留**:修复重跑前 run_direct 自动把 results.json 滚动备份为
253
+ results.prev.json——修复轨迹可审计(改了什么、为什么改),防"驯化测试"。
254
+ - **分诊三律(TD-018,防定案实验失控)**:①**同消息聚类**——多例同错误消息(如
255
+ text not found: X×N)视为同族,一次分析一次定案,禁逐条重复实验;②**同族定案实验
256
+ ≤2 次**(清数据/冷启/对照类),超限即按现有证据登记缺陷转人工——保留 FAIL 是合规
257
+ 产出,不是未完成;③**成功模式对照先行**——定案前先查同场景已 PASS 用例的断言
258
+ 范式(如冷启持久化断言),判据升级优先于现场重取证。
259
+ - **修复轮增量重试**:`run_direct.py --retry-failed` 只重跑 FAIL/SKIP 条(PASS 保留
260
+ 原结果合并写出)——禁无谓全量重跑浪费设备时间(首轮 11 条全量 286s,增量仅重跑 2 条
261
+ ~52s,省 80%)。**编写期自检批量做**:全部脚本写完统一 lint+语法批检,禁逐条单跑
262
+ 调试(单条 LLM 往返间隔是隐形大头)。
263
+ - **增量重试回归门禁(TD-019,防旧 PASS 掩盖回归)**:`run_direct.py` 自动对
264
+ `entry/src/main/ets` 计算内容指纹(srcHash,存 `results.meta.json` 侧车)。增量重试前
265
+ 重算比对:产品代码有变更 / 无上轮指纹 / 定位不到源码目录 → **自动升级全量重跑**
266
+ (实证:Profiles TC09/TC14 PASS→FAIL 靠全量重跑才抓到,走增量通道会被旧 PASS 掩盖);
267
+ 确认仅测试脚本变更可 `--force-incremental` 强制增量。顺带修复:--retry-failed 现写入
268
+ results.json(旧版误写 results-single.json)。
269
+
270
+ ## 度量与沉淀
271
+
272
+ - 记录:用例数、覆盖度、耗时、首轮通过率、调试轮次、强断言占比。
273
+ - 失败模式沉淀 testability-requirements.md(可测试性需求)。
274
+
275
+ ## References(按需加载)
276
+
277
+ | 文件 | 内容 | 何时读 |
278
+ |---|---|---|
279
+ | `references/official-api.md` | Hypium 官方 API 速查(BY 定位/模糊匹配/触摸/滑动/输入/断言 Checker/截图) | 编写脚本前查阅定位与断言 API 用法 |
280
+ | `references/pitfalls.md` | 7 项目实证踩坑清单(swipe 百分比/BY.text 全匹配/press_back 语义/系统子窗口不可见/视口外滚动/DatePicker) | 遇到定位/滑动/输入/Toast 问题时查阅 |
281
+ | `references/functional-spec-template.md` | 功能规格文档模板(9 段结构 + test_intent 选型表 + 边界异常) | 阶段一宿主 agent 产出规格时 |
282
+ | `references/testability-template.md` | 可测试性需求模板 | 沉淀失败模式时 |
283
+
284
+ ## 断言能力速查
285
+
286
+ | 层 | 能力 |
287
+ |---|---|
288
+ | 存在/消失 | BY.key/id/text/hint/type + absent |
289
+ | 属性状态 | BY.checked/selected/enabled/focused/clickable/scrollable |
290
+ | 值断言 | Checker:equal/not_equal/contains/match_regexp/greater/less |
291
+ | 几何关系 | getBounds()/中心点/相对定位 |
292
+ | Toast/系统级 | hilog 轮询 / hdc pasteboard / 外部跳转 absent 级 |
293
+ | 结构断言 | find_all_components 计数 |
294
+ | 边界 | 颜色/像素 diff/性能时序(勿硬造) |
295
+
296
+ ## 定位原语速查
297
+
298
+ | 场景 | 原语 |
299
+ |---|---|
300
+ | 已注入 ID | `BY.key(tid)` |
301
+ | 输入框 | `BY.hint('占位符')`(BY.text 命中 hint 节点静默失败) |
302
+ | 底部 Tab | find_all(BY.text CONTAINS) 取最靠下 bounds 中心 |
303
+ | 视口外 | `touch(BY.text("xx"), scroll_target=BY.type("Scroll"))` 原生滚动查找 |
304
+ | mock 数据 | `hdc rport tcp:8787 tcp:8787` |
305
+
306
+ ## 相关 skill
307
+
308
+ - `hypium-id-test`:无业务语义 ID 冒烟(转化后即跑)
309
+ - `hypium-test-gen`:白盒逐条精写(7 项目踩坑清单)