android2harmony 0.1.6 → 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 (227) 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 +20 -9
  213. package/skills/a2h-ui-transfer/scripts/arkts_static_check.js +80 -0
  214. package/skills/hmos-convert-pipeline/SKILL.md +7 -5
  215. package/skills/hmos-incremental-ui-align/README.md +7 -7
  216. package/skills/hmos-incremental-ui-align/SKILL.md +1 -1
  217. package/skills/hmos-incremental-ui-align/page_align.md +1 -1
  218. package/skills/hmos-spec-generate/SKILL.md +2 -2
  219. package/skills/hmos-spec-generate/scripts/parse_requirements.mjs +537 -0
  220. package/skills/hmos-fix-build-errors/SKILL.md +0 -266
  221. package/skills/hmos-fix-build-errors/references/arkts-strict-patterns.md +0 -219
  222. package/skills/hmos-fix-build-errors/references/known-patterns.md +0 -157
  223. package/skills/hmos-fix-build-errors/references/rdb-entity-pattern.md +0 -131
  224. package/skills/hmos-spec-generate/scripts/parse_requirements.ts +0 -515
  225. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/resolve-metadata-tool.mjs +0 -0
  226. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/self-test-runner.mjs +0 -0
  227. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/testcases-tool.mjs +0 -0
