android2harmony 0.1.7 → 0.1.8

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 (226) hide show
  1. package/agents/api-analyzer.md +113 -0
  2. package/agents/app-action.md +122 -0
  3. package/agents/code-reviewer.md +191 -295
  4. package/agents/coder.md +377 -0
  5. package/agents/self-tester.md +8 -8
  6. package/agents/spec-designer.md +56 -0
  7. package/package.json +1 -1
  8. package/skills/a2h-app-req-gen/SKILL.md +174 -0
  9. package/skills/a2h-app-req-gen/references/spec-handoff.md +99 -0
  10. package/skills/a2h-app-req-gen/references/subagent-render.md +115 -0
  11. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/build.gradle.kts +41 -0
  12. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle/wrapper/gradle-wrapper.jar +0 -0
  13. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle/wrapper/gradle-wrapper.properties +8 -0
  14. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle.properties +3 -0
  15. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradlew +251 -0
  16. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradlew.bat +94 -0
  17. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/settings.gradle.kts +17 -0
  18. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawAnalysisDetector.java +1911 -0
  19. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawIssueRegistry.java +37 -0
  20. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawJson.java +146 -0
  21. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawLocation.java +96 -0
  22. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawRecordWriter.java +227 -0
  23. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry +1 -0
  24. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/resources/dev/reqgen/analyzer/jvm/raw-record-v1.schema.json +93 -0
  25. package/skills/a2h-app-req-gen/scripts/reqgen/assets/dashboard.html +151 -0
  26. package/skills/a2h-app-req-gen/scripts/reqgen/gradle/reqgen-lint.init.gradle +60 -0
  27. package/skills/a2h-app-req-gen/scripts/reqgen/gradle/reqgen-model.init.gradle +204 -0
  28. package/skills/a2h-app-req-gen/scripts/reqgen/package.json +14 -0
  29. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/backend.js +323 -0
  30. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/cli.js +811 -0
  31. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/gap-audit-link.js +62 -0
  32. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/index.js +66 -0
  33. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/merge.js +605 -0
  34. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/name-trace.js +153 -0
  35. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/orchestrate.js +1324 -0
  36. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/output-parse.js +50 -0
  37. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/overview.js +10 -0
  38. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/pool.js +23 -0
  39. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/post-merge-fix.js +146 -0
  40. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompt-loader.js +51 -0
  41. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompt.js +967 -0
  42. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompts.md +301 -0
  43. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/serve.js +78 -0
  44. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/short-id.js +46 -0
  45. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/snapshot.js +267 -0
  46. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/types.js +1 -0
  47. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/validate.js +122 -0
  48. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/callback-call-sites.js +62 -0
  49. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/gap-audit/build.js +1291 -0
  50. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/gap-audit/index.js +1 -0
  51. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/area-ownership.js +392 -0
  52. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/build.js +1995 -0
  53. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/callback-flow.js +277 -0
  54. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/component-variants.js +725 -0
  55. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/index.js +15 -0
  56. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/index.js +12 -0
  57. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/policy.js +469 -0
  58. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/traceability.js +468 -0
  59. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/req-model/build.js +1066 -0
  60. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/req-model/index.js +1 -0
  61. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/android-interaction-registry.js +174 -0
  62. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/android.js +1062 -0
  63. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/area-titles.js +678 -0
  64. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/builder.js +350 -0
  65. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose-api-registry.js +395 -0
  66. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose-call-sites.js +2008 -0
  67. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose.js +9059 -0
  68. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/dynamic-repeated-call-sites.js +529 -0
  69. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/index.js +1 -0
  70. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/interaction-effects.js +1149 -0
  71. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/jvm.js +6272 -0
  72. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/normalize.js +33 -0
  73. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/pending-intents.js +565 -0
  74. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/resource-id.js +42 -0
  75. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/route-state-variants.js +781 -0
  76. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/state.js +234 -0
  77. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/types.js +1 -0
  78. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/extract.js +40 -0
  79. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/index.js +6 -0
  80. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/manifest.js +513 -0
  81. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/resources.js +398 -0
  82. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/types.js +1 -0
  83. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/views.js +447 -0
  84. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/xml-parser.js +175 -0
  85. package/skills/a2h-app-req-gen/scripts/reqgen/src/cli.js +398 -0
  86. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/area-graph.js +1 -0
  87. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/artifact.js +1 -0
  88. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/common.js +16 -0
  89. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/diagnostic.js +10 -0
  90. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/gap-audit.js +14 -0
  91. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/index.js +11 -0
  92. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/name-traceability.js +26 -0
  93. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/raw-fact.js +40 -0
  94. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/req-model.js +1 -0
  95. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/run.js +19 -0
  96. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/semantic-completeness.js +11 -0
  97. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/source-facts.js +1 -0
  98. package/skills/a2h-app-req-gen/scripts/reqgen/src/core/output-guard.js +41 -0
  99. package/skills/a2h-app-req-gen/scripts/reqgen/src/core/stable-id.js +92 -0
  100. package/skills/a2h-app-req-gen/scripts/reqgen/src/dashboard-cli.js +169 -0
  101. package/skills/a2h-app-req-gen/scripts/reqgen/src/diagnostics.js +104 -0
  102. package/skills/a2h-app-req-gen/scripts/reqgen/src/dump/dump.js +1028 -0
  103. package/skills/a2h-app-req-gen/scripts/reqgen/src/dump/index.js +1 -0
  104. package/skills/a2h-app-req-gen/scripts/reqgen/src/dump-cli.js +251 -0
  105. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/artifacts.js +49 -0
  106. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/index.js +18 -0
  107. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/quality-report.js +125 -0
  108. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/render.js +353 -0
  109. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/types.js +2 -0
  110. package/skills/a2h-app-req-gen/scripts/reqgen/src/graph/paths.js +474 -0
  111. package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/index.js +15 -0
  112. package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/protocol.js +408 -0
  113. package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/runner.js +452 -0
  114. package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/index.js +8 -0
  115. package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/output.js +165 -0
  116. package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/run-stage1.js +891 -0
  117. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/discovery.js +299 -0
  118. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/gradle-model.js +358 -0
  119. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/gradle-runner.js +165 -0
  120. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/resource-profile.js +60 -0
  121. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/types.js +1 -0
  122. package/skills/a2h-app-req-gen/scripts/reqgen/src/spec-handoff-cli.js +151 -0
  123. package/skills/a2h-app-req-gen/scripts/reqgen/src/spec-handoff.js +1004 -0
  124. package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-cli.js +109 -0
  125. package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-input.js +419 -0
  126. package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-name-trace-input.js +95 -0
  127. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/area-graph.js +444 -0
  128. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/index.js +7 -0
  129. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/req-model.js +580 -0
  130. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/req-text.js +195 -0
  131. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/semantic-area-graph.js +1431 -0
  132. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/types.js +1 -0
  133. package/skills/a2h-app-req-gen/scripts/reqgen-audit.mjs +298 -0
  134. package/skills/a2h-app-req-gen/scripts/reqgen-selfcheck.mjs +555 -0
  135. package/skills/a2h-code-review/SKILL.md +379 -0
  136. package/skills/{hmos-integration-test → a2h-integration-test}/README.md +6 -6
  137. package/skills/{hmos-integration-test → a2h-integration-test}/SKILL.md +6 -6
  138. package/skills/{hmos-integration-test → a2h-integration-test}/scripts/report-tool.mjs +1 -1
  139. package/skills/a2h-spec-design/SKILL.md +330 -0
  140. package/skills/a2h-spec-design/scripts/inspect_spec_design_inputs.mjs +207 -0
  141. package/skills/a2h-spec-design/scripts/move_spec_technical_reference.mjs +198 -0
  142. package/skills/a2h-spec-design/scripts/validate_spec_design_output.mjs +256 -0
  143. package/skills/a2h-spec-generate/SKILL.md +696 -0
  144. package/skills/a2h-spec-generate/references/trace-template.md +115 -0
  145. package/skills/a2h-spec-generate/scripts/parse_requirements.mjs +537 -0
  146. package/skills/a2h-spec-generate/template/REQ.txt +22 -0
  147. package/skills/a2h-spec-generate/template/REQ.xlsx +0 -0
  148. package/skills/a2h-spec-implement/SKILL.md +548 -0
  149. package/skills/a2h-spec-implement/references/harmony-pitfalls.md +186 -0
  150. package/skills/a2h-spec-implement/references/l1-unit-test.md +185 -0
  151. package/skills/a2h-spec-implement/references/l3-e2e-uitest.md +167 -0
  152. package/skills/a2h-spec-implement/references/test-core.md +132 -0
  153. package/skills/a2h-spec-implement/rules/arkts/arkts-standards.md +184 -0
  154. package/skills/a2h-spec-implement/rules/arkts/conventions/coding-style.md +96 -0
  155. package/skills/a2h-spec-implement/rules/arkts/conventions/security.md +126 -0
  156. package/skills/a2h-spec-implement/rules/arkts/language/arkts-rules.md +650 -0
  157. package/skills/a2h-spec-implement/rules/arkts/language/arkui-structure-rules.md +173 -0
  158. package/skills/a2h-spec-implement/rules/arkts/language/ts-to-arkts-rewrites.md +58 -0
  159. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md +648 -0
  160. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md +2089 -0
  161. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md +1033 -0
  162. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md +1183 -0
  163. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md +576 -0
  164. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md +297 -0
  165. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md +395 -0
  166. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md +903 -0
  167. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -0
  168. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md +1178 -0
  169. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md +911 -0
  170. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +355 -0
  171. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md +11 -0
  172. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/!!/350/257/255/346/263/225/357/274/232/345/217/214/345/220/221/347/273/221/345/256/232.md +216 -0
  173. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Computed/350/243/205/351/245/260/345/231/250/357/274/232/350/256/241/347/256/227/345/261/236/346/200/247.md +442 -0
  174. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Event/350/243/205/351/245/260/345/231/250/357/274/232/350/247/204/350/214/203/347/273/204/344/273/266/350/276/223/345/207/272.md +169 -0
  175. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Local/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/351/203/250/347/212/266/346/200/201.md +763 -0
  176. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Monitor/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/344/277/256/346/224/271/345/274/202/346/255/245/347/233/221/345/220/254.md +2088 -0
  177. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@ObservedV2/350/243/205/351/245/260/345/231/250/345/222/214@Trace/350/243/205/351/245/260/345/231/250/357/274/232/347/261/273/345/261/236/346/200/247/345/217/230/345/214/226/350/247/202/346/265/213.md +1258 -0
  178. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Once/357/274/232/345/210/235/345/247/213/345/214/226/345/220/214/346/255/245/344/270/200/346/254/241.md +175 -0
  179. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Param/357/274/232/347/273/204/344/273/266/345/244/226/351/203/250/350/276/223/345/205/245.md +850 -0
  180. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Provider/350/243/205/351/245/260/345/231/250/345/222/214@Consumer/350/243/205/351/245/260/345/231/250/357/274/232/350/267/250/347/273/204/344/273/266/345/261/202/347/272/247/345/217/214/345/220/221/345/220/214/346/255/245.md +862 -0
  181. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Type/350/243/205/351/245/260/345/231/250/357/274/232/346/240/207/350/256/260/347/261/273/345/261/236/346/200/247/347/232/204/347/261/273/345/236/213.md +110 -0
  182. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/AppStorageV2/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200UI/347/212/266/346/200/201/345/255/230/345/202/250.md +301 -0
  183. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/MVVM/346/250/241/345/274/217/357/274/210V2/357/274/211.md +1411 -0
  184. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/PersistenceV2/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +1392 -0
  185. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/getTarget/346/216/245/345/217/243/357/274/232/350/216/267/345/217/226/347/212/266/346/200/201/347/256/241/347/220/206/346/241/206/346/236/266/344/273/243/347/220/206/345/211/215/347/232/204/345/216/237/345/247/213/345/257/271/350/261/241.md +288 -0
  186. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/makeObserved/346/216/245/345/217/243/357/274/232/345/260/206/351/235/236/350/247/202/345/257/237/346/225/260/346/215/256/345/217/230/344/270/272/345/217/257/350/247/202/345/257/237/346/225/260/346/215/256.md +768 -0
  187. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206V1/345/222/214V2/346/267/267/347/224/250/346/214/207/345/257/274/357/274/210API version 19/345/217/212/344/271/213/345/220/216/357/274/211.md" +829 -0
  188. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206/346/246/202/350/277/260.md +184 -0
  189. package/skills/a2h-spec-implement/rules/arkts/ui/component-cookbook.md +431 -0
  190. package/skills/a2h-spec-implement/rules/arkts/ui/state-management.md +152 -0
  191. package/skills/a2h-spec-implement/rules/arkts/ui/ui-quality.md +67 -0
  192. package/skills/a2h-spec-implement/rules/arkts/ui/ui-runtime-diagnosis.md +32 -0
  193. package/skills/a2h-spec-implement/rules/pipeline/build/package-set-collection.md +43 -0
  194. package/skills/a2h-spec-implement/scripts/_lib/common.mjs +97 -0
  195. package/skills/a2h-spec-implement/scripts/_lib/config.mjs +70 -0
  196. package/skills/a2h-spec-implement/scripts/_lib/lessons_cli.mjs +48 -0
  197. package/skills/a2h-spec-implement/scripts/build_lessons.mjs +24 -0
  198. package/skills/a2h-spec-implement/scripts/code_pattern_lessons.mjs +26 -0
  199. package/skills/a2h-spec-implement/scripts/device_ui.mjs +433 -0
  200. package/skills/a2h-spec-implement/scripts/device_ui_core.mjs +895 -0
  201. package/skills/a2h-spec-implement/scripts/ledger/aggregate_run.mjs +207 -0
  202. package/skills/a2h-spec-implement/scripts/manifest/emit_summary_line.mjs +37 -0
  203. package/skills/a2h-spec-implement/scripts/manifest/finalize_run.mjs +115 -0
  204. package/skills/a2h-spec-implement/scripts/manifest/render_final_summary.mjs +78 -0
  205. package/skills/a2h-spec-implement/scripts/manifest/root_mirror.mjs +137 -0
  206. package/skills/a2h-spec-implement/scripts/package.json +12 -0
  207. package/skills/a2h-spec-implement/scripts/state_propagation_check.mjs +676 -0
  208. package/skills/a2h-spec-implement/scripts/test/l2_run.mjs +244 -0
  209. package/skills/a2h-spec-implement/scripts/test/tp_coverage.mjs +231 -0
  210. package/skills/a2h-spec-implement/scripts/ui_memory.mjs +161 -0
  211. package/skills/a2h-spec-implement/scripts/utils/ensure_app.mjs +134 -0
  212. package/skills/a2h-ui-transfer/SKILL.md +2 -2
  213. package/skills/hmos-convert-pipeline/SKILL.md +7 -5
  214. package/skills/hmos-incremental-ui-align/README.md +7 -7
  215. package/skills/hmos-incremental-ui-align/SKILL.md +1 -1
  216. package/skills/hmos-incremental-ui-align/page_align.md +1 -1
  217. package/skills/hmos-spec-generate/SKILL.md +2 -2
  218. package/skills/hmos-spec-generate/scripts/parse_requirements.mjs +537 -0
  219. package/skills/hmos-fix-build-errors/SKILL.md +0 -266
  220. package/skills/hmos-fix-build-errors/references/arkts-strict-patterns.md +0 -219
  221. package/skills/hmos-fix-build-errors/references/known-patterns.md +0 -157
  222. package/skills/hmos-fix-build-errors/references/rdb-entity-pattern.md +0 -131
  223. package/skills/hmos-spec-generate/scripts/parse_requirements.ts +0 -515
  224. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/resolve-metadata-tool.mjs +0 -0
  225. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/self-test-runner.mjs +0 -0
  226. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/testcases-tool.mjs +0 -0
