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,1431 @@
1
+ import { createHash } from "node:crypto";
2
+ const REFERENCE_SHAPED_TEXT = /^(?:condition|fact):/u;
3
+ class SemanticProjectionReferenceError extends Error {
4
+ path;
5
+ constructor(path, message) {
6
+ super(message);
7
+ this.name = "SemanticProjectionReferenceError";
8
+ this.path = path;
9
+ }
10
+ }
11
+ function normalizeString(value) {
12
+ return value.replace(/\r\n?/gu, "\n").normalize("NFC");
13
+ }
14
+ function normalizeJson(value) {
15
+ if (value === null) return null;
16
+ if (typeof value === "string") return normalizeString(value);
17
+ if (typeof value === "boolean") return value;
18
+ if (typeof value === "number") {
19
+ if (!Number.isFinite(value)) {
20
+ throw new TypeError("Semantic projection accepts finite JSON numbers");
21
+ }
22
+ return value;
23
+ }
24
+ if (Array.isArray(value)) {
25
+ return value.map((item) => normalizeJson(item));
26
+ }
27
+ if (typeof value === "object") {
28
+ const normalized = {};
29
+ for (const key of Object.keys(value).sort()) {
30
+ const child = value[key];
31
+ if (child !== undefined) {
32
+ normalized[key] = normalizeJson(child);
33
+ }
34
+ }
35
+ return normalized;
36
+ }
37
+ throw new TypeError(
38
+ `Semantic projection accepts JSON values, got ${typeof value}`,
39
+ );
40
+ }
41
+ function stableStringify(value) {
42
+ if (
43
+ value === null ||
44
+ typeof value === "string" ||
45
+ typeof value === "number" ||
46
+ typeof value === "boolean"
47
+ ) {
48
+ return JSON.stringify(value);
49
+ }
50
+ if (Array.isArray(value)) {
51
+ return `[${value.map(stableStringify).join(",")}]`;
52
+ }
53
+ const objectValue = value;
54
+ return `{${Object.keys(objectValue)
55
+ .sort()
56
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(objectValue[key])}`)
57
+ .join(",")}}`;
58
+ }
59
+ function fingerprint(value) {
60
+ return createHash("sha256")
61
+ .update(stableStringify(value), "utf8")
62
+ .digest("hex");
63
+ }
64
+ function sortedValues(values) {
65
+ return [...values].sort((left, right) =>
66
+ stableStringify(left).localeCompare(stableStringify(right), "en"),
67
+ );
68
+ }
69
+ function sortedStrings(values) {
70
+ return [...values]
71
+ .map(normalizeString)
72
+ .sort((left, right) => left.localeCompare(right, "en"));
73
+ }
74
+ function supportedValue(value, scope) {
75
+ if (value.status === "unresolved") {
76
+ return { status: "unresolved" };
77
+ }
78
+ return {
79
+ status: "resolved",
80
+ value: normalizeJson(value.value),
81
+ ...(scope === "complete" ? { confidence: value.confidence } : {}),
82
+ };
83
+ }
84
+ function isReferenceMetadataKey(key) {
85
+ return (
86
+ new Set([
87
+ "id",
88
+ "ownerKey",
89
+ "componentKey",
90
+ "elementKey",
91
+ "callbackKey",
92
+ "fromCallbackKey",
93
+ "toCallbackKey",
94
+ "hostKey",
95
+ "contentKeys",
96
+ "initialContentKey",
97
+ "effectKey",
98
+ "targetKey",
99
+ "stateKey",
100
+ "conditionKeys",
101
+ "relatedKeys",
102
+ "diagnosticIds",
103
+ ]).has(key) || /(?:Ref|Refs)$/u.test(key)
104
+ );
105
+ }
106
+ function semanticData(value) {
107
+ if (value === null) return null;
108
+ if (
109
+ typeof value === "string" ||
110
+ typeof value === "boolean" ||
111
+ typeof value === "number"
112
+ ) {
113
+ return normalizeJson(value);
114
+ }
115
+ if (Array.isArray(value)) {
116
+ return value.map((item) => semanticData(item));
117
+ }
118
+ if (typeof value !== "object") {
119
+ return normalizeJson(value);
120
+ }
121
+ const result = {};
122
+ for (const key of Object.keys(value).sort()) {
123
+ if (isReferenceMetadataKey(key)) continue;
124
+ const child = value[key];
125
+ if (child !== undefined) result[key] = semanticData(child);
126
+ }
127
+ return result;
128
+ }
129
+ function conditionPredicate(data) {
130
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
131
+ return semanticData(data);
132
+ }
133
+ const record = data;
134
+ const preferredKeys = [
135
+ "expressionAnchor",
136
+ "expression",
137
+ "predicate",
138
+ "description",
139
+ "plainLanguage",
140
+ ];
141
+ for (const key of preferredKeys) {
142
+ if (record[key] !== undefined) {
143
+ return semanticData(record[key]);
144
+ }
145
+ }
146
+ return semanticData(data);
147
+ }
148
+ function conditionValue(refOrLiteral, sourceFactById, scope, path) {
149
+ const fact = sourceFactById.get(refOrLiteral);
150
+ if (fact) {
151
+ if (fact.kind !== "condition") {
152
+ throw new SemanticProjectionReferenceError(
153
+ path,
154
+ `Condition reference resolves to ${fact.kind}, not condition`,
155
+ );
156
+ }
157
+ return {
158
+ status: "resolved",
159
+ predicate:
160
+ scope === "complete"
161
+ ? semanticData(fact.data)
162
+ : conditionPredicate(fact.data),
163
+ };
164
+ }
165
+ if (REFERENCE_SHAPED_TEXT.test(refOrLiteral)) {
166
+ throw new SemanticProjectionReferenceError(
167
+ path,
168
+ "Condition reference does not resolve in the companion Source Facts",
169
+ );
170
+ }
171
+ return {
172
+ status: "literal",
173
+ value: normalizeString(refOrLiteral),
174
+ };
175
+ }
176
+ function conditionValues(values, sourceFactById, scope, path) {
177
+ return sortedValues(
178
+ values.map((value, index) =>
179
+ conditionValue(value, sourceFactById, scope, `${path}/${index}`),
180
+ ),
181
+ );
182
+ }
183
+ function entityKey(kind, id) {
184
+ return `${kind}\u0000${id}`;
185
+ }
186
+ function semanticIssue(code, path, message) {
187
+ return { code, path, message };
188
+ }
189
+ class Projector {
190
+ graph;
191
+ source;
192
+ scope;
193
+ sourceFactById;
194
+ unresolvedById;
195
+ nodes = new Map();
196
+ adjacency = new Map();
197
+ fingerprints;
198
+ constructor(input, scope) {
199
+ this.graph = input.areaGraph;
200
+ this.source = input.sourceFacts;
201
+ this.scope = scope;
202
+ this.sourceFactById = new Map(
203
+ input.sourceFacts.facts.map((fact) => [fact.id, fact]),
204
+ );
205
+ this.unresolvedById = new Map(
206
+ input.areaGraph.unresolved.map((item) => [item.id, item]),
207
+ );
208
+ this.registerNodes();
209
+ this.registerRelationships();
210
+ this.fingerprints = this.refineFingerprints();
211
+ }
212
+ project() {
213
+ const pathById = new Map(
214
+ this.graph.paths.map((path, index) => [
215
+ path.id,
216
+ this.pathRecord(path, `/paths/${index}`),
217
+ ]),
218
+ );
219
+ const unresolvedRecords = this.graph.unresolved.map((item, index) =>
220
+ this.unresolvedSummary(
221
+ item,
222
+ this.ownerReference(item.ownerRef, `/unresolved/${index}/ownerRef`),
223
+ ),
224
+ );
225
+ const unresolvedRef = (ref, path) => {
226
+ const item = this.unresolvedById.get(ref);
227
+ if (!item) {
228
+ throw new SemanticProjectionReferenceError(
229
+ path,
230
+ "Unresolved reference does not resolve in Area Graph",
231
+ );
232
+ }
233
+ return {
234
+ entity: "unresolved",
235
+ fingerprint: fingerprint(
236
+ this.unresolvedSummary(
237
+ item,
238
+ this.ownerReference(item.ownerRef, `${path}/ownerRef`),
239
+ ),
240
+ ),
241
+ };
242
+ };
243
+ const unresolvedRefs = (refs, path) =>
244
+ sortedValues(
245
+ refs.map((ref, index) => unresolvedRef(ref, `${path}/${index}`)),
246
+ );
247
+ return {
248
+ projectionVersion: "1",
249
+ scope: this.scope,
250
+ areas: sortedValues(
251
+ this.graph.areas.map((area, index) => ({
252
+ fingerprint: this.reference("area", area.id, `/areas/${index}/id`),
253
+ ...this.areaIntrinsic(area, `/areas/${index}`),
254
+ controls: sortedValues(
255
+ area.controlRefs.map((ref, refIndex) =>
256
+ this.reference(
257
+ "control",
258
+ ref,
259
+ `/areas/${index}/controlRefs/${refIndex}`,
260
+ ),
261
+ ),
262
+ ),
263
+ canonicalPath:
264
+ area.canonicalPathRef === undefined
265
+ ? null
266
+ : this.pathReference(
267
+ area.canonicalPathRef,
268
+ pathById,
269
+ `/areas/${index}/canonicalPathRef`,
270
+ ),
271
+ unresolved: unresolvedRefs(
272
+ area.unresolvedRefs,
273
+ `/areas/${index}/unresolvedRefs`,
274
+ ),
275
+ })),
276
+ ),
277
+ controls: sortedValues(
278
+ this.graph.controls.map((control, index) => ({
279
+ fingerprint: this.reference(
280
+ "control",
281
+ control.id,
282
+ `/controls/${index}/id`,
283
+ ),
284
+ ...this.controlIntrinsic(control, `/controls/${index}`),
285
+ area: this.reference(
286
+ "area",
287
+ control.areaRef,
288
+ `/controls/${index}/areaRef`,
289
+ ),
290
+ actions: sortedValues(
291
+ control.actionRefs.map((ref, refIndex) =>
292
+ this.reference(
293
+ "action",
294
+ ref,
295
+ `/controls/${index}/actionRefs/${refIndex}`,
296
+ ),
297
+ ),
298
+ ),
299
+ unresolved: unresolvedRefs(
300
+ control.unresolvedRefs,
301
+ `/controls/${index}/unresolvedRefs`,
302
+ ),
303
+ })),
304
+ ),
305
+ actions: sortedValues(
306
+ this.graph.actions.map((action, index) => ({
307
+ fingerprint: this.reference(
308
+ "action",
309
+ action.id,
310
+ `/actions/${index}/id`,
311
+ ),
312
+ ...this.actionIntrinsic(action, `/actions/${index}`),
313
+ control: this.reference(
314
+ "control",
315
+ action.controlRef,
316
+ `/actions/${index}/controlRef`,
317
+ ),
318
+ effects: sortedValues(
319
+ action.effectRefs.map((ref, refIndex) =>
320
+ this.reference(
321
+ "effect",
322
+ ref,
323
+ `/actions/${index}/effectRefs/${refIndex}`,
324
+ ),
325
+ ),
326
+ ),
327
+ })),
328
+ ),
329
+ effects: sortedValues(
330
+ this.graph.effects.map((effect, index) => ({
331
+ fingerprint: this.reference(
332
+ "effect",
333
+ effect.id,
334
+ `/effects/${index}/id`,
335
+ ),
336
+ ...this.effectIntrinsic(effect, `/effects/${index}`),
337
+ action: this.reference(
338
+ "action",
339
+ effect.actionRef,
340
+ `/effects/${index}/actionRef`,
341
+ ),
342
+ unresolved: unresolvedRefs(
343
+ effect.unresolvedRefs ?? [],
344
+ `/effects/${index}/unresolvedRefs`,
345
+ ),
346
+ })),
347
+ ),
348
+ edges: sortedValues(
349
+ this.graph.edges.map((edge, index) => ({
350
+ fingerprint: this.reference("edge", edge.id, `/edges/${index}/id`),
351
+ ...this.edgeIntrinsic(edge, `/edges/${index}`),
352
+ sourceArea: this.reference(
353
+ "area",
354
+ edge.sourceAreaRef,
355
+ `/edges/${index}/sourceAreaRef`,
356
+ ),
357
+ control: this.reference(
358
+ "control",
359
+ edge.controlRef,
360
+ `/edges/${index}/controlRef`,
361
+ ),
362
+ action: this.reference(
363
+ "action",
364
+ edge.actionRef,
365
+ `/edges/${index}/actionRef`,
366
+ ),
367
+ target:
368
+ "status" in edge.target
369
+ ? {
370
+ status: "unresolved",
371
+ unresolved: unresolvedRef(
372
+ edge.target.unresolvedRef,
373
+ `/edges/${index}/target/unresolvedRef`,
374
+ ),
375
+ }
376
+ : {
377
+ status: "resolved",
378
+ kind: edge.target.kind,
379
+ ref: this.reference(
380
+ edge.target.kind === "area" ? "area" : "external",
381
+ edge.target.ref,
382
+ `/edges/${index}/target/ref`,
383
+ ),
384
+ },
385
+ })),
386
+ ),
387
+ externalEndpoints: sortedValues(
388
+ this.graph.externalEndpoints.map((endpoint, index) => ({
389
+ fingerprint: this.reference(
390
+ "external",
391
+ endpoint.id,
392
+ `/externalEndpoints/${index}/id`,
393
+ ),
394
+ ...this.externalIntrinsic(endpoint),
395
+ unresolved: unresolvedRefs(
396
+ endpoint.unresolvedRefs,
397
+ `/externalEndpoints/${index}/unresolvedRefs`,
398
+ ),
399
+ })),
400
+ ),
401
+ entryPoints: sortedValues(
402
+ this.graph.entryPoints.map((entry, index) => ({
403
+ fingerprint: this.reference(
404
+ "entry",
405
+ entry.id,
406
+ `/entryPoints/${index}/id`,
407
+ ),
408
+ ...this.entryIntrinsic(entry, `/entryPoints/${index}`),
409
+ roots: sortedValues(
410
+ entry.rootAreaRefs.map((ref, refIndex) =>
411
+ this.reference(
412
+ "area",
413
+ ref,
414
+ `/entryPoints/${index}/rootAreaRefs/${refIndex}`,
415
+ ),
416
+ ),
417
+ ),
418
+ launcherArea:
419
+ entry.launcherAreaRef === undefined
420
+ ? null
421
+ : this.reference(
422
+ "area",
423
+ entry.launcherAreaRef,
424
+ `/entryPoints/${index}/launcherAreaRef`,
425
+ ),
426
+ })),
427
+ ),
428
+ paths: sortedValues([...pathById.values()]),
429
+ unresolved: sortedValues(unresolvedRecords),
430
+ };
431
+ }
432
+ registerNodes() {
433
+ const register = (kind, id, intrinsic) => {
434
+ const key = entityKey(kind, id);
435
+ if (this.nodes.has(key)) {
436
+ throw new SemanticProjectionReferenceError(
437
+ "/",
438
+ `Duplicate ${kind} ID prevents semantic projection`,
439
+ );
440
+ }
441
+ this.nodes.set(key, { kind, id, intrinsic });
442
+ this.adjacency.set(key, []);
443
+ };
444
+ this.graph.areas.forEach((area, index) =>
445
+ register("area", area.id, this.areaIntrinsic(area, `/areas/${index}`)),
446
+ );
447
+ this.graph.controls.forEach((control, index) =>
448
+ register(
449
+ "control",
450
+ control.id,
451
+ this.controlIntrinsic(control, `/controls/${index}`),
452
+ ),
453
+ );
454
+ this.graph.actions.forEach((action, index) =>
455
+ register(
456
+ "action",
457
+ action.id,
458
+ this.actionIntrinsic(action, `/actions/${index}`),
459
+ ),
460
+ );
461
+ this.graph.effects.forEach((effect, index) =>
462
+ register(
463
+ "effect",
464
+ effect.id,
465
+ this.effectIntrinsic(effect, `/effects/${index}`),
466
+ ),
467
+ );
468
+ this.graph.edges.forEach((edge, index) =>
469
+ register("edge", edge.id, this.edgeIntrinsic(edge, `/edges/${index}`)),
470
+ );
471
+ this.graph.externalEndpoints.forEach((endpoint) =>
472
+ register("external", endpoint.id, this.externalIntrinsic(endpoint)),
473
+ );
474
+ this.graph.entryPoints.forEach((entry, index) =>
475
+ register(
476
+ "entry",
477
+ entry.id,
478
+ this.entryIntrinsic(entry, `/entryPoints/${index}`),
479
+ ),
480
+ );
481
+ }
482
+ registerRelationships() {
483
+ const connect = (
484
+ sourceKind,
485
+ sourceId,
486
+ relation,
487
+ targetKind,
488
+ targetId,
489
+ path,
490
+ ) => {
491
+ const sourceKey = entityKey(sourceKind, sourceId);
492
+ const targetKey = entityKey(targetKind, targetId);
493
+ if (!this.nodes.has(sourceKey) || !this.nodes.has(targetKey)) {
494
+ throw new SemanticProjectionReferenceError(
495
+ path,
496
+ `Relationship ${sourceKind}→${targetKind} contains a missing entity`,
497
+ );
498
+ }
499
+ this.adjacency.get(sourceKey)?.push({ relation, targetKey });
500
+ };
501
+ const pair = (
502
+ leftKind,
503
+ leftId,
504
+ leftRelation,
505
+ rightKind,
506
+ rightId,
507
+ rightRelation,
508
+ path,
509
+ ) => {
510
+ connect(leftKind, leftId, leftRelation, rightKind, rightId, path);
511
+ connect(rightKind, rightId, rightRelation, leftKind, leftId, path);
512
+ };
513
+ this.graph.controls.forEach((control, index) =>
514
+ pair(
515
+ "area",
516
+ control.areaRef,
517
+ "owns-control",
518
+ "control",
519
+ control.id,
520
+ "owned-by-area",
521
+ `/controls/${index}/areaRef`,
522
+ ),
523
+ );
524
+ this.graph.actions.forEach((action, index) =>
525
+ pair(
526
+ "control",
527
+ action.controlRef,
528
+ "owns-action",
529
+ "action",
530
+ action.id,
531
+ "owned-by-control",
532
+ `/actions/${index}/controlRef`,
533
+ ),
534
+ );
535
+ this.graph.effects.forEach((effect, index) =>
536
+ pair(
537
+ "action",
538
+ effect.actionRef,
539
+ "owns-effect",
540
+ "effect",
541
+ effect.id,
542
+ "owned-by-action",
543
+ `/effects/${index}/actionRef`,
544
+ ),
545
+ );
546
+ this.graph.edges.forEach((edge, index) => {
547
+ pair(
548
+ "area",
549
+ edge.sourceAreaRef,
550
+ "outgoing-edge",
551
+ "edge",
552
+ edge.id,
553
+ "source-area",
554
+ `/edges/${index}/sourceAreaRef`,
555
+ );
556
+ pair(
557
+ "control",
558
+ edge.controlRef,
559
+ "emits-edge",
560
+ "edge",
561
+ edge.id,
562
+ "edge-control",
563
+ `/edges/${index}/controlRef`,
564
+ );
565
+ pair(
566
+ "action",
567
+ edge.actionRef,
568
+ "emits-edge",
569
+ "edge",
570
+ edge.id,
571
+ "edge-action",
572
+ `/edges/${index}/actionRef`,
573
+ );
574
+ if (!("status" in edge.target)) {
575
+ pair(
576
+ edge.target.kind === "area" ? "area" : "external",
577
+ edge.target.ref,
578
+ "incoming-edge",
579
+ "edge",
580
+ edge.id,
581
+ "edge-target",
582
+ `/edges/${index}/target/ref`,
583
+ );
584
+ }
585
+ });
586
+ this.graph.entryPoints.forEach((entry, entryIndex) => {
587
+ entry.rootAreaRefs.forEach((areaRef, rootIndex) =>
588
+ pair(
589
+ "entry",
590
+ entry.id,
591
+ "root-area",
592
+ "area",
593
+ areaRef,
594
+ "rooted-by-entry",
595
+ `/entryPoints/${entryIndex}/rootAreaRefs/${rootIndex}`,
596
+ ),
597
+ );
598
+ });
599
+ }
600
+ refineFingerprints() {
601
+ let structuralColors = new Map(
602
+ [...this.nodes].map(([key, node]) => [
603
+ key,
604
+ fingerprint({ entity: node.kind, intrinsic: node.intrinsic }),
605
+ ]),
606
+ );
607
+ // One round propagates meaning across one relationship. Using the entity
608
+ // count covers every simple path in this finite graph while keeping the
609
+ // same round count for ID-renamed isomorphic artifacts.
610
+ for (let round = 0; round < Math.max(1, this.nodes.size); round += 1) {
611
+ structuralColors = new Map(
612
+ [...this.nodes].map(([key, node]) => {
613
+ const neighbors = sortedValues(
614
+ (this.adjacency.get(key) ?? []).map((neighbor) => ({
615
+ relation: neighbor.relation,
616
+ fingerprint:
617
+ structuralColors.get(neighbor.targetKey) ??
618
+ "missing-semantic-neighbor",
619
+ })),
620
+ );
621
+ return [
622
+ key,
623
+ fingerprint({
624
+ entity: node.kind,
625
+ intrinsic: node.intrinsic,
626
+ neighbors,
627
+ }),
628
+ ];
629
+ }),
630
+ );
631
+ }
632
+ const semanticColors = new Map();
633
+ const intrinsic = (kind, id) => {
634
+ const node = this.nodes.get(entityKey(kind, id));
635
+ if (!node) {
636
+ throw new SemanticProjectionReferenceError(
637
+ "/",
638
+ `Missing ${kind} while constructing semantic references`,
639
+ );
640
+ }
641
+ return node.intrinsic;
642
+ };
643
+ const knownSemanticColor = (kind, id, path) => {
644
+ const color = semanticColors.get(entityKey(kind, id));
645
+ if (!color) {
646
+ throw new SemanticProjectionReferenceError(
647
+ path,
648
+ `Missing ${kind} while constructing semantic references`,
649
+ );
650
+ }
651
+ return color;
652
+ };
653
+ const selectColors = (kind, identities) => {
654
+ const bases = identities.map((identity) => ({
655
+ id: identity.id,
656
+ base: fingerprint({
657
+ entity: kind,
658
+ semanticIdentity: identity.value,
659
+ }),
660
+ }));
661
+ const frequencies = new Map();
662
+ for (const item of bases) {
663
+ frequencies.set(item.base, (frequencies.get(item.base) ?? 0) + 1);
664
+ }
665
+ for (const item of bases) {
666
+ const key = entityKey(kind, item.id);
667
+ const selected =
668
+ frequencies.get(item.base) === 1
669
+ ? item.base
670
+ : structuralColors.get(key);
671
+ if (!selected) {
672
+ throw new SemanticProjectionReferenceError(
673
+ "/",
674
+ `Missing structural ${kind} fingerprint`,
675
+ );
676
+ }
677
+ semanticColors.set(key, selected);
678
+ }
679
+ };
680
+ // Most UI entities have an unambiguous name/owner identity. Use that
681
+ // local identity so one mismatch yields a focused diff. Only collisions
682
+ // (for example, two same-name Areas) fall back to the graph-neighborhood
683
+ // color above, preventing them from being exchanged silently.
684
+ selectColors(
685
+ "area",
686
+ this.graph.areas.map((area) => ({
687
+ id: area.id,
688
+ value: intrinsic("area", area.id),
689
+ })),
690
+ );
691
+ selectColors(
692
+ "external",
693
+ this.graph.externalEndpoints.map((endpoint) => ({
694
+ id: endpoint.id,
695
+ value: intrinsic("external", endpoint.id),
696
+ })),
697
+ );
698
+ selectColors(
699
+ "control",
700
+ this.graph.controls.map((control, index) => ({
701
+ id: control.id,
702
+ value: {
703
+ intrinsic: intrinsic("control", control.id),
704
+ area: knownSemanticColor(
705
+ "area",
706
+ control.areaRef,
707
+ `/controls/${index}/areaRef`,
708
+ ),
709
+ },
710
+ })),
711
+ );
712
+ selectColors(
713
+ "action",
714
+ this.graph.actions.map((action, index) => ({
715
+ id: action.id,
716
+ value: {
717
+ intrinsic: intrinsic("action", action.id),
718
+ control: knownSemanticColor(
719
+ "control",
720
+ action.controlRef,
721
+ `/actions/${index}/controlRef`,
722
+ ),
723
+ },
724
+ })),
725
+ );
726
+ selectColors(
727
+ "effect",
728
+ this.graph.effects.map((effect, index) => ({
729
+ id: effect.id,
730
+ value: {
731
+ intrinsic: intrinsic("effect", effect.id),
732
+ action: knownSemanticColor(
733
+ "action",
734
+ effect.actionRef,
735
+ `/effects/${index}/actionRef`,
736
+ ),
737
+ },
738
+ })),
739
+ );
740
+ selectColors(
741
+ "entry",
742
+ this.graph.entryPoints.map((entry, index) => ({
743
+ id: entry.id,
744
+ value: {
745
+ intrinsic: intrinsic("entry", entry.id),
746
+ roots: sortedStrings(
747
+ entry.rootAreaRefs.map((areaRef, rootIndex) =>
748
+ knownSemanticColor(
749
+ "area",
750
+ areaRef,
751
+ `/entryPoints/${index}/rootAreaRefs/${rootIndex}`,
752
+ ),
753
+ ),
754
+ ),
755
+ },
756
+ })),
757
+ );
758
+ selectColors(
759
+ "edge",
760
+ this.graph.edges.map((edge, index) => ({
761
+ id: edge.id,
762
+ value: {
763
+ intrinsic: intrinsic("edge", edge.id),
764
+ sourceArea: knownSemanticColor(
765
+ "area",
766
+ edge.sourceAreaRef,
767
+ `/edges/${index}/sourceAreaRef`,
768
+ ),
769
+ control: knownSemanticColor(
770
+ "control",
771
+ edge.controlRef,
772
+ `/edges/${index}/controlRef`,
773
+ ),
774
+ action: knownSemanticColor(
775
+ "action",
776
+ edge.actionRef,
777
+ `/edges/${index}/actionRef`,
778
+ ),
779
+ target:
780
+ "status" in edge.target
781
+ ? { status: "unresolved" }
782
+ : {
783
+ status: "resolved",
784
+ kind: edge.target.kind,
785
+ fingerprint: knownSemanticColor(
786
+ edge.target.kind === "area" ? "area" : "external",
787
+ edge.target.ref,
788
+ `/edges/${index}/target/ref`,
789
+ ),
790
+ },
791
+ },
792
+ })),
793
+ );
794
+ return semanticColors;
795
+ }
796
+ reference(kind, id, path) {
797
+ const value = this.fingerprints.get(entityKey(kind, id));
798
+ if (!value) {
799
+ throw new SemanticProjectionReferenceError(
800
+ path,
801
+ `${kind} reference does not resolve in Area Graph`,
802
+ );
803
+ }
804
+ return { entity: kind, fingerprint: value };
805
+ }
806
+ ownerReference(ref, path) {
807
+ for (const kind of [
808
+ "area",
809
+ "control",
810
+ "action",
811
+ "effect",
812
+ "edge",
813
+ "external",
814
+ "entry",
815
+ ]) {
816
+ if (this.fingerprints.has(entityKey(kind, ref))) {
817
+ return this.reference(kind, ref, path);
818
+ }
819
+ }
820
+ const fact = this.sourceFactById.get(ref);
821
+ if (fact) {
822
+ return this.sourceFactReference(fact);
823
+ }
824
+ throw new SemanticProjectionReferenceError(
825
+ path,
826
+ "Unresolved owner does not resolve in Area Graph or Source Facts",
827
+ );
828
+ }
829
+ unresolvedLinks(refs, path) {
830
+ return sortedValues(
831
+ refs.map((ref, index) => {
832
+ const unresolved = this.unresolvedById.get(ref);
833
+ if (!unresolved) {
834
+ throw new SemanticProjectionReferenceError(
835
+ `${path}/${index}`,
836
+ "Unresolved reference does not resolve in Area Graph",
837
+ );
838
+ }
839
+ return {
840
+ entity: "unresolved",
841
+ fingerprint: fingerprint(
842
+ this.unresolvedSummary(
843
+ unresolved,
844
+ this.ownerReference(
845
+ unresolved.ownerRef,
846
+ `${path}/${index}/ownerRef`,
847
+ ),
848
+ ),
849
+ ),
850
+ };
851
+ }),
852
+ );
853
+ }
854
+ sourceFactReference(fact) {
855
+ return {
856
+ entity: "source-fact",
857
+ kind: fact.kind,
858
+ fingerprint: this.sourceFactFingerprint(fact),
859
+ };
860
+ }
861
+ sourceFactFingerprint(fact) {
862
+ return fingerprint({
863
+ kind: fact.kind,
864
+ data: semanticData(fact.data),
865
+ });
866
+ }
867
+ semanticEmbeddedValue(value) {
868
+ if (typeof value === "string") {
869
+ for (const kind of [
870
+ "area",
871
+ "control",
872
+ "action",
873
+ "effect",
874
+ "edge",
875
+ "external",
876
+ "entry",
877
+ ]) {
878
+ if (this.fingerprints.has(entityKey(kind, value))) {
879
+ return this.reference(kind, value, "/unresolved/candidate");
880
+ }
881
+ }
882
+ const fact = this.sourceFactById.get(value);
883
+ if (fact) return this.sourceFactReference(fact);
884
+ const unresolved = this.unresolvedById.get(value);
885
+ if (unresolved) {
886
+ return {
887
+ entity: "unresolved",
888
+ fieldPath: normalizeString(unresolved.fieldPath).replace(/^\/+/u, ""),
889
+ reasonCode: normalizeString(unresolved.reasonCode),
890
+ };
891
+ }
892
+ return this.semanticReferenceText(value);
893
+ }
894
+ if (value === null) return null;
895
+ if (typeof value === "boolean" || typeof value === "number") {
896
+ return normalizeJson(value);
897
+ }
898
+ if (Array.isArray(value)) {
899
+ return value.map((item) => this.semanticEmbeddedValue(item));
900
+ }
901
+ if (typeof value === "object") {
902
+ const result = {};
903
+ for (const key of Object.keys(value).sort()) {
904
+ const child = value[key];
905
+ if (child !== undefined) {
906
+ result[key] = this.semanticEmbeddedValue(child);
907
+ }
908
+ }
909
+ return result;
910
+ }
911
+ return normalizeJson(value);
912
+ }
913
+ semanticReferenceText(value) {
914
+ let normalized = normalizeString(value);
915
+ const replacements = [];
916
+ for (const kind of [
917
+ "area",
918
+ "control",
919
+ "action",
920
+ "effect",
921
+ "edge",
922
+ "external",
923
+ "entry",
924
+ ]) {
925
+ for (const node of this.nodes.values()) {
926
+ if (node.kind !== kind) continue;
927
+ const color = this.fingerprints.get(entityKey(kind, node.id));
928
+ if (color) {
929
+ replacements.push([node.id, `<semantic-${kind}:${color}>`]);
930
+ }
931
+ }
932
+ }
933
+ for (const fact of this.source.facts) {
934
+ replacements.push([
935
+ fact.id,
936
+ `<semantic-source-fact:${this.sourceFactFingerprint(fact)}>`,
937
+ ]);
938
+ }
939
+ for (const item of this.graph.unresolved) {
940
+ replacements.push([
941
+ item.id,
942
+ `<semantic-unresolved:${normalizeString(item.reasonCode)}:` +
943
+ `${normalizeString(item.fieldPath).replace(/^\/+/u, "")}>`,
944
+ ]);
945
+ }
946
+ replacements.sort((left, right) => right[0].length - left[0].length);
947
+ for (const [producerId, semanticRef] of replacements) {
948
+ normalized = normalized.split(producerId).join(semanticRef);
949
+ }
950
+ return normalized;
951
+ }
952
+ unresolvedSummary(item, owner) {
953
+ return {
954
+ owner,
955
+ fieldPath: normalizeString(item.fieldPath).replace(/^\/+/u, ""),
956
+ reasonCode: normalizeString(item.reasonCode),
957
+ ...(this.scope === "complete"
958
+ ? {
959
+ message: this.semanticReferenceText(item.message),
960
+ candidates: sortedValues(
961
+ item.candidates.map((candidate) => ({
962
+ value: this.semanticEmbeddedValue(candidate.value),
963
+ ...(candidate.note === undefined
964
+ ? {}
965
+ : {
966
+ note: this.semanticReferenceText(candidate.note),
967
+ }),
968
+ })),
969
+ ),
970
+ impact: sortedStrings(
971
+ item.impact.map((impact) => this.semanticReferenceText(impact)),
972
+ ),
973
+ }
974
+ : {}),
975
+ };
976
+ }
977
+ areaIntrinsic(area, path) {
978
+ return {
979
+ kind: normalizeString(area.kind),
980
+ name: supportedValue(area.name, this.scope),
981
+ conditions: conditionValues(
982
+ area.conditions,
983
+ this.sourceFactById,
984
+ this.scope,
985
+ `${path}/conditions`,
986
+ ),
987
+ ...(this.scope === "complete"
988
+ ? {
989
+ descriptionFacts: sortedValues(
990
+ area.descriptionFacts.map((description) => ({
991
+ kind: normalizeString(description.kind),
992
+ value: supportedValue(description.value, this.scope),
993
+ })),
994
+ ),
995
+ }
996
+ : {}),
997
+ };
998
+ }
999
+ controlIntrinsic(control, path) {
1000
+ return {
1001
+ kind: normalizeString(control.kind),
1002
+ name: supportedValue(control.name, this.scope),
1003
+ enabled:
1004
+ control.enabled === undefined
1005
+ ? null
1006
+ : supportedValue(control.enabled, this.scope),
1007
+ visibilityConditions: conditionValues(
1008
+ control.visibilityConditions,
1009
+ this.sourceFactById,
1010
+ this.scope,
1011
+ `${path}/visibilityConditions`,
1012
+ ),
1013
+ enabledConditions: conditionValues(
1014
+ control.enabledConditions,
1015
+ this.sourceFactById,
1016
+ this.scope,
1017
+ `${path}/enabledConditions`,
1018
+ ),
1019
+ };
1020
+ }
1021
+ actionIntrinsic(action, path) {
1022
+ return {
1023
+ trigger: normalizeString(action.trigger),
1024
+ preconditions: conditionValues(
1025
+ action.preconditionRefs,
1026
+ this.sourceFactById,
1027
+ this.scope,
1028
+ `${path}/preconditionRefs`,
1029
+ ),
1030
+ preconditionAlternatives: sortedValues(
1031
+ (action.preconditionAlternatives ?? []).map((alternative, index) =>
1032
+ conditionValues(
1033
+ alternative.conditionRefs,
1034
+ this.sourceFactById,
1035
+ this.scope,
1036
+ `${path}/preconditionAlternatives/${index}/conditionRefs`,
1037
+ ),
1038
+ ),
1039
+ ),
1040
+ };
1041
+ }
1042
+ effectIntrinsic(effect, path) {
1043
+ return {
1044
+ kind: normalizeString(effect.kind),
1045
+ conditionAlternatives: sortedValues(
1046
+ (effect.conditionAlternatives ?? []).map((alternative, index) =>
1047
+ conditionValues(
1048
+ alternative.conditionRefs,
1049
+ this.sourceFactById,
1050
+ this.scope,
1051
+ `${path}/conditionAlternatives/${index}/conditionRefs`,
1052
+ ),
1053
+ ),
1054
+ ),
1055
+ stateKey:
1056
+ effect.stateKey === undefined
1057
+ ? null
1058
+ : supportedValue(effect.stateKey, this.scope),
1059
+ value:
1060
+ effect.value === undefined
1061
+ ? null
1062
+ : supportedValue(effect.value, this.scope),
1063
+ ...(this.scope === "complete"
1064
+ ? { description: supportedValue(effect.description, this.scope) }
1065
+ : {}),
1066
+ };
1067
+ }
1068
+ edgeIntrinsic(edge, path) {
1069
+ return {
1070
+ conditions: conditionValues(
1071
+ edge.conditions,
1072
+ this.sourceFactById,
1073
+ this.scope,
1074
+ `${path}/conditions`,
1075
+ ),
1076
+ preconditions: conditionValues(
1077
+ edge.preconditions,
1078
+ this.sourceFactById,
1079
+ this.scope,
1080
+ `${path}/preconditions`,
1081
+ ),
1082
+ conditionAlternatives: sortedValues(
1083
+ (edge.conditionAlternatives ?? []).map((alternative, index) =>
1084
+ conditionValues(
1085
+ alternative.conditionRefs,
1086
+ this.sourceFactById,
1087
+ this.scope,
1088
+ `${path}/conditionAlternatives/${index}/conditionRefs`,
1089
+ ),
1090
+ ),
1091
+ ),
1092
+ targetStatus: "status" in edge.target ? "unresolved" : edge.target.kind,
1093
+ };
1094
+ }
1095
+ externalIntrinsic(endpoint) {
1096
+ return {
1097
+ kind: normalizeString(endpoint.kind),
1098
+ name: supportedValue(endpoint.name, this.scope),
1099
+ presentation: endpoint.presentation ?? null,
1100
+ intentAction:
1101
+ endpoint.intentAction === undefined
1102
+ ? null
1103
+ : supportedValue(endpoint.intentAction, this.scope),
1104
+ mimeType:
1105
+ endpoint.mimeType === undefined
1106
+ ? null
1107
+ : supportedValue(endpoint.mimeType, this.scope),
1108
+ uri:
1109
+ endpoint.uri === undefined
1110
+ ? null
1111
+ : supportedValue(endpoint.uri, this.scope),
1112
+ };
1113
+ }
1114
+ entryIntrinsic(entry, path) {
1115
+ return {
1116
+ kind: normalizeString(entry.kind),
1117
+ category: entry.category,
1118
+ startupRole: entry.startupRole ?? null,
1119
+ conditions: conditionValues(
1120
+ entry.conditions,
1121
+ this.sourceFactById,
1122
+ this.scope,
1123
+ `${path}/conditions`,
1124
+ ),
1125
+ preconditions: conditionValues(
1126
+ entry.preconditions ?? entry.conditions,
1127
+ this.sourceFactById,
1128
+ this.scope,
1129
+ `${path}/preconditions`,
1130
+ ),
1131
+ };
1132
+ }
1133
+ pathRecord(pathValue, path) {
1134
+ return {
1135
+ kind: pathValue.kind,
1136
+ reachability: pathValue.reachability,
1137
+ targetArea: this.reference(
1138
+ "area",
1139
+ pathValue.targetAreaRef,
1140
+ `${path}/targetAreaRef`,
1141
+ ),
1142
+ rootEntry:
1143
+ pathValue.rootEntryRef === undefined
1144
+ ? null
1145
+ : this.reference(
1146
+ "entry",
1147
+ pathValue.rootEntryRef,
1148
+ `${path}/rootEntryRef`,
1149
+ ),
1150
+ preconditions: conditionValues(
1151
+ pathValue.preconditions,
1152
+ this.sourceFactById,
1153
+ this.scope,
1154
+ `${path}/preconditions`,
1155
+ ),
1156
+ steps: pathValue.steps.map((step, index) => ({
1157
+ edge: this.reference(
1158
+ "edge",
1159
+ step.edgeRef,
1160
+ `${path}/steps/${index}/edgeRef`,
1161
+ ),
1162
+ sourceArea: this.reference(
1163
+ "area",
1164
+ step.sourceAreaRef,
1165
+ `${path}/steps/${index}/sourceAreaRef`,
1166
+ ),
1167
+ control: this.reference(
1168
+ "control",
1169
+ step.controlRef,
1170
+ `${path}/steps/${index}/controlRef`,
1171
+ ),
1172
+ action: this.reference(
1173
+ "action",
1174
+ step.actionRef,
1175
+ `${path}/steps/${index}/actionRef`,
1176
+ ),
1177
+ targetArea: this.reference(
1178
+ "area",
1179
+ step.targetAreaRef,
1180
+ `${path}/steps/${index}/targetAreaRef`,
1181
+ ),
1182
+ })),
1183
+ ...(this.scope === "complete"
1184
+ ? {
1185
+ selectionReasons: sortedStrings(pathValue.selectionReasons),
1186
+ }
1187
+ : {}),
1188
+ unresolved: this.unresolvedLinks(
1189
+ pathValue.unresolvedRefs,
1190
+ `${path}/unresolvedRefs`,
1191
+ ),
1192
+ };
1193
+ }
1194
+ pathReference(id, pathById, path) {
1195
+ const record = pathById.get(id);
1196
+ if (!record) {
1197
+ throw new SemanticProjectionReferenceError(
1198
+ path,
1199
+ "Canonical path reference does not resolve in Area Graph",
1200
+ );
1201
+ }
1202
+ return { entity: "path", fingerprint: fingerprint(record) };
1203
+ }
1204
+ }
1205
+ /**
1206
+ * Removes producer IDs, evidence/support references and raw identity anchors,
1207
+ * then reconstructs references from semantic fingerprints. Array
1208
+ * multiplicity is retained and path steps remain ordered.
1209
+ *
1210
+ * This is intentionally not a replacement for schema/reference validation or
1211
+ * byte-for-byte GT compatibility. Validate each side with the validator that
1212
+ * owns that artifact version before comparing. The comparator itself checks
1213
+ * every reference it must dereference, without imposing newer policy
1214
+ * invariants on a frozen older oracle.
1215
+ */
1216
+ export function projectAreaGraphSemantics(input, options = {}) {
1217
+ const scope = options.scope ?? "complete";
1218
+ if (input.areaGraph.caseId !== input.sourceFacts.caseId) {
1219
+ throw new SemanticProjectionReferenceError(
1220
+ "/caseId",
1221
+ "Area Graph and Source Facts caseId differ",
1222
+ );
1223
+ }
1224
+ return new Projector(input, scope).project();
1225
+ }
1226
+ function summarized(value) {
1227
+ const serialized = stableStringify(value);
1228
+ return serialized.length <= 800
1229
+ ? serialized
1230
+ : `${serialized.slice(0, 797)}...`;
1231
+ }
1232
+ function compareCollection(expected, actual) {
1233
+ const frequencies = (values) => {
1234
+ const result = new Map();
1235
+ for (const value of values) {
1236
+ const key = stableStringify(value);
1237
+ const prior = result.get(key);
1238
+ result.set(key, {
1239
+ value,
1240
+ count: (prior?.count ?? 0) + 1,
1241
+ });
1242
+ }
1243
+ return result;
1244
+ };
1245
+ const expectedByValue = frequencies(expected);
1246
+ const actualByValue = frequencies(actual);
1247
+ let differenceCount = 0;
1248
+ const missingRecords = [];
1249
+ const unexpectedRecords = [];
1250
+ for (const [key, expectedEntry] of expectedByValue) {
1251
+ const actualCount = actualByValue.get(key)?.count ?? 0;
1252
+ const missing = Math.max(0, expectedEntry.count - actualCount);
1253
+ if (missing === 0) continue;
1254
+ differenceCount += missing;
1255
+ missingRecords.push({ value: expectedEntry.value, count: missing });
1256
+ }
1257
+ for (const [key, actualEntry] of actualByValue) {
1258
+ const expectedCount = expectedByValue.get(key)?.count ?? 0;
1259
+ const unexpected = Math.max(0, actualEntry.count - expectedCount);
1260
+ if (unexpected === 0) continue;
1261
+ differenceCount += unexpected;
1262
+ unexpectedRecords.push({
1263
+ value: actualEntry.value,
1264
+ count: unexpected,
1265
+ });
1266
+ }
1267
+ return {
1268
+ differenceCount,
1269
+ expectedCount: expected.length,
1270
+ actualCount: actual.length,
1271
+ missing: missingRecords,
1272
+ unexpected: unexpectedRecords,
1273
+ };
1274
+ }
1275
+ /**
1276
+ * Compares two graph/path artifacts without requiring their declared IDs to
1277
+ * match. A result can pass only when every projected record and its
1278
+ * multiplicity match; equal entity counts alone are insufficient.
1279
+ */
1280
+ export function compareAreaGraphSemantics(expected, actual, options = {}) {
1281
+ const issues = [];
1282
+ const maxIssues = options.maxIssues ?? 200;
1283
+ if (!Number.isSafeInteger(maxIssues) || maxIssues <= 0) {
1284
+ throw new RangeError("maxIssues must be a positive safe integer");
1285
+ }
1286
+ for (const [side, input] of [
1287
+ ["expected", expected],
1288
+ ["actual", actual],
1289
+ ]) {
1290
+ if (input.areaGraph.caseId !== input.sourceFacts.caseId) {
1291
+ issues.push(
1292
+ semanticIssue(
1293
+ "SEMANTIC_CASE_MISMATCH",
1294
+ `/${side}/caseId`,
1295
+ "Area Graph and Source Facts caseId differ",
1296
+ ),
1297
+ );
1298
+ }
1299
+ }
1300
+ if (issues.length > 0) {
1301
+ return { ok: false, issues };
1302
+ }
1303
+ let expectedProjection;
1304
+ let actualProjection;
1305
+ try {
1306
+ expectedProjection = projectAreaGraphSemantics(expected, options);
1307
+ } catch (error) {
1308
+ if (error instanceof SemanticProjectionReferenceError) {
1309
+ return {
1310
+ ok: false,
1311
+ issues: [
1312
+ semanticIssue(
1313
+ "SEMANTIC_EXPECTED_PROJECTION_INVALID",
1314
+ `/expected${error.path}`,
1315
+ error.message,
1316
+ ),
1317
+ ],
1318
+ };
1319
+ }
1320
+ throw error;
1321
+ }
1322
+ try {
1323
+ actualProjection = projectAreaGraphSemantics(actual, options);
1324
+ } catch (error) {
1325
+ if (error instanceof SemanticProjectionReferenceError) {
1326
+ return {
1327
+ ok: false,
1328
+ issues: [
1329
+ semanticIssue(
1330
+ "SEMANTIC_ACTUAL_PROJECTION_INVALID",
1331
+ `/actual${error.path}`,
1332
+ error.message,
1333
+ ),
1334
+ ],
1335
+ };
1336
+ }
1337
+ throw error;
1338
+ }
1339
+ const collections = [
1340
+ "areas",
1341
+ "controls",
1342
+ "actions",
1343
+ "effects",
1344
+ "edges",
1345
+ "externalEndpoints",
1346
+ "entryPoints",
1347
+ "paths",
1348
+ "unresolved",
1349
+ ];
1350
+ const collectionResults = collections.map((collection) => ({
1351
+ collection,
1352
+ difference: compareCollection(
1353
+ expectedProjection[collection],
1354
+ actualProjection[collection],
1355
+ ),
1356
+ }));
1357
+ const differenceCount = collectionResults.reduce(
1358
+ (sum, result) => sum + result.difference.differenceCount,
1359
+ 0,
1360
+ );
1361
+ const differingCollections = collectionResults.filter(
1362
+ (result) => result.difference.differenceCount > 0,
1363
+ );
1364
+ const desiredDiagnostics = differingCollections.reduce(
1365
+ (sum, result) =>
1366
+ sum +
1367
+ 1 +
1368
+ result.difference.missing.length +
1369
+ result.difference.unexpected.length,
1370
+ 0,
1371
+ );
1372
+ const truncated = desiredDiagnostics > maxIssues;
1373
+ const detailLimit = truncated && maxIssues > 1 ? maxIssues - 1 : maxIssues;
1374
+ // Emit one summary for every collection before record examples. Even a
1375
+ // large Area mismatch therefore cannot hide whether paths also differ.
1376
+ for (const { collection, difference } of differingCollections) {
1377
+ if (issues.length >= detailLimit) break;
1378
+ const missingCount = difference.missing.reduce(
1379
+ (sum, item) => sum + item.count,
1380
+ 0,
1381
+ );
1382
+ const unexpectedCount = difference.unexpected.reduce(
1383
+ (sum, item) => sum + item.count,
1384
+ 0,
1385
+ );
1386
+ issues.push(
1387
+ semanticIssue(
1388
+ "SEMANTIC_COLLECTION_DIFFERENT",
1389
+ `/${collection}`,
1390
+ `Expected ${difference.expectedCount}, actual ${difference.actualCount}; ` +
1391
+ `missing ${missingCount}, unexpected ${unexpectedCount}`,
1392
+ ),
1393
+ );
1394
+ }
1395
+ for (const { collection, difference } of differingCollections) {
1396
+ for (const item of difference.missing) {
1397
+ if (issues.length >= detailLimit) break;
1398
+ issues.push(
1399
+ semanticIssue(
1400
+ "SEMANTIC_MISSING",
1401
+ `/${collection}`,
1402
+ `Missing ${item.count} semantic record(s): ${summarized(item.value)}`,
1403
+ ),
1404
+ );
1405
+ }
1406
+ for (const item of difference.unexpected) {
1407
+ if (issues.length >= detailLimit) break;
1408
+ issues.push(
1409
+ semanticIssue(
1410
+ "SEMANTIC_UNEXPECTED",
1411
+ `/${collection}`,
1412
+ `Unexpected ${item.count} semantic record(s): ${summarized(item.value)}`,
1413
+ ),
1414
+ );
1415
+ }
1416
+ if (issues.length >= detailLimit) break;
1417
+ }
1418
+ if (truncated && maxIssues > 1) {
1419
+ issues.push(
1420
+ semanticIssue(
1421
+ "SEMANTIC_DIAGNOSTICS_TRUNCATED",
1422
+ "/",
1423
+ "Additional semantic differences were omitted by maxIssues",
1424
+ ),
1425
+ );
1426
+ }
1427
+ return {
1428
+ ok: differenceCount === 0,
1429
+ issues,
1430
+ };
1431
+ }