@harmonyos-arkts/d2h 0.0.0-stage → 0.1.1

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 (74) hide show
  1. package/.claude-plugin/marketplace.json +13 -0
  2. package/.claude-plugin/plugin.json +7 -0
  3. package/README.md +177 -2
  4. package/agents/android-to-hmos-00-orchestrator.md +312 -0
  5. package/agents/d2h.md +168 -0
  6. package/bin/install-opencode.mjs +57 -0
  7. package/install-opencode.sh +160 -0
  8. package/opencode/agents.json +16 -0
  9. package/package.json +43 -4
  10. package/schemas/android-source-manifest.schema.json +25 -0
  11. package/schemas/checkpoint-provenance.schema.json +67 -0
  12. package/schemas/final-acceptance.schema.json +29 -0
  13. package/schemas/managed-evidence-index.schema.json +15 -0
  14. package/schemas/managed-evidence.schema.json +114 -0
  15. package/schemas/migration-config.schema.json +51 -0
  16. package/schemas/migration-report-index.schema.json +39 -0
  17. package/schemas/migration-report-item.schema.json +51 -0
  18. package/schemas/migration-report-summary.schema.json +25 -0
  19. package/schemas/migration-status.schema.json +202 -0
  20. package/schemas/preflight.schema.json +60 -0
  21. package/schemas/source-order-audit.schema.json +16 -0
  22. package/schemas/source-provenance-event.schema.json +19 -0
  23. package/schemas/spec-app-shard.schema.json +45 -0
  24. package/schemas/spec-fact-corrections-shard.schema.json +13 -0
  25. package/schemas/spec-features-shard.schema.json +43 -0
  26. package/schemas/spec-index.schema.json +26 -0
  27. package/schemas/spec-interactions-shard.schema.json +40 -0
  28. package/schemas/spec-page.schema.json +98 -0
  29. package/schemas/spec-pages-index.schema.json +1 -0
  30. package/schemas/spec-unresolved-shard.schema.json +1 -0
  31. package/schemas/task-envelope.schema.json +55 -0
  32. package/schemas/task-plan.schema.json +123 -0
  33. package/skills/android2hmos_resources_convert/SKILL.md +162 -0
  34. package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -0
  35. package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -0
  36. package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -0
  37. package/skills/appgraph-rule-audit/SKILL.md +58 -0
  38. package/skills/appgraph-rule-audit/references/audit-contract.md +47 -0
  39. package/skills/appgraph-rule-audit/references/recommendation-schema.md +41 -0
  40. package/skills/appgraph-rule-audit/schemas/rule-opportunities.schema.json +125 -0
  41. package/skills/arkts-app-identity/SKILL.md +238 -0
  42. package/skills/arkts-i18n/SKILL.md +496 -0
  43. package/skills/arkts-i18n/evals/evals.json +84 -0
  44. package/skills/arkts-i18n/references/code-examples.md +302 -0
  45. package/skills/arkts-i18n/references/common-pitfalls.md +391 -0
  46. package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -0
  47. package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -0
  48. package/skills/arkts-i18n/references/language-codes.md +104 -0
  49. package/skills/arkts-i18n/references/resource-file-structure.md +775 -0
  50. package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -0
  51. package/skills/arkts-i18n/references/v1-compat.md +244 -0
  52. package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -0
  53. package/skills/arkts-icon-sizing/SKILL.md +211 -0
  54. package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -0
  55. package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -0
  56. package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -0
  57. package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -0
  58. package/skills/arkts-mvvm-architecture/SKILL.md +613 -0
  59. package/skills/harmonyos-migration-playbook/SKILL.md +56 -0
  60. package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -0
  61. package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -0
  62. package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -0
  63. package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -0
  64. package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -0
  65. package/skills/harmonyos-migration-playbook/references/ui-automation.md +52 -0
  66. package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -0
  67. package/skills/maintaining-migration-report/SKILL.md +155 -0
  68. package/skills/native-library-substitution/SKILL.md +385 -0
  69. package/skills/native-library-substitution/references/native-library-substitution.json +56906 -0
  70. package/skills/native-library-substitution/references/native-library-substitution.md +163 -0
  71. package/skills/preparing-migration-workspace/SKILL.md +124 -0
  72. package/skills/preparing-migration-workspace/toolchain.json +43 -0
  73. package/skills/reviewing-migration-process/SKILL.md +62 -0
  74. package/src/install-opencode.mjs +110 -0
