@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,513 @@
1
+ # Android XML Drawable to SVG Conversion Rules
2
+
3
+ This document provides complete rules for converting Android XML drawable resources to SVG format for HarmonyOS. HarmonyOS natively supports SVG in `media/` directories, making SVG the ideal target format for XML drawables that would otherwise be lost in migration.
4
+
5
+ ## Table of Contents
6
+ 1. [General Principles](#general-principles)
7
+ 2. [VectorDrawable Conversion](#vectordrawable-conversion)
8
+ 3. [ShapeDrawable Conversion](#shapedrawable-conversion)
9
+ 4. [LayerList Conversion](#layerlist-conversion)
10
+ 5. [Selector / StateList Conversion](#selector--statelist-conversion)
11
+ 6. [Gradient Conversion](#gradient-conversion)
12
+ 7. [Common Attribute Mapping](#common-attribute-mapping)
13
+ 8. [Unconvertible Types](#unconvertible-types)
14
+
15
+ ---
16
+
17
+ ## General Principles
18
+
19
+ - Output files use the original Android filename with `.svg` extension (e.g., `ic_arrow.xml` → `ic_arrow.svg`)
20
+ - All SVG output should use the SVG 1.1 namespace: `xmlns="http://www.w3.org/2000/svg"`
21
+ - Android uses `dp` units for dimensions; convert to unitless numbers in SVG (1dp = 1 SVG user unit)
22
+ - Android colors use `#AARRGGBB` or `#RRGGBB`; SVG uses `#RRGGBB` with a separate `opacity` or `fill-opacity` attribute for alpha
23
+ - When a drawable XML references another resource (`@drawable/other`, `@color/primary`), resolve the reference if possible. If not resolvable, use a reasonable default and log it in the report.
24
+
25
+ ### Color Conversion (Android → SVG)
26
+
27
+ | Android Format | SVG Fill/Stroke | SVG Opacity |
28
+ |---|---|---|
29
+ | `#RRGGBB` | `#RRGGBB` | (none, fully opaque) |
30
+ | `#AARRGGBB` | `#RRGGBB` | `opacity="AA/255"` as decimal (e.g., `#80FF0000` → `fill="#FF0000" fill-opacity="0.502"`) |
31
+ | `#RGB` | Expand to `#RRGGBB` | (none) |
32
+ | `#ARGB` | Expand to `#RRGGBB` | Extract alpha as decimal |
33
+
34
+ ### Dimension Conversion
35
+
36
+ - `24dp` → `24` (strip `dp` suffix, use as unitless SVG user units)
37
+ - `@dimen/ref` → resolve from dimens.xml if available, otherwise use a default and log
38
+
39
+ ---
40
+
41
+ ## VectorDrawable Conversion
42
+
43
+ Android VectorDrawable (`<vector>`) is structurally very similar to SVG. This is the most straightforward conversion.
44
+
45
+ ### Android VectorDrawable Example
46
+
47
+ ```xml
48
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
49
+ android:width="24dp"
50
+ android:height="24dp"
51
+ android:viewportWidth="24"
52
+ android:viewportHeight="24"
53
+ android:tint="#FF000000"
54
+ android:alpha="0.8">
55
+ <group
56
+ android:name="arrow_group"
57
+ android:rotation="90"
58
+ android:pivotX="12"
59
+ android:pivotY="12"
60
+ android:scaleX="1.5"
61
+ android:scaleY="1.5"
62
+ android:translateX="2"
63
+ android:translateY="0">
64
+ <path
65
+ android:name="arrow"
66
+ android:pathData="M12,2L4.5,20.29l0.71,0.71L12,18l6.79,3 0.71,-0.71z"
67
+ android:fillColor="#FF4081"
68
+ android:fillAlpha="0.8"
69
+ android:strokeColor="#000000"
70
+ android:strokeWidth="1"
71
+ android:strokeAlpha="1"
72
+ android:strokeLineCap="round"
73
+ android:strokeLineJoin="round"
74
+ android:fillType="evenOdd"
75
+ android:trimPathStart="0"
76
+ android:trimPathEnd="1" />
77
+ <clip-path
78
+ android:name="clip"
79
+ android:pathData="M0,0h24v24H0z" />
80
+ </group>
81
+ </vector>
82
+ ```
83
+
84
+ ### Converted SVG Output
85
+
86
+ ```svg
87
+ <svg xmlns="http://www.w3.org/2000/svg"
88
+ width="24" height="24"
89
+ viewBox="0 0 24 24"
90
+ opacity="0.8">
91
+ <g transform="translate(2,0) scale(1.5,1.5) rotate(90,12,12)">
92
+ <defs>
93
+ <clipPath id="clip">
94
+ <path d="M0,0h24v24H0z"/>
95
+ </clipPath>
96
+ </defs>
97
+ <path d="M12,2L4.5,20.29l0.71,0.71L12,18l6.79,3 0.71,-0.71z"
98
+ fill="#FF4081" fill-opacity="0.8"
99
+ stroke="#000000" stroke-width="1"
100
+ stroke-linecap="round" stroke-linejoin="round"
101
+ fill-rule="evenodd"
102
+ clip-path="url(#clip)"/>
103
+ </g>
104
+ </svg>
105
+ ```
106
+
107
+ ### Element Mapping: `<vector>` → `<svg>`
108
+
109
+ | Android Attribute | SVG Equivalent |
110
+ |---|---|
111
+ | `android:width` | `width` (strip `dp`) |
112
+ | `android:height` | `height` (strip `dp`) |
113
+ | `android:viewportWidth` | Part of `viewBox="0 0 W H"` |
114
+ | `android:viewportHeight` | Part of `viewBox="0 0 W H"` |
115
+ | `android:alpha` | `opacity` on root `<svg>` |
116
+ | `android:tint` | Apply as a filter or ignore (SVG has no direct tint; log in report) |
117
+
118
+ ### Element Mapping: `<path>` → `<path>`
119
+
120
+ | Android Attribute | SVG Attribute |
121
+ |---|---|
122
+ | `android:pathData` | `d` (path data syntax is identical) |
123
+ | `android:fillColor` | `fill` (convert color format) |
124
+ | `android:fillAlpha` | `fill-opacity` |
125
+ | `android:strokeColor` | `stroke` (convert color format) |
126
+ | `android:strokeWidth` | `stroke-width` |
127
+ | `android:strokeAlpha` | `stroke-opacity` |
128
+ | `android:strokeLineCap` | `stroke-linecap` (`butt`, `round`, `square`) |
129
+ | `android:strokeLineJoin` | `stroke-linejoin` (`miter`, `round`, `bevel`) |
130
+ | `android:strokeMiterLimit` | `stroke-miterlimit` |
131
+ | `android:fillType` | `fill-rule` (`nonZero` → `nonzero`, `evenOdd` → `evenodd`) |
132
+ | `android:trimPathStart` | No direct SVG equivalent; if != 0, use `stroke-dasharray` + `stroke-dashoffset` to approximate, or log |
133
+ | `android:trimPathEnd` | Same as above |
134
+ | `android:trimPathOffset` | Same as above |
135
+
136
+ ### Element Mapping: `<group>` → `<g>`
137
+
138
+ Build the SVG `transform` attribute from the group's transformation properties. Apply in this order:
139
+ 1. `translate(translateX, translateY)`
140
+ 2. `scale(scaleX, scaleY)`
141
+ 3. `rotate(rotation, pivotX, pivotY)`
142
+
143
+ Combined: `transform="translate(tX,tY) scale(sX,sY) rotate(r,pX,pY)"`
144
+
145
+ Only include transform components that differ from defaults (translate 0,0; scale 1,1; rotation 0).
146
+
147
+ ### Element Mapping: `<clip-path>` → `<clipPath>` + `<path>`
148
+
149
+ ```xml
150
+ <!-- Android -->
151
+ <clip-path android:pathData="M0,0h24v24H0z"/>
152
+
153
+ <!-- SVG -->
154
+ <defs>
155
+ <clipPath id="generated_clip_id">
156
+ <path d="M0,0h24v24H0z"/>
157
+ </clipPath>
158
+ </defs>
159
+ <!-- Apply via clip-path="url(#generated_clip_id)" on sibling paths -->
160
+ ```
161
+
162
+ Generate unique IDs for clip paths (e.g., `clip_1`, `clip_2`, or derive from `android:name`).
163
+
164
+ ---
165
+
166
+ ## ShapeDrawable Conversion
167
+
168
+ Android `<shape>` drawables define geometric shapes with fill, stroke, gradients, corners, and sizing.
169
+
170
+ ### Shape Types
171
+
172
+ #### Rectangle (default)
173
+
174
+ ```xml
175
+ <!-- Android -->
176
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
177
+ android:shape="rectangle">
178
+ <solid android:color="#FF4081"/>
179
+ <stroke android:width="2dp" android:color="#000000"
180
+ android:dashWidth="4dp" android:dashGap="2dp"/>
181
+ <corners android:radius="8dp"/>
182
+ <size android:width="100dp" android:height="50dp"/>
183
+ <padding android:left="10dp" android:top="5dp"
184
+ android:right="10dp" android:bottom="5dp"/>
185
+ </shape>
186
+ ```
187
+
188
+ ```svg
189
+ <!-- SVG -->
190
+ <svg xmlns="http://www.w3.org/2000/svg" width="100" height="50" viewBox="0 0 100 50">
191
+ <rect x="0" y="0" width="100" height="50" rx="8" ry="8"
192
+ fill="#FF4081"
193
+ stroke="#000000" stroke-width="2"
194
+ stroke-dasharray="4,2"/>
195
+ </svg>
196
+ ```
197
+
198
+ **Corner radius variants:**
199
+ - `android:radius` → `rx` and `ry` (same value)
200
+ - `android:topLeftRadius`, `android:topRightRadius`, `android:bottomRightRadius`, `android:bottomLeftRadius` → When different per corner, use a `<path>` with rounded corner arcs instead of `<rect>`
201
+
202
+ **Per-corner radius SVG path formula:**
203
+ ```
204
+ M {tlr},0
205
+ H {w-trr}
206
+ Q {w},0 {w},{trr}
207
+ V {h-brr}
208
+ Q {w},{h} {w-brr},{h}
209
+ H {blr}
210
+ Q 0,{h} 0,{h-blr}
211
+ V {tlr}
212
+ Q 0,0 {tlr},0
213
+ Z
214
+ ```
215
+ Where `tlr`=topLeftRadius, `trr`=topRightRadius, `brr`=bottomRightRadius, `blr`=bottomLeftRadius, `w`=width, `h`=height.
216
+
217
+ #### Oval
218
+
219
+ ```xml
220
+ <!-- Android -->
221
+ <shape android:shape="oval">
222
+ <solid android:color="#2196F3"/>
223
+ <size android:width="60dp" android:height="60dp"/>
224
+ </shape>
225
+ ```
226
+
227
+ ```svg
228
+ <!-- SVG -->
229
+ <svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60">
230
+ <ellipse cx="30" cy="30" rx="30" ry="30" fill="#2196F3"/>
231
+ </svg>
232
+ ```
233
+
234
+ - If width == height, use `<circle>` instead: `<circle cx="30" cy="30" r="30"/>`
235
+
236
+ #### Line
237
+
238
+ ```xml
239
+ <!-- Android -->
240
+ <shape android:shape="line">
241
+ <stroke android:width="2dp" android:color="#000000"/>
242
+ <size android:height="2dp" android:width="100dp"/>
243
+ </shape>
244
+ ```
245
+
246
+ ```svg
247
+ <!-- SVG -->
248
+ <svg xmlns="http://www.w3.org/2000/svg" width="100" height="2" viewBox="0 0 100 2">
249
+ <line x1="0" y1="1" x2="100" y2="1" stroke="#000000" stroke-width="2"/>
250
+ </svg>
251
+ ```
252
+
253
+ #### Ring
254
+
255
+ ```xml
256
+ <!-- Android -->
257
+ <shape android:shape="ring"
258
+ android:innerRadius="20dp"
259
+ android:thickness="5dp">
260
+ <solid android:color="#4CAF50"/>
261
+ <size android:width="50dp" android:height="50dp"/>
262
+ </shape>
263
+ ```
264
+
265
+ ```svg
266
+ <!-- SVG -->
267
+ <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
268
+ <circle cx="25" cy="25" r="22.5"
269
+ fill="none" stroke="#4CAF50" stroke-width="5"/>
270
+ </svg>
271
+ ```
272
+
273
+ - Ring center radius = `innerRadius + thickness/2`
274
+ - SVG stroke-width = `thickness`
275
+ - If `android:innerRadiusRatio` is used instead of `android:innerRadius`: `innerRadius = width / (2 * innerRadiusRatio)`
276
+ - If `android:thicknessRatio` is used: `thickness = width / (2 * thicknessRatio)`
277
+
278
+ ### Shape Size Handling
279
+
280
+ If `<size>` is not specified, use a sensible default (e.g., `24x24`) and note in the report that the original had no explicit size. The SVG `viewBox` should match the dimensions.
281
+
282
+ ---
283
+
284
+ ## Gradient Conversion
285
+
286
+ Android shapes and vector drawables can use gradients for fill or stroke colors.
287
+
288
+ ### Linear Gradient
289
+
290
+ ```xml
291
+ <!-- Android (in shape) -->
292
+ <gradient
293
+ android:type="linear"
294
+ android:startColor="#FF0000"
295
+ android:centerColor="#00FF00"
296
+ android:endColor="#0000FF"
297
+ android:angle="45"/>
298
+ ```
299
+
300
+ ```svg
301
+ <!-- SVG -->
302
+ <defs>
303
+ <linearGradient id="grad_1" x1="0%" y1="100%" x2="100%" y2="0%">
304
+ <stop offset="0%" stop-color="#FF0000"/>
305
+ <stop offset="50%" stop-color="#00FF00"/>
306
+ <stop offset="100%" stop-color="#0000FF"/>
307
+ </linearGradient>
308
+ </defs>
309
+ <rect ... fill="url(#grad_1)"/>
310
+ ```
311
+
312
+ **Angle to coordinates mapping:**
313
+
314
+ | Android `angle` | SVG `x1,y1 → x2,y2` |
315
+ |---|---|
316
+ | 0 (left→right) | `0%,0% → 100%,0%` |
317
+ | 45 (bottom-left→top-right) | `0%,100% → 100%,0%` |
318
+ | 90 (bottom→top) | `0%,100% → 0%,0%` |
319
+ | 135 (bottom-right→top-left) | `100%,100% → 0%,0%` |
320
+ | 180 (right→left) | `100%,0% → 0%,0%` |
321
+ | 225 (top-right→bottom-left) | `100%,0% → 0%,100%` |
322
+ | 270 (top→bottom) | `0%,0% → 0%,100%` |
323
+ | 315 (top-left→bottom-right) | `0%,0% → 100%,100%` |
324
+
325
+ Android `angle` must be a multiple of 45. For other values, calculate:
326
+ - `x1 = 50 + 50*cos(angle+180)`, `y1 = 50 - 50*sin(angle+180)`
327
+ - `x2 = 50 + 50*cos(angle)`, `y2 = 50 - 50*sin(angle)`
328
+
329
+ ### Radial Gradient
330
+
331
+ ```xml
332
+ <!-- Android -->
333
+ <gradient
334
+ android:type="radial"
335
+ android:centerX="0.5"
336
+ android:centerY="0.5"
337
+ android:gradientRadius="50%p"
338
+ android:startColor="#FFFFFF"
339
+ android:endColor="#000000"/>
340
+ ```
341
+
342
+ ```svg
343
+ <!-- SVG -->
344
+ <defs>
345
+ <radialGradient id="grad_2" cx="50%" cy="50%" r="50%">
346
+ <stop offset="0%" stop-color="#FFFFFF"/>
347
+ <stop offset="100%" stop-color="#000000"/>
348
+ </radialGradient>
349
+ </defs>
350
+ ```
351
+
352
+ - `centerX`/`centerY` are fractions (0.0–1.0) → convert to percentages
353
+ - `gradientRadius` with `%p` suffix → percentage of parent dimension
354
+
355
+ ### Sweep Gradient
356
+
357
+ ```xml
358
+ <!-- Android -->
359
+ <gradient
360
+ android:type="sweep"
361
+ android:centerX="0.5"
362
+ android:centerY="0.5"
363
+ android:startColor="#FF0000"
364
+ android:endColor="#0000FF"/>
365
+ ```
366
+
367
+ SVG doesn't have a native sweep/conic gradient in SVG 1.1. Options:
368
+ 1. Use CSS `conic-gradient` if targeting modern renderers (but not SVG 1.1 compliant)
369
+ 2. Approximate with a series of `<path>` segments in a radial pattern
370
+ 3. Log as partially converted and note that sweep gradient was approximated
371
+
372
+ Recommended: convert to a `<linearGradient>` as an approximation and log in the report that the original was a sweep gradient.
373
+
374
+ ### Gradient in VectorDrawable Paths
375
+
376
+ Android API 24+ supports `<aapt:attr>` inline gradient in vector drawables:
377
+
378
+ ```xml
379
+ <path android:pathData="...">
380
+ <aapt:attr name="android:fillColor">
381
+ <gradient android:type="linear" .../>
382
+ </aapt:attr>
383
+ </path>
384
+ ```
385
+
386
+ Convert the gradient to an SVG `<linearGradient>` in `<defs>` and reference via `fill="url(#grad_id)"`.
387
+
388
+ ---
389
+
390
+ ## LayerList Conversion
391
+
392
+ `<layer-list>` stacks multiple drawable items. Each `<item>` can contain a `<shape>`, a `<bitmap>`, or reference another drawable.
393
+
394
+ ```xml
395
+ <!-- Android -->
396
+ <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
397
+ <item>
398
+ <shape android:shape="rectangle">
399
+ <solid android:color="#CCCCCC"/>
400
+ <corners android:radius="4dp"/>
401
+ </shape>
402
+ </item>
403
+ <item android:left="2dp" android:top="2dp"
404
+ android:right="2dp" android:bottom="2dp">
405
+ <shape android:shape="rectangle">
406
+ <solid android:color="#FFFFFF"/>
407
+ <corners android:radius="2dp"/>
408
+ </shape>
409
+ </item>
410
+ </layer-list>
411
+ ```
412
+
413
+ ```svg
414
+ <!-- SVG -->
415
+ <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
416
+ <!-- Layer 0: background -->
417
+ <rect x="0" y="0" width="100" height="100" rx="4" ry="4" fill="#CCCCCC"/>
418
+ <!-- Layer 1: foreground with insets -->
419
+ <rect x="2" y="2" width="96" height="96" rx="2" ry="2" fill="#FFFFFF"/>
420
+ </svg>
421
+ ```
422
+
423
+ ### Rules
424
+
425
+ - Items are rendered in document order (first item = bottom layer)
426
+ - `android:left/top/right/bottom` on `<item>` → inset the element's position and reduce its size accordingly
427
+ - `android:width/height` on `<item>` → explicit size override
428
+ - `android:gravity` on `<item>` → position within the layer bounds
429
+ - If an `<item>` references another drawable via `android:drawable="@drawable/other"`, try to resolve and inline it; if not resolvable, log in report
430
+ - For layer-lists without explicit `<size>`, use a default canvas of `100x100` and note in report
431
+
432
+ ---
433
+
434
+ ## Selector / StateList Conversion
435
+
436
+ `<selector>` defines different drawables for different UI states (pressed, focused, disabled, etc.). SVG is static, so we extract the most useful representation.
437
+
438
+ ```xml
439
+ <!-- Android -->
440
+ <selector xmlns:android="http://schemas.android.com/apk/res/android">
441
+ <item android:state_pressed="true">
442
+ <shape android:shape="rectangle">
443
+ <solid android:color="#E0E0E0"/>
444
+ <corners android:radius="4dp"/>
445
+ </shape>
446
+ </item>
447
+ <item android:state_focused="true">
448
+ <shape android:shape="rectangle">
449
+ <solid android:color="#F0F0F0"/>
450
+ <corners android:radius="4dp"/>
451
+ </shape>
452
+ </item>
453
+ <item>
454
+ <shape android:shape="rectangle">
455
+ <solid android:color="#FFFFFF"/>
456
+ <corners android:radius="4dp"/>
457
+ </shape>
458
+ </item>
459
+ </selector>
460
+ ```
461
+
462
+ ### Conversion Strategy
463
+
464
+ 1. Find the **default item** — the `<item>` with no `android:state_*` attributes (or the last item, which serves as the default in Android)
465
+ 2. Convert that single item to SVG using the appropriate rules (shape → SVG, vector → SVG, etc.)
466
+ 3. In the conversion report, list all the state variants that were dropped:
467
+ - `state_pressed`, `state_focused`, `state_selected`, `state_checked`, `state_enabled`, `state_activated`, `state_hovered`
468
+ 4. Note that HarmonyOS state handling should be done in ArkUI component code, not in resource files
469
+
470
+ ---
471
+
472
+ ## Common Attribute Mapping
473
+
474
+ ### Android `android:` namespace to SVG
475
+
476
+ | Android Attribute | SVG Equivalent | Notes |
477
+ |---|---|---|
478
+ | `android:fillColor` | `fill` | Convert color format |
479
+ | `android:strokeColor` | `stroke` | Convert color format |
480
+ | `android:strokeWidth` | `stroke-width` | Strip `dp` |
481
+ | `android:alpha` | `opacity` | 0.0-1.0 range |
482
+ | `android:fillAlpha` | `fill-opacity` | 0.0-1.0 range |
483
+ | `android:strokeAlpha` | `stroke-opacity` | 0.0-1.0 range |
484
+ | `android:rotation` | `transform="rotate(deg)"` | In `<group>` |
485
+ | `android:scaleX/Y` | `transform="scale(x,y)"` | In `<group>` |
486
+ | `android:translateX/Y` | `transform="translate(x,y)"` | In `<group>` |
487
+
488
+ ### Default Values
489
+
490
+ If an Android drawable doesn't specify certain attributes, use these defaults:
491
+ - `fillColor`: `#000000` (black) for paths in `<vector>`, transparent for shapes without `<solid>`
492
+ - `strokeWidth`: `0` (no stroke)
493
+ - `alpha`: `1.0`
494
+ - `fillType`: `nonZero`
495
+
496
+ ---
497
+
498
+ ## Unconvertible Types
499
+
500
+ These XML drawable types cannot be meaningfully converted to SVG and should be logged as unmappable in the conversion report:
501
+
502
+ | Type | Root Element | Reason |
503
+ |---|---|---|
504
+ | Ripple | `<ripple>` | Runtime touch feedback animation |
505
+ | Animated Vector | `<animated-vector>` | Requires animation timing/interpolation |
506
+ | Animated Selector | `<animated-selector>` | State transitions with animations |
507
+ | Transition Drawable | `<transition>` | Cross-fade between two drawables |
508
+ | Inset Drawable | `<inset>` | Simple wrapper; if inner drawable is convertible, convert that instead |
509
+ | Scale Drawable | `<scale>` | Runtime scaling behavior |
510
+ | Rotate Drawable | `<rotate>` | Runtime rotation; could be converted as static rotation if angle is fixed |
511
+ | Level List | `<level-list>` | Runtime level-based selection |
512
+
513
+ For `<inset>` and `<rotate>` with fixed values, attempt conversion by applying the inset/rotation statically and convert the inner drawable. Log in the report either way.
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: appgraph-rule-audit
3
+ description: Review fresh AppGraph and SPEC results after Android analysis, identify evidence-backed opportunities to improve page, ownership, and navigation rules, and write a local rule-opportunity report. Use after `droid2hmos analyze android` and before initializing the migration report; do not use it to edit analyzer output or implement rules.
4
+ ---
5
+
6
+ # Audit AppGraph rule opportunities
7
+
8
+ Review the latest deterministic Android analysis and produce maintainable rule-improvement candidates. This is a read-only diagnostic step. Do not edit AppGraph, SPEC facts, Android source, rule packs, or analyzers.
9
+
10
+ All `.migration/...` paths below live in the sibling migration workspace `<android-project>-migration/.migration/`. Fall back to the legacy `<android-project>/.migration/` only when the sibling workspace does not exist.
11
+
12
+ ## Preconditions
13
+
14
+ 1. Run `droid2hmos analyze android <android-project>` successfully before this Skill.
15
+ 2. Locate `.migration/spec/index.json`, `.migration/spec/facts/`, and `.migration/appgraph/app-graph.json`.
16
+ 3. Check that the SPEC `generation.sourceRevision`, when present, matches the Android Git HEAD. Treat a mismatch or missing required artifact as stale analysis: write no recommendations and ask for a new analysis run.
17
+ 4. Keep all source and generated data local. Network access is unnecessary.
18
+
19
+ ## Review workflow
20
+
21
+ 1. Read only the indexes and summaries first. Record page counts by type, navigation edges, ownership/host edges, unresolved counts, reachability gaps, coverage warnings, rule-pack versions, and rule contribution/drop/conflict counts.
22
+ 2. Group gaps by stable evidence shape rather than business page name:
23
+ - unresolved reason;
24
+ - framework/package/base type/annotation;
25
+ - receiver and method call shape;
26
+ - route declaration or factory pattern;
27
+ - missing host, target, caller, projection, or uniqueness evidence.
28
+ 3. Prioritize repeated or high-impact clusters. Inspect at most five representative samples per cluster initially. Read additional source only when it changes the diagnosis.
29
+ 4. Classify each candidate using [references/audit-contract.md](references/audit-contract.md). Determine whether evidence is missing in Analyzer, rejected by Judge, lost during Lift, omitted from SPEC, missing a host, or indicates a false positive.
30
+ 5. Establish a positive example and a similar negative or ambiguous example before proposing a generic or framework rule. If the target is dynamic, reflective, or non-unique, record it as an accepted limitation instead of proposing an unsafe rule.
31
+ 6. Compare with a previous report for the same source project when available. Treat newly added pages/edges, disappearing pages/edges, changed direction, and changed ownership as accuracy signals; raw count growth is not automatically an improvement.
32
+ 7. Rank opportunities by impact, recurrence, evidence strength, generality, false-positive risk, and implementation cost. Keep at most 20 active opportunities.
33
+ 8. Write the two outputs defined below. Follow [references/recommendation-schema.md](references/recommendation-schema.md) exactly.
34
+
35
+ ## Outputs
36
+
37
+ Create or replace:
38
+
39
+ ```text
40
+ .migration/rule-audit/rule-opportunities.json
41
+ .migration/rule-audit/rule-opportunities.md
42
+ ```
43
+
44
+ The JSON must conform to `schemas/rule-opportunities.schema.json` in this Skill. The Markdown is the human review view of the same findings; it must not add claims absent from JSON.
45
+
46
+ Each opportunity must include evidence refs with Android source file and line, the affected AppGraph/SPEC entities, current and expected behavior, proposed change layer, scope, confidence, generality, risk, and a concrete test plan. Separate observations from inferred root causes and label uncertain causes as hypotheses.
47
+
48
+ When no evidence-backed rule improvement exists, write valid outputs with an empty `opportunities` array and explain the reviewed limitations. Do not invent work to avoid an empty report.
49
+
50
+ ## Boundaries
51
+
52
+ - Do not modify immutable `.migration/spec/facts/` or `app-graph.json`.
53
+ - Do not write a project screen name into a generic rule proposal.
54
+ - Do not call a suffix match, route-string similarity, or nearest-name guess sufficient evidence.
55
+ - Do not turn host relationships into navigation edges.
56
+ - Do not mark multiple targets as resolved.
57
+ - Do not implement recommendations. A maintainer may later pass an accepted opportunity to the separate `appgraph-navigation-rules` Skill.
58
+ - Audit failure is advisory and must not block HarmonyOS migration. Record the limitation and allow the orchestrator to continue to `report init`.
@@ -0,0 +1,47 @@
1
+ # Rule audit contract
2
+
3
+ Use this reference to classify a finding and decide whether it is actionable.
4
+
5
+ ## Layers
6
+
7
+ ### analyzer-gap
8
+
9
+ The Android source contains stable syntax or type evidence, but `ruleEngine.contributions` has no corresponding facts. A proposal must name the API identity and facts an Analyzer should emit. JSON5 Judge conditions cannot repair this gap.
10
+
11
+ ### judge-gap
12
+
13
+ Required facts exist, but no rule confirms them or the contribution is dropped. Cite the existing fact paths, matching/drop result, and the smallest condition change. Preserve ambiguity.
14
+
15
+ ### lift-gap
16
+
17
+ The contribution is confirmed, but the expected Screen/host/navigation node or edge is absent from AppGraph. Cite the matched rule and expected graph projection.
18
+
19
+ ### spec-gap
20
+
21
+ The AppGraph node or edge exists with valid provenance, but SPEC facts omit or misclassify it. Cite both the AppGraph entity and missing SPEC representation.
22
+
23
+ ### host-gap
24
+
25
+ A page is recognized but lacks a deterministic owner or upstream host. Propose a rule only when the host chain follows a stable framework API, factory, entry point, or unique call relationship. Never infer a host from proximity or a single Activity fallback.
26
+
27
+ ### false-positive
28
+
29
+ A recognized page, host, or transition is a component, preview, test fixture, factory, helper, or unrelated call. Cite the matched rule and a structural discriminator that excludes the bad result without naming the product entity.
30
+
31
+ ### performance
32
+
33
+ A rule or analyzer has observed pathological cost. Cite timings, affected stage, input shape, and a bounded alternative. Do not infer performance problems from project size alone.
34
+
35
+ ## Scope
36
+
37
+ - `builtin-generic`: supported Android or Jetpack API with evidence across unrelated projects or authoritative framework structure.
38
+ - `framework-pack`: stable third-party or private framework identity such as package, base type, annotation, receiver type, or route registry.
39
+ - `project-pack`: valid behavior tied to one project's local abstraction without enough evidence for broader scope.
40
+
41
+ ## Evidence minimum
42
+
43
+ Every opportunity needs one positive source example and its current generated result. A `builtin-generic` or `framework-pack` opportunity also needs a negative, boundary, or ambiguous example demonstrating how false matches will be prevented. Evidence includes project-relative file, line when available, symbol/call shape, related entity refs, and related rule IDs.
44
+
45
+ ## Accepted limitations
46
+
47
+ Use an accepted limitation for reflection, runtime-generated destinations, remote configuration, unresolved dependency types, multiple valid targets, or missing source. Explain what evidence would be required to resolve it. Do not propose a low-confidence guessing rule.
@@ -0,0 +1,41 @@
1
+ # Rule opportunity output
2
+
3
+ Write JSON matching `schemas/rule-opportunities.schema.json` in this Skill and a concise Chinese Markdown rendering.
4
+
5
+ ## Top-level fields
6
+
7
+ - `schemaVersion`: `1.0.0`.
8
+ - `skillVersion`: `1.0.0`.
9
+ - `source`: Android project identity, source revision, `factsSha256`, and observed AppGraph/rule-pack versions.
10
+ - `summary`: reviewed counts and a short conclusion.
11
+ - `opportunities`: at most 20 ranked candidates.
12
+ - `acceptedLimitations`: evidence-backed gaps that should remain unresolved.
13
+ - `auditWarnings`: stale, incomplete, or unavailable inputs that limit confidence.
14
+
15
+ ## Opportunity fields
16
+
17
+ - `id`: stable lowercase ID derived from category, framework identity, and evidence shape; never use list position.
18
+ - `priority`: `P0`, `P1`, or `P2`. Use P0 only for systemic false results or a blocker affecting a large part of the page/navigation graph.
19
+ - `category`: one audit layer from the contract.
20
+ - `scope`: `builtin-generic`, `framework-pack`, or `project-pack`.
21
+ - `title`: concise problem statement.
22
+ - `frameworkIdentity`: stable package/type/annotation/receiver/call identity.
23
+ - `observation`: directly observed current result.
24
+ - `rootCause`: diagnosis plus `certainty` of `confirmed` or `hypothesis`.
25
+ - `expectedBehavior`: required page, host, edge, projection, exclusion, or performance behavior.
26
+ - `affectedEntities`: page/edge/contribution refs.
27
+ - `evidence`: source locations, generated refs, rule IDs, and the evidence role (`positive`, `negative`, `boundary`, `ambiguous`).
28
+ - `proposal`: target layer, proposed rule shape, and files or rule IDs likely to change; no executable patch.
29
+ - `confidence`, `generality`, `risk`: `high`, `medium`, or `low`, each with a reason.
30
+ - `testPlan`: positive, negative, boundary, ambiguity, Lift/SPEC, and project baseline checks as applicable.
31
+ - `expectedMetricDelta`: directional expectation for pages, navigation, hosts, unresolved, and false positives; use `unknown` when unsupported.
32
+ - `status`: always `proposed` in this Skill.
33
+
34
+ ## Markdown order
35
+
36
+ 1. Input freshness and coverage summary.
37
+ 2. Ranked opportunities with evidence and tests.
38
+ 3. Accepted limitations.
39
+ 4. Audit warnings.
40
+
41
+ Do not reproduce the full page inventory or migration report.