@@ -0,0 +1,1062 @@
1
+ function resolvedClassName(name, packageName) {
2
+ if (!packageName || (name.includes(".") && !name.startsWith(".")))
3
+ return name;
4
+ if (name.startsWith(".")) return `${packageName}${name}`;
5
+ return `${packageName}.${name}`;
6
+ }
7
+ function manifestComponentType(component) {
8
+ switch (component.kind) {
9
+ case "fragment":
10
+ return "fragment";
11
+ case "app-widget":
12
+ return "widget";
13
+ case "receiver":
14
+ return "receiver";
15
+ case "activity":
16
+ case "activity-alias":
17
+ return "activity";
18
+ }
19
+ }
20
+ function simpleTag(tag) {
21
+ return tag.includes(".") ? tag.slice(tag.lastIndexOf(".") + 1) : tag;
22
+ }
23
+ function elementType(view) {
24
+ const name = simpleTag(view.tag).toLowerCase();
25
+ if (name.endsWith("edittextpreference")) return "text-input";
26
+ if (name.includes("switchpreference") || name.endsWith("checkboxpreference"))
27
+ return name.includes("switch") ? "switch" : "checkbox";
28
+ if (name.endsWith("listpreference") || name.endsWith("dropdownpreference"))
29
+ return "preference-list";
30
+ if (name.endsWith("preferencecategory")) return "preference-category";
31
+ if (name.endsWith("preferencescreen")) return "preference-screen";
32
+ if (name.endsWith("preference")) return "preference";
33
+ if (name === "edittext" || name.endsWith("textinputedittext")) {
34
+ return "text-input";
35
+ }
36
+ if (name === "imagebutton") return "icon-button";
37
+ if (name === "switch" || name === "switchcompat") return "switch";
38
+ if (name === "checkbox") return "checkbox";
39
+ if (name === "radiobutton") return "radio-button";
40
+ if (name === "togglebutton") return "toggle-button";
41
+ if (name === "textview" && view.actionable) return "clickable-text";
42
+ if (view.resourceType === "menu" && name === "item") return "menu-item";
43
+ return name.replace(/([a-z0-9])([A-Z])/gu, "$1-$2").toLowerCase();
44
+ }
45
+ function resolvedLabel(attribute) {
46
+ if (!attribute) return undefined;
47
+ if (attribute.resolved?.trim()) return attribute.resolved.trim();
48
+ return attribute.raw.startsWith("@") ? undefined : attribute.raw.trim();
49
+ }
50
+ function iconResource(reference) {
51
+ if (!reference) return undefined;
52
+ const owner = reference.packageName ? `${reference.packageName}.R` : "R";
53
+ return `${owner}.${reference.resourceType}.${reference.name}`;
54
+ }
55
+ function labelsByTarget(android) {
56
+ const result = new Map();
57
+ for (const label of android.views) {
58
+ if (!label.labelFor) continue;
59
+ const attribute = label.text ?? label.contentDescription;
60
+ const key = `${label.resourceType}/${label.resourceName}#${label.labelFor}`;
61
+ const candidates = result.get(key) ?? [];
62
+ candidates.push({
63
+ ...(resolvedLabel(attribute) ? { value: resolvedLabel(attribute) } : {}),
64
+ ...(attribute?.raw ? { raw: attribute.raw } : {}),
65
+ evidence: label.evidence,
66
+ });
67
+ result.set(key, candidates);
68
+ }
69
+ return result;
70
+ }
71
+ function associatedLabelFor(view, labels) {
72
+ const explicit = view.id
73
+ ? (labels.get(`${view.resourceType}/${view.resourceName}#${view.id}`) ?? [])
74
+ : [];
75
+ const values = [
76
+ ...new Set(
77
+ explicit
78
+ .map((candidate) => candidate.value)
79
+ .filter((value) => value !== undefined),
80
+ ),
81
+ ];
82
+ if (values.length === 1) {
83
+ return {
84
+ value: values[0],
85
+ candidates: explicit,
86
+ source: "label-for",
87
+ labelForUnresolved: false,
88
+ };
89
+ }
90
+ const hint = resolvedLabel(view.hint);
91
+ if (explicit.length > 0) {
92
+ return {
93
+ ...(hint ? { value: hint } : {}),
94
+ candidates: explicit,
95
+ source: hint ? "hint" : "none",
96
+ labelForUnresolved: true,
97
+ };
98
+ }
99
+ return {
100
+ ...(hint ? { value: hint } : {}),
101
+ candidates: [],
102
+ source: hint ? "hint" : "none",
103
+ labelForUnresolved: false,
104
+ };
105
+ }
106
+ function ownerFactData(builder, ownerRef) {
107
+ const fact = builder.fact(ownerRef);
108
+ if (!fact || fact.kind !== "component") {
109
+ throw new Error(`Missing component owner ${ownerRef}`);
110
+ }
111
+ return fact.data;
112
+ }
113
+ function propagateIncludedLayoutOwners(android, state) {
114
+ let changed = true;
115
+ while (changed) {
116
+ changed = false;
117
+ for (const include of android.includes) {
118
+ const parentOwners = state.layoutOwners.get(include.resourceName);
119
+ if (!parentOwners) continue;
120
+ const childOwners =
121
+ state.layoutOwners.get(include.includedLayout) ?? new Set();
122
+ const before = childOwners.size;
123
+ parentOwners.forEach((owner) => childOwners.add(owner));
124
+ if (childOwners.size !== before) {
125
+ state.layoutOwners.set(include.includedLayout, childOwners);
126
+ changed = true;
127
+ }
128
+ }
129
+ }
130
+ }
131
+ function addManifestFacts(android, state) {
132
+ for (const component of android.manifest.components) {
133
+ if (component.kind === "activity-alias" && component.targetActivity) {
134
+ const targetRef = state.ensureComponent(
135
+ component.targetActivity,
136
+ "activity",
137
+ component.evidence,
138
+ "activity-alias target",
139
+ "high",
140
+ {
141
+ uiTechnology: "views",
142
+ ...(component.provenance
143
+ ? { manifestProvenance: component.provenance }
144
+ : {}),
145
+ },
146
+ );
147
+ // Retain the technical alias declaration without turning it into a
148
+ // user-visible Area. Entry filters declared on the alias resolve to the
149
+ // actual Activity target.
150
+ state.ensureComponent(
151
+ component.name,
152
+ "other",
153
+ component.evidence,
154
+ "activity-alias declaration",
155
+ "high",
156
+ {
157
+ exported: component.exported,
158
+ ...(component.provenance
159
+ ? { manifestProvenance: component.provenance }
160
+ : {}),
161
+ },
162
+ );
163
+ state.activityAliasTargetByQualifiedName.set(component.name, targetRef);
164
+ continue;
165
+ }
166
+ const widgetLayout =
167
+ component.kind === "app-widget"
168
+ ? android.appWidgetLayouts?.find(
169
+ (item) => item.componentName === component.name,
170
+ )
171
+ : undefined;
172
+ const componentRef = state.ensureComponent(
173
+ component.name,
174
+ manifestComponentType(component),
175
+ component.evidence,
176
+ "manifest component declaration",
177
+ "high",
178
+ {
179
+ exported: component.exported,
180
+ uiTechnology:
181
+ component.kind === "app-widget" ? "remote-views" : "views",
182
+ ...(widgetLayout
183
+ ? { layoutResource: widgetLayout.layoutResourceName }
184
+ : {}),
185
+ ...(component.provenance
186
+ ? { manifestProvenance: component.provenance }
187
+ : {}),
188
+ },
189
+ );
190
+ if (widgetLayout) {
191
+ state.addLayoutOwner(widgetLayout.layoutResourceName, componentRef);
192
+ }
193
+ }
194
+ for (const entry of android.manifest.entries) {
195
+ let componentRef =
196
+ state.activityAliasTargetByQualifiedName.get(entry.componentName) ??
197
+ state.componentByQualifiedName.get(entry.componentName);
198
+ if (!componentRef) {
199
+ componentRef = state.ensureComponent(
200
+ entry.componentName,
201
+ "activity",
202
+ entry.evidence,
203
+ "entry component declaration",
204
+ "high",
205
+ { uiTechnology: "views" },
206
+ );
207
+ }
208
+ const widgetInfo =
209
+ entry.kind === "widget"
210
+ ? android.appWidgetLayouts?.find(
211
+ (item) => item.componentName === entry.componentName,
212
+ )
213
+ : undefined;
214
+ const widgetConditionRef =
215
+ entry.kind === "widget"
216
+ ? state.builder.addFact("condition", {
217
+ identity: [
218
+ { kind: "component", value: componentRef },
219
+ { kind: "semantic-key", value: "app-widget-added" },
220
+ ],
221
+ confidence: "high",
222
+ support: [
223
+ {
224
+ location: entry.evidence,
225
+ role: "manifest declares an installable app-widget provider",
226
+ },
227
+ ...(widgetInfo
228
+ ? [
229
+ {
230
+ location: widgetInfo.evidence,
231
+ role: "app-widget provider declares a RemoteViews layout",
232
+ },
233
+ ]
234
+ : []),
235
+ ],
236
+ data: {
237
+ ownerKey: componentRef,
238
+ expressionAnchor: "app-widget-added",
239
+ plainLanguage: "应用小组件已添加到 Android 主屏幕",
240
+ valueKind: "runtime",
241
+ },
242
+ relatedFactRefs: [componentRef],
243
+ })
244
+ : undefined;
245
+ state.builder.addFact("entry-point", {
246
+ identity: [
247
+ { kind: "entry", value: entry.kind },
248
+ { kind: "component", value: entry.componentName },
249
+ ...(entry.uri ? [{ kind: "route", value: entry.uri }] : []),
250
+ ],
251
+ confidence: "high",
252
+ support: [
253
+ {
254
+ location: entry.evidence,
255
+ role: "manifest intent filter",
256
+ },
257
+ ],
258
+ data: {
259
+ entryKind: entry.kind,
260
+ category: entry.category,
261
+ componentKey: componentRef,
262
+ action: entry.action,
263
+ ...(entry.uri ? { uri: entry.uri } : {}),
264
+ conditionKeys: widgetConditionRef ? [widgetConditionRef] : [],
265
+ },
266
+ relatedFactRefs: [
267
+ componentRef,
268
+ ...(widgetConditionRef ? [widgetConditionRef] : []),
269
+ ],
270
+ });
271
+ }
272
+ }
273
+ function addResourceFacts(android, builder) {
274
+ for (const value of android.values) {
275
+ builder.addFact("resource-value", {
276
+ identity: [
277
+ {
278
+ kind: "resource",
279
+ value: `${value.resourceType}/${value.name}`,
280
+ },
281
+ { kind: "variant", value: value.configuration },
282
+ ],
283
+ confidence: "high",
284
+ support: [
285
+ {
286
+ location: value.evidence,
287
+ role: "selected resource value",
288
+ },
289
+ ],
290
+ data: {
291
+ resourceType: value.resourceType,
292
+ resourceName: value.name,
293
+ configuration: value.configuration,
294
+ value: value.value,
295
+ },
296
+ });
297
+ }
298
+ }
299
+ function addFragmentComponents(android, state) {
300
+ for (const fragment of android.fragments) {
301
+ const className = resolvedClassName(
302
+ fragment.className,
303
+ android.manifest.packageName,
304
+ );
305
+ const ref = state.ensureComponent(
306
+ className,
307
+ "fragment",
308
+ fragment.evidence,
309
+ "XML fragment declaration",
310
+ "high",
311
+ { uiTechnology: "views" },
312
+ );
313
+ if (fragment.id) state.fragmentByNavigationId.set(fragment.id, ref);
314
+ }
315
+ }
316
+ /**
317
+ * Connects fragments declared by a selected layout to that layout's resolved
318
+ * Activity/Fragment owner. A NavHost is implementation infrastructure: its
319
+ * navigation graph is hosted by the layout owner, while a plain static
320
+ * <fragment> is the owner's fixed initial content.
321
+ *
322
+ * This runs after JVM structure preparation because setContentView/ViewBinding
323
+ * calls are what establish most layout owners.
324
+ */
325
+ function addXmlFragmentBindings(android, state) {
326
+ for (const fragment of android.fragments) {
327
+ const owners = [
328
+ ...(state.layoutOwners.get(fragment.resourceName) ?? []),
329
+ ].sort();
330
+ if (owners.length === 0) continue;
331
+ if (fragment.navigationGraph) {
332
+ const hosts =
333
+ state.navigationHostsByGraph.get(fragment.navigationGraph) ?? new Set();
334
+ owners.forEach((ownerRef) => hosts.add(ownerRef));
335
+ state.navigationHostsByGraph.set(fragment.navigationGraph, hosts);
336
+ continue;
337
+ }
338
+ const className = resolvedClassName(
339
+ fragment.className,
340
+ android.manifest.packageName,
341
+ );
342
+ const fragmentRef = state.componentByQualifiedName.get(className);
343
+ if (!fragmentRef) continue;
344
+ for (const ownerRef of owners) {
345
+ state.builder.addFact("container-binding", {
346
+ identity: [
347
+ { kind: "component", value: ownerRef },
348
+ {
349
+ kind: "semantic-key",
350
+ value: `fragment:${fragment.id ?? className}`,
351
+ },
352
+ ],
353
+ confidence: "high",
354
+ support: [
355
+ {
356
+ location: fragment.evidence,
357
+ role: "static XML fragment content",
358
+ },
359
+ ],
360
+ data: {
361
+ bindingKind: "fragment",
362
+ hostKey: ownerRef,
363
+ contentKeys: [fragmentRef],
364
+ initialContentKey: fragmentRef,
365
+ },
366
+ relatedFactRefs: [ownerRef, fragmentRef],
367
+ });
368
+ }
369
+ }
370
+ }
371
+ function identityForView(ownerRef, view, inferredResourceId) {
372
+ const semanticControl =
373
+ view.id ??
374
+ inferredResourceId ??
375
+ [
376
+ simpleTag(view.tag),
377
+ elementType(view) === "text-input"
378
+ ? ""
379
+ : (resolvedLabel(view.text) ?? ""),
380
+ resolvedLabel(view.hint) ?? "",
381
+ resolvedLabel(view.contentDescription) ?? "",
382
+ iconResource(view.iconResources?.[0]) ?? "",
383
+ view.onClick ?? "",
384
+ ].join("|");
385
+ return [
386
+ { kind: "component", value: ownerRef },
387
+ {
388
+ kind: "resource",
389
+ value: `${view.resourceType}/${view.resourceName}`,
390
+ },
391
+ { kind: "control", value: semanticControl },
392
+ ];
393
+ }
394
+ function addAttributeUnresolved(
395
+ builder,
396
+ factRef,
397
+ property,
398
+ attribute,
399
+ evidenceRef,
400
+ ) {
401
+ if (
402
+ !attribute?.raw.startsWith("@") ||
403
+ attribute.resolved !== undefined ||
404
+ attribute.resolvedValue !== undefined
405
+ ) {
406
+ return;
407
+ }
408
+ builder.addUnresolved({
409
+ ownerRef: factRef,
410
+ fieldPath: `data.${property}`,
411
+ reasonCode: "INTRINSIC_RESOURCE_VALUE_UNKNOWN",
412
+ message: `The selected resources do not resolve ${attribute.raw}.`,
413
+ evidenceRefs: [evidenceRef],
414
+ candidates: [],
415
+ impact: ["control-description"],
416
+ originRef: attribute.raw,
417
+ });
418
+ }
419
+ function resolvedAttributeValue(attribute) {
420
+ if (!attribute) return undefined;
421
+ if (attribute.resolvedValue !== undefined) {
422
+ return attribute.resolvedValue;
423
+ }
424
+ if (attribute.resolved !== undefined) return attribute.resolved;
425
+ return attribute.raw.startsWith("@") ? undefined : attribute.raw;
426
+ }
427
+ function booleanAttributeValue(attribute) {
428
+ const value = resolvedAttributeValue(attribute);
429
+ if (typeof value === "boolean") return value;
430
+ if (typeof value !== "string") return undefined;
431
+ const normalized = value.trim().toLowerCase();
432
+ if (normalized === "true") return true;
433
+ if (normalized === "false") return false;
434
+ return undefined;
435
+ }
436
+ function textDefaultValue(attribute) {
437
+ const value = resolvedAttributeValue(attribute);
438
+ return typeof value === "string" ? value : undefined;
439
+ }
440
+ function preferenceDefaultValue(attribute) {
441
+ const value = resolvedAttributeValue(attribute);
442
+ if (typeof value !== "string") return value;
443
+ const normalized = value.trim().toLowerCase();
444
+ if (normalized === "true") return true;
445
+ if (normalized === "false") return false;
446
+ return value;
447
+ }
448
+ function optionsAttributeValue(attribute) {
449
+ const value = resolvedAttributeValue(attribute);
450
+ return Array.isArray(value) ? value : undefined;
451
+ }
452
+ function addXmlUiProperty(state, factRef, view, property, value) {
453
+ state.builder.addFact("ui-property", {
454
+ identity: [
455
+ { kind: "control", value: factRef },
456
+ { kind: "semantic-key", value: property },
457
+ ],
458
+ confidence: "high",
459
+ support: [
460
+ {
461
+ location: view.evidence,
462
+ role: "selected XML control property",
463
+ },
464
+ ],
465
+ data: {
466
+ ownerKey: factRef,
467
+ property,
468
+ value,
469
+ },
470
+ relatedFactRefs: [factRef],
471
+ });
472
+ }
473
+ function addXmlPropertyUnresolved(
474
+ state,
475
+ factRef,
476
+ property,
477
+ attribute,
478
+ evidenceRef,
479
+ ) {
480
+ if (!attribute) return;
481
+ state.builder.addUnresolved({
482
+ ownerRef: factRef,
483
+ fieldPath: `data.${property}`,
484
+ reasonCode: attribute.raw.startsWith("@")
485
+ ? "INTRINSIC_RESOURCE_VALUE_UNKNOWN"
486
+ : "INTRINSIC_XML_PROPERTY_VALUE_UNKNOWN",
487
+ message:
488
+ `The selected XML value ${attribute.raw || "(empty)"} cannot be ` +
489
+ `normalized as control property ${property}.`,
490
+ evidenceRefs: [evidenceRef],
491
+ candidates: [],
492
+ impact: ["control-description"],
493
+ originRef: attribute.raw,
494
+ });
495
+ }
496
+ const booleanOptionElementTypes = new Set([
497
+ "checkbox",
498
+ "radio-button",
499
+ "switch",
500
+ "toggle-button",
501
+ ]);
502
+ function addXmlHandler(state, ownerRef, viewFactRef, view) {
503
+ if (!view.onClick) return;
504
+ const owner = ownerFactData(state.builder, ownerRef);
505
+ const callableSymbol = `${owner.qualifiedName}#${view.onClick}`;
506
+ const callbackRef = state.builder.addFact("callback", {
507
+ identity: [
508
+ { kind: "component", value: ownerRef },
509
+ { kind: "callback", value: callableSymbol },
510
+ ],
511
+ confidence: "high",
512
+ support: [
513
+ {
514
+ location: view.evidence,
515
+ role: "android:onClick handler",
516
+ },
517
+ ],
518
+ data: {
519
+ ownerKey: ownerRef,
520
+ callableSymbol,
521
+ callbackKind: "xml-handler",
522
+ },
523
+ relatedFactRefs: [ownerRef],
524
+ });
525
+ state.registerCallback(callableSymbol, callbackRef);
526
+ state.builder.addFact("interaction-binding", {
527
+ identity: [
528
+ { kind: "control", value: viewFactRef },
529
+ { kind: "callback", value: callbackRef },
530
+ { kind: "semantic-key", value: "click" },
531
+ ],
532
+ confidence: "high",
533
+ support: [
534
+ {
535
+ location: view.evidence,
536
+ role: "android:onClick binding",
537
+ },
538
+ ],
539
+ data: {
540
+ elementKey: viewFactRef,
541
+ callbackKey: callbackRef,
542
+ trigger: "click",
543
+ bindingApi: "android:onClick",
544
+ },
545
+ relatedFactRefs: [viewFactRef, callbackRef],
546
+ });
547
+ }
548
+ function addViews(android, state) {
549
+ propagateIncludedLayoutOwners(android, state);
550
+ const associatedLabels = labelsByTarget(android);
551
+ const contentByLayout = new Map();
552
+ const semanticOccurrences = new Map();
553
+ for (const view of android.views) {
554
+ let owners = [...(state.layoutOwners.get(view.resourceName) ?? [])];
555
+ let surrogate = false;
556
+ if (owners.length === 0) {
557
+ owners = [state.ensureLayoutSurrogate(view.resourceName, view.evidence)];
558
+ surrogate = true;
559
+ }
560
+ for (const ownerRef of owners.sort()) {
561
+ const inferredResourceId = view.isLayoutRoot
562
+ ? state.repeatedLayoutResourceId(view.resourceName, ownerRef)
563
+ : undefined;
564
+ const effectiveResourceId = view.id ?? inferredResourceId;
565
+ const identity = identityForView(ownerRef, view, inferredResourceId);
566
+ const collisionKey = JSON.stringify(identity);
567
+ semanticOccurrences.set(
568
+ collisionKey,
569
+ (semanticOccurrences.get(collisionKey) ?? 0) + 1,
570
+ );
571
+ const normalizedElementType = elementType(view);
572
+ // android:text is the initial value of an input, not its product label.
573
+ const text =
574
+ normalizedElementType === "text-input"
575
+ ? undefined
576
+ : resolvedLabel(view.text);
577
+ const description = resolvedLabel(view.contentDescription);
578
+ const label = associatedLabelFor(view, associatedLabels);
579
+ const icon = iconResource(view.iconResources?.[0]);
580
+ const support = [
581
+ {
582
+ location: view.evidence,
583
+ role: "selected XML view declaration",
584
+ },
585
+ ...label.candidates.map((candidate) => ({
586
+ location: candidate.evidence,
587
+ role: "android:labelFor naming evidence",
588
+ })),
589
+ ];
590
+ const onlyIconNamingEvidence =
591
+ !text && !description && !label.value && Boolean(icon);
592
+ const onlyIdentifierNamingEvidence =
593
+ !text &&
594
+ !description &&
595
+ !label.value &&
596
+ !icon &&
597
+ Boolean(effectiveResourceId);
598
+ const noNamingEvidence =
599
+ !text && !description && !label.value && !icon && !effectiveResourceId;
600
+ const factRef = state.builder.addFact("ui-element", {
601
+ identity,
602
+ confidence:
603
+ surrogate ||
604
+ onlyIconNamingEvidence ||
605
+ onlyIdentifierNamingEvidence ||
606
+ noNamingEvidence
607
+ ? "medium"
608
+ : "high",
609
+ support,
610
+ data: {
611
+ ownerKey: ownerRef,
612
+ elementType: inferredResourceId
613
+ ? "repeated-row"
614
+ : ownerFactData(state.builder, ownerRef).uiTechnology ===
615
+ "remote-views"
616
+ ? `remote-views-${normalizedElementType}`
617
+ : normalizedElementType,
618
+ actionable: view.actionable,
619
+ ...(effectiveResourceId ? { resourceId: effectiveResourceId } : {}),
620
+ ...(text ? { visibleText: text } : {}),
621
+ ...(description ? { contentDescription: description } : {}),
622
+ ...(label.value ? { associatedLabel: label.value } : {}),
623
+ ...(icon ? { iconResource: icon } : {}),
624
+ ...(inferredResourceId
625
+ ? { dynamicIdentity: inferredResourceId }
626
+ : {}),
627
+ },
628
+ relatedFactRefs: [ownerRef],
629
+ });
630
+ state.uiOwnerById.set(factRef, ownerRef);
631
+ state.registerUi(view.id, factRef);
632
+ state.registerLayoutUi(
633
+ view.resourceName,
634
+ factRef,
635
+ view.isLayoutRoot === true,
636
+ );
637
+ const list = contentByLayout.get(view.resourceName) ?? [];
638
+ list.push(factRef);
639
+ contentByLayout.set(view.resourceName, list);
640
+ const evidenceRef = state.builder.addEvidence(view.evidence);
641
+ if (normalizedElementType !== "text-input") {
642
+ addAttributeUnresolved(
643
+ state.builder,
644
+ factRef,
645
+ "visibleText",
646
+ view.text,
647
+ evidenceRef,
648
+ );
649
+ }
650
+ if (view.enabled) {
651
+ const enabled = booleanAttributeValue(view.enabled);
652
+ if (enabled === undefined) {
653
+ addXmlPropertyUnresolved(
654
+ state,
655
+ factRef,
656
+ "enabled",
657
+ view.enabled,
658
+ evidenceRef,
659
+ );
660
+ } else {
661
+ addXmlUiProperty(state, factRef, view, "enabled", enabled);
662
+ }
663
+ }
664
+ const defaultAttribute =
665
+ view.checked ??
666
+ (normalizedElementType === "text-input" ? view.text : undefined);
667
+ if (defaultAttribute) {
668
+ const preferenceControl =
669
+ view.resourceType === "xml" &&
670
+ simpleTag(view.tag).toLowerCase().includes("preference");
671
+ const defaultValue = view.checked
672
+ ? preferenceControl
673
+ ? preferenceDefaultValue(defaultAttribute)
674
+ : booleanAttributeValue(defaultAttribute)
675
+ : textDefaultValue(defaultAttribute);
676
+ if (defaultValue === undefined) {
677
+ addXmlPropertyUnresolved(
678
+ state,
679
+ factRef,
680
+ "default",
681
+ defaultAttribute,
682
+ evidenceRef,
683
+ );
684
+ } else {
685
+ addXmlUiProperty(state, factRef, view, "default", defaultValue);
686
+ }
687
+ }
688
+ if (view.entries) {
689
+ const options = optionsAttributeValue(view.entries);
690
+ if (!options) {
691
+ addXmlPropertyUnresolved(
692
+ state,
693
+ factRef,
694
+ "options",
695
+ view.entries,
696
+ evidenceRef,
697
+ );
698
+ } else {
699
+ addXmlUiProperty(state, factRef, view, "options", options);
700
+ }
701
+ } else if (
702
+ view.checked !== undefined ||
703
+ booleanOptionElementTypes.has(normalizedElementType)
704
+ ) {
705
+ addXmlUiProperty(state, factRef, view, "options", [false, true]);
706
+ }
707
+ if (label.source !== "label-for") {
708
+ addAttributeUnresolved(
709
+ state.builder,
710
+ factRef,
711
+ "associatedLabel",
712
+ view.hint,
713
+ evidenceRef,
714
+ );
715
+ }
716
+ if (label.candidates.length > 0 && label.labelForUnresolved) {
717
+ const candidateEvidence = label.candidates.map((candidate) =>
718
+ state.builder.addEvidence(candidate.evidence),
719
+ );
720
+ state.builder.addUnresolved({
721
+ ownerRef: factRef,
722
+ fieldPath: "data.associatedLabel",
723
+ reasonCode: "INTRINSIC_LABEL_FOR_TEXT_UNKNOWN",
724
+ message:
725
+ "android:labelFor associates this control with label elements, " +
726
+ "but their selected text is absent or conflicting.",
727
+ evidenceRefs: candidateEvidence,
728
+ candidates: label.candidates.map((candidate, index) => ({
729
+ value: candidate.value ?? candidate.raw ?? null,
730
+ supportRefs: [candidateEvidence[index]],
731
+ })),
732
+ impact: ["control-description"],
733
+ });
734
+ }
735
+ if (
736
+ view.actionable &&
737
+ !inferredResourceId &&
738
+ (onlyIdentifierNamingEvidence || noNamingEvidence)
739
+ ) {
740
+ state.builder.addUnresolved({
741
+ ownerRef: factRef,
742
+ fieldPath: "data.controlName",
743
+ reasonCode: "INTRINSIC_CONTROL_NAME_UNKNOWN",
744
+ message:
745
+ "The control has no visible text, content description, " +
746
+ "associated label, hint, or stable icon resource name.",
747
+ evidenceRefs: [evidenceRef],
748
+ candidates: [],
749
+ impact: ["control-description"],
750
+ });
751
+ }
752
+ addAttributeUnresolved(
753
+ state.builder,
754
+ factRef,
755
+ "contentDescription",
756
+ view.contentDescription,
757
+ evidenceRef,
758
+ );
759
+ if (surrogate) {
760
+ state.builder.addUnresolved({
761
+ ownerRef: factRef,
762
+ fieldPath: "data.ownerKey",
763
+ reasonCode: "INTRINSIC_LAYOUT_OWNER_UNKNOWN",
764
+ message:
765
+ `No resolved source binding associates @layout/${view.resourceName} ` +
766
+ "with an Activity or Fragment.",
767
+ evidenceRefs: [evidenceRef],
768
+ candidates: [],
769
+ impact: ["area-ownership", "control-paths"],
770
+ originRef: ownerRef,
771
+ });
772
+ }
773
+ addXmlHandler(state, ownerRef, factRef, view);
774
+ }
775
+ }
776
+ for (const [collisionKey, count] of semanticOccurrences) {
777
+ if (count <= 1) continue;
778
+ const identity = JSON.parse(collisionKey);
779
+ const factRef = state.builder.stableId("fact", "ui-element", identity);
780
+ const fact = state.builder.fact(factRef);
781
+ if (!fact) continue;
782
+ state.builder.addUnresolved({
783
+ ownerRef: factRef,
784
+ fieldPath: "id",
785
+ reasonCode: "INTRINSIC_UI_IDENTITY_AMBIGUOUS",
786
+ message:
787
+ "Multiple XML elements have the same semantic identity and no " +
788
+ "resource ID; they are represented as one conservative fact.",
789
+ evidenceRefs: fact.support.map((item) => item.evidenceRef),
790
+ candidates: [],
791
+ impact: ["control-cardinality"],
792
+ });
793
+ }
794
+ return contentByLayout;
795
+ }
796
+ function addLayoutBindings(android, state, contentByLayout) {
797
+ for (const resource of android.resources) {
798
+ if (resource.resourceType !== "layout") continue;
799
+ const contentRefs = [
800
+ ...new Set(contentByLayout.get(resource.name) ?? []),
801
+ ].sort();
802
+ if (contentRefs.length === 0) continue;
803
+ let owners = [...(state.layoutOwners.get(resource.name) ?? [])];
804
+ if (owners.length === 0) {
805
+ owners = [state.ensureLayoutSurrogate(resource.name, resource.evidence)];
806
+ }
807
+ for (const ownerRef of owners.sort()) {
808
+ state.builder.addFact("container-binding", {
809
+ identity: [
810
+ { kind: "component", value: ownerRef },
811
+ { kind: "resource", value: `layout/${resource.name}` },
812
+ ],
813
+ confidence:
814
+ ownerFactData(state.builder, ownerRef).componentType === "widget"
815
+ ? "medium"
816
+ : "high",
817
+ support: [
818
+ {
819
+ location: resource.evidence,
820
+ role: "selected layout",
821
+ },
822
+ ],
823
+ data: {
824
+ bindingKind: "layout",
825
+ hostKey: ownerRef,
826
+ contentKeys: contentRefs,
827
+ },
828
+ relatedFactRefs: [ownerRef, ...contentRefs],
829
+ });
830
+ }
831
+ }
832
+ }
833
+ function navigationHosts(android, state, resourceName, location) {
834
+ const resolved = [
835
+ ...(state.navigationHostsByGraph.get(resourceName) ?? []),
836
+ ].sort();
837
+ if (resolved.length > 0) return resolved;
838
+ return [
839
+ state.ensureComponent(
840
+ `@navigation/${resourceName}`,
841
+ "other",
842
+ location,
843
+ "navigation graph",
844
+ "high",
845
+ { uiTechnology: "views" },
846
+ ),
847
+ ];
848
+ }
849
+ function addNavigationFacts(android, state) {
850
+ const shortcutResources = new Set(
851
+ android.manifest.components.flatMap(
852
+ (component) => component.shortcutResources ?? [],
853
+ ),
854
+ );
855
+ for (const target of android.navigationTargets) {
856
+ // Shortcut intents are alternate entry declarations, not user actions
857
+ // originating from an in-app Area.
858
+ if (
859
+ target.kind === "intent" &&
860
+ target.resourceType === "xml" &&
861
+ shortcutResources.has(target.resourceName)
862
+ )
863
+ continue;
864
+ const hostRefs = navigationHosts(
865
+ android,
866
+ state,
867
+ target.resourceName,
868
+ target.evidence,
869
+ );
870
+ for (const hostRef of hostRefs) {
871
+ const sourceRef = target.sourceId
872
+ ? (state.fragmentByNavigationId.get(target.sourceId) ?? hostRef)
873
+ : hostRef;
874
+ const destinationRef = target.destination
875
+ ? state.fragmentByNavigationId.get(target.destination)
876
+ : undefined;
877
+ if (
878
+ target.kind === "navigation-action" &&
879
+ target.actionId &&
880
+ destinationRef
881
+ ) {
882
+ state.navigationTargetByActionId.set(target.actionId, destinationRef);
883
+ }
884
+ if (target.kind === "graph-start" && destinationRef) {
885
+ state.builder.addFact("container-binding", {
886
+ identity: [
887
+ { kind: "component", value: hostRef },
888
+ {
889
+ kind: "resource",
890
+ value: `navigation/${target.resourceName}`,
891
+ },
892
+ ],
893
+ confidence: "high",
894
+ support: [
895
+ {
896
+ location: target.evidence,
897
+ role: "navigation start destination",
898
+ },
899
+ ],
900
+ data: {
901
+ bindingKind: "navigation",
902
+ hostKey: hostRef,
903
+ contentKeys: [destinationRef],
904
+ initialContentKey: destinationRef,
905
+ },
906
+ relatedFactRefs: [hostRef, destinationRef],
907
+ });
908
+ continue;
909
+ }
910
+ let resolvedTarget = destinationRef;
911
+ let targetKind = destinationRef ? "component" : "dynamic";
912
+ if (target.targetClass) {
913
+ const className = resolvedClassName(
914
+ target.targetClass,
915
+ target.targetPackage ?? android.manifest.packageName,
916
+ );
917
+ resolvedTarget =
918
+ state.componentByQualifiedName.get(className) ??
919
+ state.ensureComponent(
920
+ className,
921
+ "activity",
922
+ target.evidence,
923
+ "navigation intent target",
924
+ "high",
925
+ { uiTechnology: "views" },
926
+ );
927
+ targetKind =
928
+ target.targetPackage &&
929
+ target.targetPackage !== android.manifest.packageName
930
+ ? "external"
931
+ : "component";
932
+ } else if (target.targetPackage) {
933
+ resolvedTarget = state.ensureComponent(
934
+ `external-package:${target.targetPackage}`,
935
+ "other",
936
+ target.evidence,
937
+ "external navigation package",
938
+ "medium",
939
+ );
940
+ targetKind = "external";
941
+ }
942
+ const callbackRef = state.builder.addFact("callback", {
943
+ identity: [
944
+ { kind: "component", value: sourceRef },
945
+ {
946
+ kind: "callback",
947
+ value:
948
+ `@${target.resourceType}/${target.resourceName}:${target.kind}:` +
949
+ `${target.actionId ?? target.sourceId ?? "root"}`,
950
+ },
951
+ ],
952
+ confidence: "high",
953
+ support: [
954
+ {
955
+ location: target.evidence,
956
+ role: "navigation declaration",
957
+ },
958
+ ],
959
+ data: {
960
+ ownerKey: sourceRef,
961
+ callableSymbol:
962
+ `@${target.resourceType}/${target.resourceName}#` +
963
+ `${target.actionId ?? target.sourceId ?? target.kind}`,
964
+ callbackKind: "function",
965
+ },
966
+ relatedFactRefs: [sourceRef],
967
+ });
968
+ const effectRef = state.builder.addFact("behavior-effect", {
969
+ identity: [
970
+ { kind: "callback", value: callbackRef },
971
+ {
972
+ kind: "route",
973
+ value:
974
+ target.destination ??
975
+ target.targetClass ??
976
+ target.targetPackage ??
977
+ target.kind,
978
+ },
979
+ ],
980
+ confidence: resolvedTarget ? "high" : "medium",
981
+ support: [
982
+ {
983
+ location: target.evidence,
984
+ role: "navigation target declaration",
985
+ },
986
+ ],
987
+ data: {
988
+ callbackKey: callbackRef,
989
+ effectKind:
990
+ targetKind === "external" ? "open-external" : "navigate-area",
991
+ ...(resolvedTarget ? { targetKey: resolvedTarget } : {}),
992
+ },
993
+ relatedFactRefs: [
994
+ callbackRef,
995
+ ...(resolvedTarget ? [resolvedTarget] : []),
996
+ ],
997
+ });
998
+ const navigationRef = state.builder.addFact("navigation-target", {
999
+ identity: [
1000
+ { kind: "semantic-key", value: effectRef },
1001
+ {
1002
+ kind: "route",
1003
+ value:
1004
+ target.destination ??
1005
+ target.targetClass ??
1006
+ target.targetPackage ??
1007
+ target.kind,
1008
+ },
1009
+ ],
1010
+ confidence: resolvedTarget ? "high" : "medium",
1011
+ support: [
1012
+ {
1013
+ location: target.evidence,
1014
+ role: "navigation destination",
1015
+ },
1016
+ ],
1017
+ data: {
1018
+ effectKey: effectRef,
1019
+ targetKind,
1020
+ ...(resolvedTarget ? { targetKey: resolvedTarget } : {}),
1021
+ },
1022
+ relatedFactRefs: [
1023
+ effectRef,
1024
+ ...(resolvedTarget ? [resolvedTarget] : []),
1025
+ ],
1026
+ });
1027
+ if (!resolvedTarget) {
1028
+ const evidenceRef = state.builder.addEvidence(target.evidence);
1029
+ state.builder.addUnresolved({
1030
+ ownerRef: navigationRef,
1031
+ fieldPath: "data.targetKey",
1032
+ reasonCode: "INTRINSIC_NAVIGATION_TARGET_UNKNOWN",
1033
+ message:
1034
+ "The navigation declaration names a destination that is not " +
1035
+ "present in the selected source facts.",
1036
+ evidenceRefs: [evidenceRef],
1037
+ candidates: target.destination
1038
+ ? [{ value: target.destination, supportRefs: [evidenceRef] }]
1039
+ : [],
1040
+ impact: ["area-edge-target"],
1041
+ });
1042
+ }
1043
+ }
1044
+ }
1045
+ }
1046
+ export function normalizeAndroidFacts(android, state) {
1047
+ addManifestFacts(android, state);
1048
+ addResourceFacts(android, state.builder);
1049
+ addFragmentComponents(android, state);
1050
+ addXmlFragmentBindings(android, state);
1051
+ const contentByLayout = addViews(android, state);
1052
+ addLayoutBindings(android, state, contentByLayout);
1053
+ addNavigationFacts(android, state);
1054
+ }
1055
+ /**
1056
+ * Registers semantic component identities before JVM calls are interpreted.
1057
+ * Calling `normalizeAndroidFacts` afterwards is safe and only merges support.
1058
+ */
1059
+ export function prepareAndroidStructure(android, state) {
1060
+ addManifestFacts(android, state);
1061
+ addFragmentComponents(android, state);
1062
+ }