@@ -0,0 +1,613 @@
1
+ ---
2
+ name: arkts-mvvm-architecture
3
+ description: >-
4
+ HarmonyOS/ArkTS 工程架构规范与落地指南(MVVM + 状态管理 V2 + ArkTS 严格类型 + 导航/生命周期/模块化/并发/工程治理)。当用户需要新建鸿蒙工程、搭建或重构目录分层、设计 Model/ViewModel/View/Repository、使用 @ComponentV2、@Local、@Param、@Event、@ObservedV2、@Trace、@Monitor、@Computed、AppStorageV2、PersistenceV2,或使用 Navigation/NavPathStack、设计 UIAbility 生命周期、拆分 HAR/HSP、TaskPool/Worker 并发,或询问「ArkTS 怎么写」「MVVM 怎么分层」「V2 状态管理怎么用」「工程目录怎么放」「状态不刷新怎么办」「页面怎么跳转」「怎么拆包」时触发。也适用于代码评审与规范检查。Use for HarmonyOS ArkTS project architecture, MVVM layering, state management V2, navigation, lifecycle, modularization and concurrency. 不处理 Android→鸿蒙迁移(另见迁移类 skill)、i18n、图片资源转换。
5
+ type: domain
6
+ domain: engineering
7
+ tags: [arkts, mvvm, state-management-v2, navigation, architecture, harmonyos]
8
+ ---
9
+
10
+ # HarmonyOS 工程架构规范(MVVM + V2 + ArkTS)
11
+
12
+ 本 skill 是鸿蒙应用**架构与编码规范**的事实来源。生成或评审 ArkTS 代码时严格遵守下列规范。
13
+ 核心三原则:**MVVM 分层**、**状态管理一律用 V2**、**ArkTS 严格类型**。
14
+
15
+ 目录:1 目录结构 · 2 MVVM 分层 · 3 状态管理 V2 · 4 ArkTS 编码规范 · 5 组件规范 · 6 导航与路由 ·
16
+ 7 Ability 与生命周期 · 8 模块化与打包 · 9 数据层与网络 · 10 并发规范 · 11 错误与日志 ·
17
+ 12 性能与安全 · 13 多设备适配 · 14 自检清单 · 15 错误对照 · 16 交付格式
18
+
19
+ ---
20
+
21
+ ## 1. 目录结构规范
22
+
23
+ 按「功能 + 分层」组织。`core/` 放跨模块共享资产,业务按 feature 内聚。
24
+
25
+ ```
26
+ entry/src/main/ets/
27
+ ├── entryability/
28
+ │ └── EntryAbility.ets
29
+ ├── pages/ # 页面入口(Navigation 容器 / 页面)
30
+ │ ├── Index.ets
31
+ │ └── DetailPage.ets
32
+ ├── view/ # 视图层:可复用 UI 组件
33
+ │ └── components/
34
+ │ ├── UserCard.ets
35
+ │ └── EmptyView.ets
36
+ ├── viewmodel/ # 视图模型层:每个页面一个 VM
37
+ │ ├── IndexViewModel.ets
38
+ │ └── DetailViewModel.ets
39
+ ├── model/ # 数据模型层:实体、DTO、枚举
40
+ │ ├── UserModel.ets
41
+ │ └── ApiResponse.ets
42
+ ├── repository/ # 仓储层:聚合数据源,对 VM 暴露接口
43
+ │ ├── UserRepository.ets # 接口 + 默认实现
44
+ │ └── impl/
45
+ │ └── UserRepositoryImpl.ets
46
+ ├── datasource/ # 数据源层:远程 / 本地
47
+ │ ├── remote/
48
+ │ │ └── UserRemoteSource.ets
49
+ │ └── local/
50
+ │ └── UserLocalSource.ets
51
+ ├── core/ # 跨模块公共资产(或独立 HAR/HSP)
52
+ │ ├── constants/
53
+ │ │ ├── AppConstants.ets
54
+ │ │ ├── RouteConstants.ets
55
+ │ │ └── ErrorCode.ets
56
+ │ ├── utils/
57
+ │ │ ├── Logger.ets
58
+ │ │ └── DateUtil.ets
59
+ │ ├── network/
60
+ │ │ └── HttpClient.ets
61
+ │ ├── storage/
62
+ │ │ └── PreferencesUtil.ets
63
+ │ └── theme/
64
+ │ └── AppTheme.ets
65
+ └── router/
66
+ └── RouterManager.ets
67
+ ```
68
+
69
+ 规则:
70
+ - **model/ 集中定义数据模型**,所有层从这里导入,禁止在 View 内就地定义业务实体。
71
+ - 一个页面/组件对应一个 ViewModel,命名 `<名称>ViewModel`。
72
+ - 可复用 UI 放 `view/components/`,不写业务请求逻辑。
73
+ - 工具、网络、存储、常量统一进 `core/`。
74
+ - 分层依赖**单向向下**:`view → viewmodel → repository → datasource → core`。禁止反向依赖与跨 feature 直接引用内部 VM。
75
+ - 单模块工程用本结构;多模块工程把 `core/` 提升为独立 HAR(见 §8)。
76
+
77
+ ---
78
+
79
+ ## 2. MVVM 分层职责
80
+
81
+ | 层 | 位置 | 职责 | 禁止 |
82
+ | --- | --- | --- | --- |
83
+ | Model | `model/` | 数据结构、DTO、枚举、纯数据转换 | 引用 UI、持有页面状态、发请求 |
84
+ | ViewModel | `viewmodel/` | 持有状态、组织用例、调 Repository、暴露只读数据与事件 | 直接操作 UI、import ArkUI 组件、直连三方库 |
85
+ | View | `pages/`、`view/` | 渲染、绑定 VM、转发用户事件 | 写网络/复杂逻辑、跨层直连 Model 做业务 |
86
+ | Repository | `repository/` | 对 VM 暴露稳定接口,聚合/编排数据源、缓存策略 | 写 UI、持有页面状态 |
87
+ | DataSource | `datasource/` | 具体远程/本地读写(HTTP、RDB、KV、Preferences) | 写业务规则、跨源编排 |
88
+
89
+ 数据流单向:**View(事件) → ViewModel(改状态/调 Repository) → 状态变更 → View 自动刷新**。
90
+
91
+ 示例:VM 依赖 Repository 接口,而非具体实现(便于替换与测试):
92
+
93
+ ```typescript
94
+ // repository/UserRepository.ets
95
+ import { UserModel } from '../model/UserModel';
96
+
97
+ export interface UserRepository {
98
+ fetchUsers(keyword: string): Promise<UserModel[]>;
99
+ }
100
+ ```
101
+
102
+ ```typescript
103
+ // viewmodel/IndexViewModel.ets
104
+ import { BusinessError } from '@kit.BasicServicesKit';
105
+ import { UserModel } from '../model/UserModel';
106
+ import { UserRepository } from '../repository/UserRepository';
107
+ import { UserRepositoryImpl } from '../repository/impl/UserRepositoryImpl';
108
+ import { Logger } from '../core/utils/Logger';
109
+
110
+ @ObservedV2
111
+ export class IndexViewModel {
112
+ private repo: UserRepository = new UserRepositoryImpl();
113
+
114
+ @Trace userList: UserModel[] = [];
115
+ @Trace isLoading: boolean = false;
116
+ @Trace keyword: string = '';
117
+
118
+ @Computed
119
+ get hasData(): boolean {
120
+ return this.userList.length > 0;
121
+ }
122
+
123
+ async loadUsers(): Promise<void> {
124
+ this.isLoading = true;
125
+ try {
126
+ this.userList = await this.repo.fetchUsers(this.keyword);
127
+ } catch (err) {
128
+ const e = err as BusinessError;
129
+ Logger.error('IndexViewModel', `loadUsers failed: ${e.code} ${e.message}`);
130
+ } finally {
131
+ this.isLoading = false;
132
+ }
133
+ }
134
+
135
+ toggleFollow(user: UserModel): void {
136
+ user.isFollowed = !user.isFollowed;
137
+ }
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 3. 状态管理:一律用 V2
144
+
145
+ V2 装饰器是内置的,**无需 import**。旧版 `@Component/@State/@Prop/@Link/@Observed/@ObjectLink` 在新增代码中**禁止使用**。
146
+
147
+ ### 3.1 装饰器速查
148
+
149
+ | 装饰器 | 作用对象 | 用途 |
150
+ | --- | --- | --- |
151
+ | `@ComponentV2` | struct | 声明 V2 组件 |
152
+ | `@ObservedV2` | class | 声明可深度观测的类(Model/ViewModel) |
153
+ | `@Trace` | class 属性 | 使属性参与 V2 观测(**刷新关键**) |
154
+ | `@Local` | 组件成员 | 组件内部状态 |
155
+ | `@Param` | 组件成员 | 父→子入参(只读,可带默认值) |
156
+ | `@Once` | 配合 @Param | 入参只初始化一次,之后父变更不再同步 |
157
+ | `@Event` | 组件成员 | 子→父回调 |
158
+ | `@Provider` / `@Consumer` | 组件成员 | 组件树内跨层级共享(避免逐层透传) |
159
+ | `@Monitor` | 组件/VM 成员 | 监听指定属性变化,回调响应 |
160
+ | `@Computed` | getter | 派生状态,自动缓存 |
161
+
162
+ ### 3.2 响应式刷新铁律
163
+
164
+ **所有参与界面刷新的 Model / ViewModel 必须用 `@ObservedV2` 修饰,且需被观测的字段必须逐个加 `@Trace`。**
165
+
166
+ - 未加 `@Trace` 的属性即使所属类标了 `@ObservedV2` 也不会触发刷新。
167
+ - 嵌套对象(对象数组、子对象)的深层字段同样需要各自 `@ObservedV2 + @Trace`。
168
+ - **取舍**:纯 DTO(仅传输、从不直接绑定 UI)可保持为普通 class/interface,避免领域模型耦合 ArkUI;一旦要直接驱动刷新,就必须加 `@ObservedV2 + @Trace`(或用 VM 包装)。
169
+ - 类替换 / 数组整体赋值会触发刷新;只改普通字段不会,需该字段带 `@Trace`。
170
+
171
+ ### 3.3 全局状态与持久化(AppStorageV2 / PersistenceV2)
172
+
173
+ 页面内状态放 VM;**只有真正跨页面/全局共享**(登录会话、主题、用户设置)才用全局存储。
174
+
175
+ ```typescript
176
+ // core/store/AppStore.ets
177
+ import { AppStorageV2, PersistenceV2 } from '@kit.ArkUI';
178
+
179
+ @ObservedV2
180
+ export class SessionModel {
181
+ @Trace userId: string = '';
182
+ @Trace token: string = '';
183
+ @Trace isLogin: boolean = false;
184
+ }
185
+
186
+ // 内存级全局(进程存活期);此处 `!` 是存储边界的受控断言(§4.7 例外),connect 必返回实例
187
+ const session: SessionModel = AppStorageV2.connect(SessionModel, 'session', () => new SessionModel())!;
188
+
189
+ // 持久化全局(重启仍在),键名与类型必须稳定
190
+ @ObservedV2
191
+ export class SettingsModel {
192
+ @Trace theme: string = 'light';
193
+ @Trace language: string = 'zh-CN';
194
+ }
195
+ const settings: SettingsModel = PersistenceV2.connect(SettingsModel, 'settings', () => new SettingsModel())!;
196
+ ```
197
+
198
+ 规则:
199
+ - V2 全局存储的类同样需 `@ObservedV2`,需刷新的字段需 `@Trace`。
200
+ - 仅对可序列化字段持久化;敏感 token 存 [加密存储/KeyStore],不要进 PersistenceV2。
201
+ - 关闭/变更时调用 `AppStorageV2.remove(key)` / 更新,避免脏数据。
202
+ - 组件子树内共享优先用 `@Provider/@Consumer`;跨页面才用 AppStorageV2。
203
+
204
+ ### 3.4 完整示例
205
+
206
+ ```typescript
207
+ // model/UserModel.ets
208
+ @ObservedV2
209
+ export class UserModel {
210
+ @Trace id: string = '';
211
+ @Trace name: string = '';
212
+ @Trace avatar: string = '';
213
+ @Trace isFollowed: boolean = false;
214
+ }
215
+ ```
216
+
217
+ ```typescript
218
+ // pages/Index.ets
219
+ import { IndexViewModel } from '../viewmodel/IndexViewModel';
220
+ import { UserModel } from '../model/UserModel';
221
+ import { UserCard } from '../view/components/UserCard';
222
+
223
+ @Entry
224
+ @ComponentV2
225
+ struct Index {
226
+ private viewModel: IndexViewModel = new IndexViewModel();
227
+
228
+ aboutToAppear(): void {
229
+ this.viewModel.loadUsers();
230
+ }
231
+
232
+ build() {
233
+ Column({ space: 12 }) {
234
+ Search({ value: this.viewModel.keyword })
235
+ .onChange((value: string) => {
236
+ this.viewModel.keyword = value;
237
+ })
238
+
239
+ if (this.viewModel.hasData) {
240
+ List() {
241
+ ForEach(this.viewModel.userList, (user: UserModel) => {
242
+ ListItem() {
243
+ UserCard({
244
+ user: user,
245
+ cardOnFollow: (u: UserModel): void => this.viewModel.toggleFollow(u)
246
+ })
247
+ }
248
+ }, (user: UserModel) => user.id)
249
+ }
250
+ } else {
251
+ Text('暂无数据')
252
+ }
253
+ }
254
+ .width('100%')
255
+ .height('100%')
256
+ }
257
+ }
258
+ ```
259
+
260
+ ```typescript
261
+ // view/components/UserCard.ets
262
+ import { UserModel } from '../../model/UserModel';
263
+
264
+ @ComponentV2
265
+ export struct UserCard {
266
+ @Require @Param user: UserModel;
267
+ @Event cardOnFollow: (user: UserModel) => void = (user: UserModel): void => {};
268
+
269
+ build() {
270
+ Row({ space: 8 }) {
271
+ Image(this.user.avatar)
272
+ .width(40)
273
+ .height(40)
274
+ .borderRadius(20)
275
+ Text(this.user.name).fontSize(16).layoutWeight(1)
276
+ Button(this.user.isFollowed ? '已关注' : '关注')
277
+ .onClick(() => this.cardOnFollow(this.user))
278
+ }
279
+ .width('100%')
280
+ }
281
+ }
282
+ ```
283
+
284
+ ---
285
+
286
+ ## 4. ArkTS 编码规范
287
+
288
+ ArkTS 比 TypeScript 更严格,以下为硬性约束:
289
+
290
+ 1. **禁止 `any` / `unknown`**:用具体类型或自定义 `interface`/`class`。
291
+ 2. **`as` 仅在「类型收窄边界」允许**:路由参数、JSON 反序列化、`catch (err)`。其余场景用类型守卫/泛型/重构数据流。边界处必须紧跟校验,例如 `catch (err) { const e = err as BusinessError; }`。
292
+ 3. **禁止 `for...in`**:遍历一律用 `for...of`,或 `Array.forEach/map/filter`。
293
+ 4. **禁止动态属性访问**:`obj[key]`、`Reflect`、`delete` 不可用;键值不固定的场景用 `Map<string, T>`,不要用 `Record` 做动态下标。
294
+ 5. **所有类型显式声明**:变量、参数、返回值不依赖推断得出 `any`。
295
+ 6. **对象字面量必须有明确类型**:赋给已声明类型的变量,或作为具名形参,不可直接传未定型字面量。
296
+ 7. **禁止滥用 `!` 非空断言**;用 `??` 提供默认值。
297
+ 8. **`build()` 方法约束**:
298
+ - 禁止 `return`(提前返回);
299
+ - 禁止声明局部变量;
300
+ - 禁止传入无类型对象字面量;
301
+ - 条件渲染用 `if/else`,循环用 `ForEach/LazyForEach`。
302
+ 9. **`@Param`/`@Event` 属性名不得与 ArkUI 内置属性同名**(`onClick`、`borderColor`、`width`、`height`、`enabled`、`visibility` 等),必须加业务前缀,如 `cardOnClick`、`cardOnFollow`、`cardWidth`。
303
+ 10. **常见替代**:
304
+ - `Fsys.symbol` → 用 Emoji 或本地资源图片;
305
+ - `for...in` → `for...of`;
306
+ - `any` → `interface` / 泛型;
307
+ - 动态字典 → `Map<string, T>`;
308
+ - 多态分支 → 判别联合类型(`type` 字段)+ `switch`。
309
+ 11. **导入必须完整**:每个被使用的类型/类都显式 `import`,禁止隐式全局。
310
+ 12. **每个文件一个主导出**,文件名与主类/struct 名一致。
311
+ 13. **命名**:类/struct `PascalCase`,方法/变量 `camelCase`,常量 `UPPER_SNAKE_CASE`,私有成员加 `private`。
312
+ 14. **字符串拼接与日志**用模板串;统一经 `Logger`,禁止裸 `console.log` 进主干。
313
+
314
+ ---
315
+
316
+ ## 5. 组件规范
317
+
318
+ - 组件一律 `@ComponentV2`,入口页面再加 `@Entry`。
319
+ - 入参用 `@Param`:需要父必传加 `@Require`(无需再给默认值);否则给出默认值。
320
+ - 回调用 `@Event`;组件自身状态用 `@Local`(不要 `@State`)。
321
+ - 组件只做渲染与事件转发,业务逻辑放 ViewModel。
322
+ - 组件树内跨层共享用 `@Provider/@Consumer`,超过两层不再手动透传。
323
+ - 列表项必须提供稳定 `keyGenerator`(如 `id`),禁止用索引。
324
+ - 长列表用 `LazyForEach` + `IDataSource`,配合 `@Reusable` 复用组件。
325
+ - 组件间通过接口/数据类传参,避免直接依赖具体 ViewModel 实现。
326
+ - 弹窗/半模态用 `@Builder` + `bindSheet/bindContentCover/customDialog`,统一封装在 `view/`。
327
+
328
+ ---
329
+
330
+ ## 6. 导航与路由规范
331
+
332
+ **统一采用 `Navigation` + `NavPathStack`(系统路由/组件导航),不使用已过时的 `router`。**
333
+
334
+ ```typescript
335
+ // core/constants/RouteConstants.ets
336
+ export class RouteNames {
337
+ static readonly DETAIL: string = 'DetailPage';
338
+ static readonly SETTINGS: string = 'SettingsPage';
339
+ }
340
+ ```
341
+
342
+ ```typescript
343
+ // pages/Index.ets(导航容器)
344
+ import { RouteNames } from '../core/constants/RouteConstants';
345
+
346
+ @Entry
347
+ @ComponentV2
348
+ struct Index {
349
+ @Provider() pathStack: NavPathStack = new NavPathStack();
350
+
351
+ @Builder
352
+ pageMap(name: string, param: object) {
353
+ if (name === RouteNames.DETAIL) {
354
+ DetailPage()
355
+ } else if (name === RouteNames.SETTINGS) {
356
+ SettingsPage()
357
+ }
358
+ }
359
+
360
+ build() {
361
+ Navigation(this.pathStack) {
362
+ // 首页内容
363
+ Button('去详情')
364
+ .onClick(() => this.pathStack.pushPathByName(RouteNames.DETAIL, new DetailParam('1001')))
365
+ }
366
+ .navDestination(this.pageMap)
367
+ .mode(NavigationMode.Stack)
368
+ .title('首页')
369
+ }
370
+ }
371
+ ```
372
+
373
+ ```typescript
374
+ // pages/DetailPage.ets
375
+ import { DetailParam } from '../model/DetailParam';
376
+
377
+ @ComponentV2
378
+ struct DetailPage {
379
+ private param: DetailParam = new DetailParam('');
380
+ @Consumer() pathStack: NavPathStack = new NavPathStack();
381
+
382
+ build() {
383
+ NavDestination() {
384
+ Column() {
385
+ Text(`id=${this.param.id}`)
386
+ Button('返回').onClick(() => this.pathStack.pop())
387
+ }
388
+ }
389
+ .onReady((ctx: NavDestinationContext) => {
390
+ const p = ctx.pathInfo.param;
391
+ if (p instanceof DetailParam) {
392
+ this.param = p;
393
+ }
394
+ })
395
+ }
396
+ }
397
+ ```
398
+
399
+ 规则:
400
+ - 路由名集中放 `RouteConstants`,禁止散落字符串。
401
+ - 参数必须是可序列化对象(推荐数据类),在 `onReady` 通过 `instanceof`/类型守卫收窄,避免裸 `as`。
402
+ - 路由表可抽到 `resources/base/profile/router_map.json` + `module.json5` 的 `routerMap` 以支持按需/跨模块路由;跨模块页面走该路由表,避免 HB 间直接 import 页面。
403
+ - 返回拦截用 `NavDestination.onBackPressed`;埋点/登录守卫在 `RouterManager` 统一收口。
404
+ - 跨模块路由封装在 `router/RouterManager.ets`,暴露 `navigateTo/pop/replace`,禁止各处直接 `pushPath`。
405
+
406
+ ---
407
+
408
+ ## 7. Ability 与生命周期
409
+
410
+ **UIAbility 生命周期**(`entryability/EntryAbility.ets`):
411
+
412
+ | 阶段 | 时机 | 典型用途 |
413
+ | --- | --- | --- |
414
+ | `onCreate` | 实例创建 | 初始化全局资源、注册监听 |
415
+ | `onWindowStageCreate` | 窗口创建 | `loadContent` 加载首页、设置窗口 |
416
+ | `onForeground` | 切前台 | 刷新/恢复 |
417
+ | `onBackground` | 切后台 | 保存状态、暂停任务 |
418
+ | `onWindowStageDestroy` | 窗口销毁 | 释放 UI 资源 |
419
+ | `onDestroy` | 实例销毁 | 注销监听、释放资源 |
420
+
421
+ ```typescript
422
+ // entryability/EntryAbility.ets
423
+ import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
424
+ import { window } from '@kit.ArkUI';
425
+ import { Logger } from '../core/utils/Logger';
426
+
427
+ export default class EntryAbility extends UIAbility {
428
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
429
+ Logger.info('EntryAbility', 'onCreate');
430
+ }
431
+
432
+ onWindowStageCreate(windowStage: window.WindowStage): void {
433
+ windowStage.loadContent('pages/Index');
434
+ }
435
+
436
+ onDestroy(): void {
437
+ Logger.info('EntryAbility', 'onDestroy');
438
+ }
439
+ }
440
+ ```
441
+
442
+ **页面/组件生命周期**(V2 组件):
443
+ - `aboutToAppear`:入参就绪后、首次 build 前,做数据加载;
444
+ - `aboutToDisappear`:销毁前,做清理(取消订阅、定时器);
445
+ - `onPageShow/onPageHide`:仅 `@Entry` 页面生效,处理可见性刷新;
446
+ - `onBackPress`:返回键拦截,返回 `true` 表示已消费。
447
+
448
+ 规则:
449
+ - 启动模式在 `module.json5` 的 `abilities[].launchType` 指定:`singleton`(默认)/`multiton`/`specified`。多实例场景显式选 `multiton`。
450
+ - 冷启动只做必要初始化,重活异步化(见 §10);避免在 `onCreate`/`aboutToAppear` 同步阻塞。
451
+ - 订阅/定时器在 `aboutToDisappear`/`onDestroy` 成对释放。
452
+ - 页面刷新与 VM 加载解耦:可见性刷新走 `onPageShow`,一次性加载走 `aboutToAppear`。
453
+
454
+ ---
455
+
456
+ ## 8. 模块化与打包
457
+
458
+ - **单 HAP 只承载入口与少量页面**;业务按 feature 拆分为 **HSP(动态共享包,运行时共享单例)** 或 **HAR(静态共享包,编译期内联)**。
459
+ - 推荐三层:`entry`(HAP,壳) → `features`(HAR/HSP,业务) → `commons/core`(HAR,基础设施)。
460
+ - **依赖方向单向**:feature 依赖 core,core 不反向;feature 之间禁止直接依赖,需通信则下沉 core 或走路由表。
461
+ - HAR 的页面通过 §6 的系统路由表暴露,调用方不直接 import 页面。
462
+ - 三方库统一在 core 或各 HAR 声明,注意 HSP 单例语义(全局状态不要跨 HSP 重复创建)。
463
+ - 常量、主题、网络、存储等基础设施放 core HAR,供所有 feature 复用。
464
+
465
+ ```
466
+ root/
467
+ ├── entry/ # HAP:入口 Ability + 首页
468
+ ├── features/
469
+ │ ├── home/ # HSP
470
+ │ └── profile/ # HSP
471
+ ├── commons/
472
+ │ └── core/ # HAR:网络/存储/工具/主题/UI 基座
473
+ └── build-profile.json5
474
+ ```
475
+
476
+ ---
477
+
478
+ ## 9. 数据层与网络规范
479
+
480
+ 原则:**统一出口、按需选型**。网络实现允许使用系统 API,也允许使用三方库(如 `@ohos/axios`、`@ohos/retrofit`、okhttp 移植版等),不强制只用某一种。
481
+
482
+ - 网络能力收敛在 `datasource/remote/`,经 `repository/` 对 VM 暴露接口;VM **不直接**调用三方库。
483
+ - 三方库选型自由,但必须限定在 datasource 内,替换实现不影响 VM。
484
+ - 每个请求返回 `Promise<T>`,`T` 优先使用 `model/` 中定义的 DTO。
485
+ - 统一处理:拦截器/重试/超时/鉴权/错误码映射放在 `core/network/HttpClient.ets` 或 repository 基类。
486
+ - 错误统一 `try/catch` + `Logger`,不允许静默吞异常。
487
+ - 持久化集中在 `datasource/local/`(Preferences、KV、RDB 或三方库均可),避免散落调用。
488
+ - 常量/路由名集中放 `core/constants/`,禁止魔法字符串与魔法数字。
489
+
490
+ ---
491
+
492
+ ## 10. 并发规范
493
+
494
+ - **UI 线程只做渲染与状态更新**,耗时计算/IO 不得阻塞主线程。
495
+ - **短时并行任务**用 `TaskPool`:`@Concurrent` 函数必须是顶层(或 `static`)函数,不能捕获闭包变量。
496
+ - **长时/有状态任务**(需常驻、双向通信)用 `Worker`。
497
+ - 大对象(`ArrayBuffer`)跨线程传输用 `task.setTransferList` 转移所有权,避免拷贝。
498
+ - 并发数受控:`TaskPool` 自动调度,不要无节制 `execute`;`Worker` 实例数要有限。
499
+ - 任务内**禁止访问 UI**、禁止直接改 `@Trace` 状态;结果通过 `Promise` 回主线程后再更新状态。
500
+ - 取消/超时:使用 `task.cancel` 或 `Promise.race` + 超时,页面销毁时取消在途任务(`aboutToDisappear`)。
501
+
502
+ ```typescript
503
+ import { taskpool } from '@kit.ArkTS';
504
+
505
+ // @Concurrent 函数必须是顶层函数,参数/返回值须为可序列化类型
506
+ @Concurrent
507
+ function parseLargeJson(raw: string): ParsedData { /* ... */ }
508
+
509
+ // 调用侧(主线程)
510
+ async function run(): Promise<void> {
511
+ const result: ParsedData = await taskpool.execute(parseLargeJson, raw) as ParsedData;
512
+ }
513
+ ```
514
+
515
+ ---
516
+
517
+ ## 11. 错误与日志
518
+
519
+ - 统一错误类型 `BusinessError`,错误码集中在 `core/constants/ErrorCode.ets`(枚举/常量),禁止魔法码。
520
+ - `catch` 必须处理或上报,**禁止空 catch**;UI 层对失败给出可读提示,不外泄堆栈。
521
+ - `Logger` 分级:`DEBUG / INFO / WARN / ERROR`;Release 构建关闭 DEBUG。日志带 `TAG` 与上下文,禁止打印隐私与密钥。
522
+ - 关键链路(登录、支付、启动)失败需可追踪,必要时接 `HiAppEvent`/faultLogger 上报。
523
+
524
+ ```typescript
525
+ // core/constants/ErrorCode.ets
526
+ export enum ErrorCode {
527
+ NETWORK_TIMEOUT = 10001,
528
+ UNAUTHORIZED = 10002,
529
+ SERVER_ERROR = 10500
530
+ }
531
+ ```
532
+
533
+ ---
534
+
535
+ ## 12. 性能与安全
536
+
537
+ **性能**
538
+ - 启动:冷启动只做必要初始化,非首屏任务延迟/异步;避免 `aboutToAppear` 同步重活。
539
+ - 列表:长列表用 `LazyForEach` + `IDataSource`,组件 `@Reusable`。
540
+ - 渲染:减少无效状态导致的全量刷新,合理拆分 `@Trace` 粒度。
541
+ - 图片:按需解码/降采样,列表图用缓存;避免超大图直出。
542
+ - 包体:按 feature 拆 HSP/HAR,移除未用资源与依赖。
543
+
544
+ **安全**
545
+ - 密钥/token 不得硬编码,敏感数据用 [KeyStore/加密存储],不要写进 PersistenceV2。
546
+ - 权限最小化,运行时权限按需申请并说明用途;隐私弹窗合规。
547
+ - 网络默认 HTTPS,证书校验不可关闭;日志脱敏。
548
+
549
+ ---
550
+
551
+ ## 13. 多设备适配
552
+
553
+ - 用断点(`sm/md/lg`,如 320/600/840vp)与栅格 `GridRow/GridCol` 做响应式,而非写死尺寸。
554
+ - 优先百分比/`layoutWeight`/`Flex`,关键页适配手机、平板、折叠屏。
555
+ - 一多部署:一套代码多设备,用资源限定符与 `mediaquery` 区分布局,不复制工程。
556
+ - 主题/字号/间距用 `core/theme` 的 token,禁止散落魔法值。
557
+
558
+ ---
559
+
560
+ ## 14. 自检清单(生成代码后逐条核对)
561
+
562
+ - [ ] 是否使用 `@ComponentV2`,且未出现 `@Component/@State/@Prop/@Link`?
563
+ - [ ] 参与刷新的 Model/ViewModel 是否 `@ObservedV2`,需刷新字段是否逐个 `@Trace`?
564
+ - [ ] 全局共享是否用 `AppStorageV2/PersistenceV2`,且仅限真正跨页面状态?
565
+ - [ ] 页面/组件是否有对应 `viewmodel/`,逻辑是否已下沉?
566
+ - [ ] VM 是否依赖 Repository 接口而非具体数据源/三方库?
567
+ - [ ] Model 是否集中在 `model/` 且被完整 import?
568
+ - [ ] 是否出现 `any`/`unknown`/滥用 `as`/`for...in`/`obj[key]`/`Record` 动态下标?
569
+ - [ ] `build()` 内有 `return`、局部变量或无类型字面量吗?
570
+ - [ ] `@Param`/`@Event` 名是否与 ArkUI 内置属性冲突(已加业务前缀)?
571
+ - [ ] 长列表是否用 `LazyForEach` + 稳定 key,必要时 `@Reusable`?
572
+ - [ ] 页面跳转是否走 `Navigation/NavPathStack` + 集中路由名,未用旧 `router`?
573
+ - [ ] 订阅/定时器/在途任务是否在 `aboutToDisappear/onDestroy` 释放?
574
+ - [ ] 耗时任务是否移出主线程(TaskPool/Worker)?
575
+ - [ ] 网络请求是否收敛在数据层(datasource/repository),未散落在 View?
576
+ - [ ] 错误是否统一 `BusinessError` + 错误码 + `Logger`,无空 catch?
577
+
578
+ 任何一项不通过 → 先修正再交付。
579
+
580
+ ---
581
+
582
+ ## 15. 常见错误对照表
583
+
584
+ | 错误写法 | 正确写法 |
585
+ | --- | --- |
586
+ | `@Component struct A {}` | `@ComponentV2 struct A {}` |
587
+ | `@State count: number = 0` | `@Local count: number = 0` |
588
+ | `@Prop` / `@Link` | `@Param`(只读)/ `@Event` + VM 方法 |
589
+ | 类标 `@ObservedV2` 但字段无 `@Trace` | 每个需刷新的字段加 `@Trace` |
590
+ | `@Observed class M {}` | `@ObservedV2 class M { @Trace x }` |
591
+ | `let list: any[]` | `let list: UserModel[]` |
592
+ | `for (const k in obj)` | `for (const k of Object.keys(obj))` |
593
+ | `obj['name']` | `obj.name` 或 `map.get('name')` |
594
+ | `Record<string, T>` 动态下标 | `Map<string, T>` |
595
+ | `build() { if (x) return ...; ... }` | `build() { if (x) { ... } else { ... } }` |
596
+ | `@Param onClick: () => void` | `@Event cardOnClick: () => void` |
597
+ | `router.pushUrl({ url: 'pages/Detail' })` | `pathStack.pushPathByName(RouteNames.DETAIL, param)` |
598
+ | VM 里 `new Axios(...)` | VM 依赖 `Repository`,三方库在 datasource |
599
+ | `catch (e) {}` 空捕获 | 上报 `BusinessError` + 错误码 + `Logger` |
600
+
601
+ ---
602
+
603
+ ## 16. 交付格式
604
+
605
+ 生成页面/组件代码时,按此顺序输出:
606
+ 1. `model/` 中的实体(若新增);
607
+ 2. `repository/` + `datasource/` 中的数据访问(若新增);
608
+ 3. `viewmodel/` 中的 VM;
609
+ 4. `pages/` 或 `view/components/` 中的视图;
610
+ 5. 涉及的 `core/`(网络/常量/日志)与路由注册;
611
+ 6. 末尾附「自检清单」核对结果。
612
+
613
+ 改动多个文件时,每个文件标注完整路径,import 路径与实际目录一致。
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: harmonyos-migration-playbook
3
+ description: Diagnose and reuse verified Android-to-HarmonyOS migration lessons for ArkTS compilation, HarmonyOS runtime, UI automation, protocol E2E, and Windows toolchains. Use during migration implementation, failure recovery, validation, or post-commit retrospectives; do not treat project-specific observations as universal platform rules.
4
+ ---
5
+
6
+ # HarmonyOS migration playbook
7
+
8
+ Use verified experience to shorten diagnosis without replacing current-project evidence.
9
+
10
+ ## Required operating loop
11
+
12
+ 1. Identify the current symptom and task boundary.
13
+ 2. Read only the relevant reference below.
14
+ 3. Check that the recorded applicability conditions match the current SDK, UI stack, runtime and code path.
15
+ 4. Reproduce or inspect evidence before applying a fix. Historical experience is a hypothesis until confirmed in the current project.
16
+ 5. Apply the smallest in-scope fix and run the stated verification.
17
+ 6. After validation, create an explicit Git checkpoint with `droid2hmos checkpoint create`; record a reusable lesson only when the root cause and fix are proven.
18
+
19
+ ## Reference routing
20
+
21
+ - ArkTS compiler errors or language restrictions: read [references/arkts-compile.md](references/arkts-compile.md).
22
+ - Socket, TLS, IME, asynchronous execution, component state or theme problems: read [references/harmony-runtime.md](references/harmony-runtime.md).
23
+ - Emulator interaction, screenshots, `dumpLayout`, input or scrolling failures: read [references/ui-automation.md](references/ui-automation.md).
24
+ - IMAP/SMTP or other protocol-level end-to-end verification: read [references/protocol-e2e.md](references/protocol-e2e.md).
25
+ - Windows, Gradle, SDK, proxy, encoding or background-service process hangs: read [references/windows-environment.md](references/windows-environment.md).
26
+ - DevEco CLI login callback refused (`localhost 拒绝了连接`) or auth gate retries: read [references/windows-environment.md](references/windows-environment.md).
27
+ - Recording or promoting migration experience: read [references/lesson-lifecycle.md](references/lesson-lifecycle.md).
28
+
29
+ ## Evidence levels
30
+
31
+ - **Platform rule**: supported by the active SDK/compiler behavior or repeatable minimal reproduction.
32
+ - **Reusable pattern**: reproduced in more than one relevant project or independent environment.
33
+ - **Project-validated**: fully proven in one project and useful beyond that project. It may enter the public playbook immediately, but every new project must revalidate it before relying on it.
34
+ - **Candidate observation**: plausible but not yet proven; never use as a completion claim.
35
+
36
+ Prefer narrower classifications. Public does not mean universal: a project-validated lesson is visible for reuse but remains a hypothesis outside its source project. Record SDK/tool versions when behavior could change.
37
+
38
+ ## Non-negotiable boundaries
39
+
40
+ - Android source and observable behavior remain authoritative.
41
+ - Do not bypass AppGraph, compilation, tests or device validation merely because an earlier project was slow or unreliable. Diagnose the current failure; any explicit degradation must preserve a visible coverage gap.
42
+ - Do not convert implementation defects into platform limitations or external blockers.
43
+ - Do not claim a lesson is verified from a report, comment, code existence or compile success alone when runtime behavior is relevant.
44
+ - Do not modify the public playbook from every task. First record the lesson in the current project; a reviewed, non-obvious and well-verified single-project lesson may be promoted as `project-validated` under `lesson-lifecycle.md` instead of waiting invisibly for another project.
45
+
46
+ ## Quick triage
47
+
48
+ ```text
49
+ Compile failure -> ArkTS error code and exact source construct
50
+ Button does nothing -> reachability, UI tree, callback log, async context
51
+ Socket failure -> bind/connect/TLS stage and network logs
52
+ Login callback refused-> login process lifecycle (windows-environment.md)
53
+ Input corruption -> focus, IME state, input command and current value
54
+ Visual mismatch -> element-by-element layout/style/content comparison
55
+ Repeated task failure -> read project lessons, then verify applicability
56
+ ```
@@ -0,0 +1,7 @@
1
+ interface:
2
+ display_name: "HarmonyOS Migration Playbook"
3
+ short_description: "复用经验证的鸿蒙迁移诊断与测试经验"
4
+ default_prompt: "Use $harmonyos-migration-playbook to diagnose this migration issue and verify that the historical lesson applies here."
5
+
6
+ policy:
7
+ allow_implicit_invocation: true