@@ -0,0 +1,580 @@
1
+ function add(issues, code, path, message) {
2
+ issues.push({ code, path, message });
3
+ }
4
+ function uniqueSorted(values) {
5
+ return [...new Set(values)].sort((left, right) =>
6
+ left.localeCompare(right, "en"),
7
+ );
8
+ }
9
+ function graphTargetKey(target) {
10
+ return "status" in target
11
+ ? `unresolved:${target.unresolvedRef}`
12
+ : `${target.kind}:${target.ref}`;
13
+ }
14
+ function reqBranchTargetKey(target) {
15
+ return "status" in target
16
+ ? `unresolved:${target.unresolvedRef}`
17
+ : `${target.kind}:${target.ref}`;
18
+ }
19
+ function validateExternalTarget(target, path, graph, issues) {
20
+ const endpoint = graph.externalEndpoints.find(
21
+ (candidate) => candidate.id === target.ref,
22
+ );
23
+ if (!endpoint) {
24
+ add(
25
+ issues,
26
+ "REQ_FEATURE_EXTERNAL_TARGET_MISSING",
27
+ `${path}/ref`,
28
+ `Unknown external endpoint ${target.ref}`,
29
+ );
30
+ return;
31
+ }
32
+ for (const field of ["presentation", "intentAction", "mimeType", "uri"]) {
33
+ if (
34
+ endpoint[field] !== undefined &&
35
+ JSON.stringify(target[field]) !== JSON.stringify(endpoint[field])
36
+ ) {
37
+ add(
38
+ issues,
39
+ "REQ_FEATURE_EXTERNAL_METADATA_MISMATCH",
40
+ `${path}/${field}`,
41
+ `Feature target must preserve endpoint ${field}`,
42
+ );
43
+ }
44
+ }
45
+ }
46
+ function graphConditionAlternatives(edges) {
47
+ return uniqueSorted(
48
+ edges.flatMap((edge) => {
49
+ const alternatives =
50
+ edge.conditionAlternatives && edge.conditionAlternatives.length > 0
51
+ ? edge.conditionAlternatives
52
+ : [{ conditionRefs: edge.conditions }];
53
+ return alternatives.map((alternative) =>
54
+ uniqueSorted(alternative.conditionRefs).join("\u0000"),
55
+ );
56
+ }),
57
+ );
58
+ }
59
+ function interactionViews(feature) {
60
+ if (feature.interactions !== undefined) return feature.interactions;
61
+ return [
62
+ {
63
+ id: feature.id,
64
+ trigger: feature.trigger,
65
+ conditions: feature.conditions,
66
+ effects: feature.effects,
67
+ ...(feature.target === undefined ? {} : { target: feature.target }),
68
+ unresolvedRefs: feature.unresolvedRefs,
69
+ },
70
+ ];
71
+ }
72
+ function validateInteractionSemantics(
73
+ interaction,
74
+ action,
75
+ interactionPath,
76
+ feature,
77
+ graph,
78
+ graphAreaById,
79
+ unresolvedIds,
80
+ issues,
81
+ ) {
82
+ if (interaction.trigger !== action.trigger) {
83
+ add(
84
+ issues,
85
+ "REQ_INTERACTION_TRIGGER_MISMATCH",
86
+ `${interactionPath}/trigger`,
87
+ "Interaction trigger differs from its Graph Action",
88
+ );
89
+ }
90
+ const graphEffects = action.effectRefs
91
+ .map((effectRef) => graph.effects.find((effect) => effect.id === effectRef))
92
+ .filter((effect) => effect !== undefined);
93
+ if (graphEffects.length !== interaction.effects.length) {
94
+ add(
95
+ issues,
96
+ "REQ_FEATURE_EFFECT_SET_MISMATCH",
97
+ `${interactionPath}/effects`,
98
+ "Interaction must preserve every Effect owned by its Action",
99
+ );
100
+ } else {
101
+ graphEffects.forEach((graphEffect, effectIndex) => {
102
+ const reqEffect = interaction.effects[effectIndex];
103
+ if (reqEffect.kind !== undefined && reqEffect.kind !== graphEffect.kind) {
104
+ add(
105
+ issues,
106
+ "REQ_FEATURE_EFFECT_KIND_MISMATCH",
107
+ `${interactionPath}/effects/${effectIndex}/kind`,
108
+ "Interaction Effect kind differs from Area Graph",
109
+ );
110
+ }
111
+ for (const field of ["stateKey", "value"]) {
112
+ if (
113
+ graphEffect[field] !== undefined &&
114
+ JSON.stringify(reqEffect[field]) !==
115
+ JSON.stringify(graphEffect[field])
116
+ ) {
117
+ add(
118
+ issues,
119
+ "REQ_FEATURE_EFFECT_DATA_MISMATCH",
120
+ `${interactionPath}/effects/${effectIndex}/${field}`,
121
+ `Interaction Effect must preserve Graph ${field}`,
122
+ );
123
+ }
124
+ }
125
+ });
126
+ }
127
+ const requiredConditionRefs = uniqueSorted([
128
+ ...action.preconditionRefs,
129
+ ...((action.preconditionAlternatives?.length ?? 0) > 1 &&
130
+ action.preconditionAlternatives?.every(
131
+ (alternative) => alternative.conditionRefs.length > 0,
132
+ )
133
+ ? action.preconditionAlternatives.flatMap(
134
+ (alternative) => alternative.conditionRefs,
135
+ )
136
+ : []),
137
+ ...graph.edges
138
+ .filter((edge) => edge.actionRef === action.id)
139
+ .flatMap((edge) => edge.preconditions),
140
+ ]);
141
+ const projectedConditionRefs = new Set(
142
+ interaction.conditions.flatMap((condition) => condition.supportRefs),
143
+ );
144
+ if (requiredConditionRefs.some((ref) => !projectedConditionRefs.has(ref))) {
145
+ add(
146
+ issues,
147
+ "REQ_INTERACTION_CONDITION_SET_MISMATCH",
148
+ `${interactionPath}/conditions`,
149
+ "Interaction must preserve every Action precondition",
150
+ );
151
+ }
152
+ const target = interaction.target;
153
+ if (target && !("status" in target) && target.kind === "external") {
154
+ validateExternalTarget(target, `${interactionPath}/target`, graph, issues);
155
+ }
156
+ if (
157
+ target &&
158
+ !("status" in target) &&
159
+ target.kind === "conditional-area-set"
160
+ ) {
161
+ const expectedRefs = [
162
+ ...new Set(
163
+ graph.edges.flatMap((edge) =>
164
+ edge.actionRef === action.id &&
165
+ !("status" in edge.target) &&
166
+ edge.target.kind === "area"
167
+ ? [edge.target.ref]
168
+ : [],
169
+ ),
170
+ ),
171
+ ].sort();
172
+ const actualRefs = [...target.refs].sort();
173
+ const branchRefs = (target.branches ?? [])
174
+ .map((branch) => branch.ref)
175
+ .sort();
176
+ if (
177
+ JSON.stringify(actualRefs) !== JSON.stringify(expectedRefs) ||
178
+ JSON.stringify(branchRefs) !== JSON.stringify(expectedRefs)
179
+ ) {
180
+ add(
181
+ issues,
182
+ "REQ_FEATURE_CONDITIONAL_BRANCH_MISMATCH",
183
+ `${interactionPath}/target/branches`,
184
+ "Conditional target must preserve every Graph target branch",
185
+ );
186
+ }
187
+ }
188
+ if (
189
+ target &&
190
+ !("status" in target) &&
191
+ target.kind === "alternative-target-set"
192
+ ) {
193
+ const actionEdges = graph.edges.filter(
194
+ (edge) => edge.actionRef === action.id,
195
+ );
196
+ const expectedKeys = uniqueSorted(
197
+ actionEdges.map((edge) => graphTargetKey(edge.target)),
198
+ );
199
+ const actualKeys = target.branches.map((branch) =>
200
+ reqBranchTargetKey(branch.target),
201
+ );
202
+ if (
203
+ new Set(actualKeys).size !== actualKeys.length ||
204
+ JSON.stringify([...actualKeys].sort()) !== JSON.stringify(expectedKeys)
205
+ ) {
206
+ add(
207
+ issues,
208
+ "REQ_FEATURE_ALTERNATIVE_TARGET_MISMATCH",
209
+ `${interactionPath}/target/branches`,
210
+ "Alternative target must preserve every Graph target branch",
211
+ );
212
+ }
213
+ target.branches.forEach((branch, branchIndex) => {
214
+ const branchPath = `${interactionPath}/target/branches/${branchIndex}`;
215
+ const key = reqBranchTargetKey(branch.target);
216
+ const matchingEdges = actionEdges.filter(
217
+ (edge) => graphTargetKey(edge.target) === key,
218
+ );
219
+ if ("status" in branch.target) {
220
+ if (!unresolvedIds.has(branch.target.unresolvedRef)) {
221
+ add(
222
+ issues,
223
+ "REQ_FEATURE_TARGET_UNRESOLVED_MISSING",
224
+ `${branchPath}/target/unresolvedRef`,
225
+ `Unknown unresolved item ${branch.target.unresolvedRef}`,
226
+ );
227
+ }
228
+ if (
229
+ !interaction.unresolvedRefs.includes(branch.target.unresolvedRef) ||
230
+ !feature.unresolvedRefs.includes(branch.target.unresolvedRef)
231
+ ) {
232
+ add(
233
+ issues,
234
+ "REQ_FEATURE_TARGET_UNRESOLVED_NOT_LISTED",
235
+ `${interactionPath}/unresolvedRefs`,
236
+ "Interaction and Feature unresolvedRefs must include every " +
237
+ "unresolved target branch",
238
+ );
239
+ }
240
+ } else if (branch.target.kind === "area") {
241
+ if (!graphAreaById.has(branch.target.ref)) {
242
+ add(
243
+ issues,
244
+ "REQ_FEATURE_AREA_TARGET_MISSING",
245
+ `${branchPath}/target/ref`,
246
+ `Unknown Area ${branch.target.ref}`,
247
+ );
248
+ }
249
+ } else {
250
+ validateExternalTarget(
251
+ branch.target,
252
+ `${branchPath}/target`,
253
+ graph,
254
+ issues,
255
+ );
256
+ }
257
+ const expectedAlternatives = graphConditionAlternatives(matchingEdges);
258
+ const actualAlternatives = uniqueSorted(
259
+ branch.conditionAlternatives.map((alternative) =>
260
+ uniqueSorted(
261
+ alternative.conditions.flatMap(
262
+ (condition) => condition.supportRefs,
263
+ ),
264
+ ).join("\u0000"),
265
+ ),
266
+ );
267
+ if (
268
+ JSON.stringify(actualAlternatives) !==
269
+ JSON.stringify(expectedAlternatives)
270
+ ) {
271
+ add(
272
+ issues,
273
+ "REQ_FEATURE_ALTERNATIVE_CONDITION_MISMATCH",
274
+ `${branchPath}/conditionAlternatives`,
275
+ "Alternative target must preserve every Graph condition branch",
276
+ );
277
+ }
278
+ const expectedSupportRefs = uniqueSorted(
279
+ matchingEdges.flatMap((edge) => edge.sourceFactRefs),
280
+ );
281
+ if (
282
+ JSON.stringify(uniqueSorted(branch.supportRefs)) !==
283
+ JSON.stringify(expectedSupportRefs)
284
+ ) {
285
+ add(
286
+ issues,
287
+ "REQ_FEATURE_ALTERNATIVE_SUPPORT_MISMATCH",
288
+ `${branchPath}/supportRefs`,
289
+ "Alternative target must preserve Graph branch support",
290
+ );
291
+ }
292
+ });
293
+ }
294
+ interaction.unresolvedRefs.forEach((ref) => {
295
+ if (!unresolvedIds.has(ref)) {
296
+ add(
297
+ issues,
298
+ "REQ_UNRESOLVED_REF_MISSING",
299
+ `${interactionPath}/unresolvedRefs`,
300
+ `Unknown unresolved item ${ref}`,
301
+ );
302
+ }
303
+ if (!feature.unresolvedRefs.includes(ref)) {
304
+ add(
305
+ issues,
306
+ "REQ_INTERACTION_UNRESOLVED_NOT_LISTED",
307
+ `${interactionPath}/unresolvedRefs`,
308
+ "Feature unresolvedRefs must include every Interaction unresolved ref",
309
+ );
310
+ }
311
+ });
312
+ }
313
+ export function validateReqModel(req, graph, source) {
314
+ const issues = [];
315
+ if (
316
+ req.caseId !== graph.caseId ||
317
+ JSON.stringify(req.analysisContext) !==
318
+ JSON.stringify(graph.analysisContext)
319
+ ) {
320
+ add(
321
+ issues,
322
+ "REQ_CONTEXT_MISMATCH",
323
+ "/analysisContext",
324
+ "req-model and area-graph must use the same analysis context",
325
+ );
326
+ }
327
+ const graphAreaById = new Map(graph.areas.map((area) => [area.id, area]));
328
+ const controlById = new Map(
329
+ graph.controls.map((control) => [control.id, control]),
330
+ );
331
+ const pathById = new Map(graph.paths.map((path) => [path.id, path]));
332
+ const unresolvedIds = new Set(graph.unresolved.map((item) => item.id));
333
+ const reqIds = new Set();
334
+ req.areas.forEach((area, areaIndex) => {
335
+ const base = `/areas/${areaIndex}`;
336
+ if (reqIds.has(area.id)) {
337
+ add(issues, "REQ_ID_DUPLICATE", `${base}/id`, area.id);
338
+ }
339
+ reqIds.add(area.id);
340
+ const graphArea = graphAreaById.get(area.areaRef);
341
+ if (!graphArea) {
342
+ add(
343
+ issues,
344
+ "REQ_AREA_REF_MISSING",
345
+ `${base}/areaRef`,
346
+ `Unknown Area ${area.areaRef}`,
347
+ );
348
+ }
349
+ if (
350
+ area.canonicalPath.status === "resolved" ||
351
+ area.canonicalPath.status === "unreachable"
352
+ ) {
353
+ const path = pathById.get(area.canonicalPath.pathRef);
354
+ if (!path || path.targetAreaRef !== area.areaRef) {
355
+ add(
356
+ issues,
357
+ "REQ_PATH_REF_INVALID",
358
+ `${base}/canonicalPath/pathRef`,
359
+ `Canonical path does not target ${area.areaRef}`,
360
+ );
361
+ } else if (area.canonicalPath.reachability !== path.reachability) {
362
+ add(
363
+ issues,
364
+ "REQ_PATH_REACHABILITY_MISMATCH",
365
+ `${base}/canonicalPath/reachability`,
366
+ `Expected path reachability ${path.reachability}`,
367
+ );
368
+ }
369
+ } else if (!unresolvedIds.has(area.canonicalPath.unresolvedRef)) {
370
+ add(
371
+ issues,
372
+ "REQ_PATH_UNRESOLVED_MISSING",
373
+ `${base}/canonicalPath`,
374
+ `Unknown unresolved ${area.canonicalPath.unresolvedRef}`,
375
+ );
376
+ }
377
+ area.features.forEach((feature, featureIndex) => {
378
+ const featurePath = `${base}/features/${featureIndex}`;
379
+ if (reqIds.has(feature.id)) {
380
+ add(issues, "REQ_ID_DUPLICATE", `${featurePath}/id`, feature.id);
381
+ }
382
+ reqIds.add(feature.id);
383
+ const control = controlById.get(feature.controlRef);
384
+ const controlInArea =
385
+ graphArea !== undefined &&
386
+ control !== undefined &&
387
+ control.areaRef === area.areaRef &&
388
+ graphArea.controlRefs.includes(control.id);
389
+ if (!controlInArea) {
390
+ add(
391
+ issues,
392
+ "REQ_FEATURE_OWNER_CHAIN_INVALID",
393
+ featurePath,
394
+ "Feature does not preserve Area→Control ownership",
395
+ );
396
+ }
397
+ if (control?.enabled !== undefined) {
398
+ const enabledMatches =
399
+ feature.enabled?.status === control.enabled.status &&
400
+ (control.enabled.status === "resolved"
401
+ ? feature.enabled?.status === "resolved" &&
402
+ feature.enabled.value === control.enabled.value &&
403
+ feature.enabled.confidence === control.enabled.confidence &&
404
+ JSON.stringify(feature.enabled.supportRefs) ===
405
+ JSON.stringify(control.enabled.supportRefs)
406
+ : feature.enabled?.status === "unresolved" &&
407
+ feature.enabled.unresolvedRef === control.enabled.unresolvedRef);
408
+ if (!enabledMatches) {
409
+ add(
410
+ issues,
411
+ "REQ_FEATURE_ENABLED_MISMATCH",
412
+ `${featurePath}/enabled`,
413
+ "Feature must preserve its Control's static enabled value",
414
+ );
415
+ }
416
+ }
417
+ const interactions = interactionViews(feature);
418
+ if (feature.interactions !== undefined) {
419
+ if (feature.interactions.length === 0) {
420
+ add(
421
+ issues,
422
+ "REQ_FEATURE_INTERACTIONS_EMPTY",
423
+ `${featurePath}/interactions`,
424
+ "A Control-backed Feature must contain at least one Interaction",
425
+ );
426
+ }
427
+ const actualActionRefs = feature.interactions.map(
428
+ (interaction) => interaction.actionRef,
429
+ );
430
+ const expectedActionRefs = controlInArea ? control.actionRefs : [];
431
+ if (
432
+ new Set(actualActionRefs).size !== actualActionRefs.length ||
433
+ JSON.stringify(actualActionRefs) !==
434
+ JSON.stringify(expectedActionRefs)
435
+ ) {
436
+ add(
437
+ issues,
438
+ "REQ_FEATURE_INTERACTION_SET_MISMATCH",
439
+ `${featurePath}/interactions`,
440
+ "Feature must preserve every Control Action exactly once and in " +
441
+ "stable order",
442
+ );
443
+ }
444
+ }
445
+ interactions.forEach((interaction, interactionIndex) => {
446
+ const interactionPath =
447
+ feature.interactions === undefined
448
+ ? featurePath
449
+ : `${featurePath}/interactions/${interactionIndex}`;
450
+ if (feature.interactions !== undefined) {
451
+ if (reqIds.has(interaction.id)) {
452
+ add(
453
+ issues,
454
+ "REQ_ID_DUPLICATE",
455
+ `${interactionPath}/id`,
456
+ interaction.id,
457
+ );
458
+ }
459
+ reqIds.add(interaction.id);
460
+ }
461
+ const matchingActions = !controlInArea
462
+ ? []
463
+ : interaction.actionRef === undefined
464
+ ? graph.actions.filter(
465
+ (action) =>
466
+ action.controlRef === feature.controlRef &&
467
+ action.trigger === interaction.trigger,
468
+ )
469
+ : graph.actions.filter(
470
+ (action) =>
471
+ action.id === interaction.actionRef &&
472
+ action.controlRef === feature.controlRef,
473
+ );
474
+ if (matchingActions.length === 0) {
475
+ add(
476
+ issues,
477
+ "REQ_FEATURE_ACTION_MISSING",
478
+ interaction.actionRef === undefined
479
+ ? `${interactionPath}/trigger`
480
+ : `${interactionPath}/actionRef`,
481
+ "No graph Action matches this Area, Control and Interaction",
482
+ );
483
+ } else if (matchingActions.length > 1) {
484
+ add(
485
+ issues,
486
+ "REQ_FEATURE_ACTION_AMBIGUOUS",
487
+ `${interactionPath}/trigger`,
488
+ "Multiple graph Actions match this legacy Interaction",
489
+ );
490
+ }
491
+ const action =
492
+ matchingActions.length === 1 ? matchingActions[0] : undefined;
493
+ if (
494
+ action !== undefined &&
495
+ control !== undefined &&
496
+ !control.actionRefs.includes(action.id)
497
+ ) {
498
+ add(
499
+ issues,
500
+ "REQ_FEATURE_OWNER_CHAIN_INVALID",
501
+ interactionPath,
502
+ "Interaction does not preserve Area→Control→Action ownership",
503
+ );
504
+ }
505
+ if (action) {
506
+ validateInteractionSemantics(
507
+ interaction,
508
+ action,
509
+ interactionPath,
510
+ feature,
511
+ graph,
512
+ graphAreaById,
513
+ unresolvedIds,
514
+ issues,
515
+ );
516
+ }
517
+ });
518
+ feature.unresolvedRefs.forEach((ref) => {
519
+ if (!unresolvedIds.has(ref)) {
520
+ add(
521
+ issues,
522
+ "REQ_UNRESOLVED_REF_MISSING",
523
+ `${featurePath}/unresolvedRefs`,
524
+ `Unknown unresolved item ${ref}`,
525
+ );
526
+ }
527
+ });
528
+ });
529
+ if (area.features.some((feature) => feature.interactions !== undefined)) {
530
+ const actualControlRefs = area.features.map(
531
+ (feature) => feature.controlRef,
532
+ );
533
+ const expectedControlRefs = (graphArea?.controlRefs ?? []).filter(
534
+ (controlRef) =>
535
+ (controlById.get(controlRef)?.actionRefs.length ?? 0) > 0,
536
+ );
537
+ if (
538
+ new Set(actualControlRefs).size !== actualControlRefs.length ||
539
+ JSON.stringify(actualControlRefs) !==
540
+ JSON.stringify(expectedControlRefs)
541
+ ) {
542
+ add(
543
+ issues,
544
+ "REQ_AREA_FEATURE_CONTROL_SET_MISMATCH",
545
+ `${base}/features`,
546
+ "Area must project every actionable Control to exactly one Feature",
547
+ );
548
+ }
549
+ }
550
+ });
551
+ if (
552
+ req.areas.length !== graph.areas.length ||
553
+ graph.areas.some(
554
+ (area) => !req.areas.some((reqArea) => reqArea.areaRef === area.id),
555
+ )
556
+ ) {
557
+ add(
558
+ issues,
559
+ "REQ_AREA_SET_MISMATCH",
560
+ "/areas",
561
+ "req-model must include every graph Area exactly once",
562
+ );
563
+ }
564
+ if (source) {
565
+ const sourceIds = new Set(source.facts.map((fact) => fact.id));
566
+ req.areas.forEach((area, areaIndex) => {
567
+ area.supportRefs.forEach((ref) => {
568
+ if (!sourceIds.has(ref)) {
569
+ add(
570
+ issues,
571
+ "REQ_SUPPORT_REF_MISSING",
572
+ `/areas/${areaIndex}/supportRefs`,
573
+ `Unknown source fact ${ref}`,
574
+ );
575
+ }
576
+ });
577
+ });
578
+ }
579
+ return { ok: issues.length === 0, issues };
580
+ }