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,109 @@
1
+ import { resolve } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { runAgentStage2 } from "./agent-render/index.js";
4
+ export { loadStage2Input } from "./stage2-input.js";
5
+ export { loadStage2NameTraceabilityInput } from "./stage2-name-trace-input.js";
6
+ export { parseAgentArguments, runAgentStage2 } from "./agent-render/index.js";
7
+ export {
8
+ buildSpecInputBatches,
9
+ exportSpecHandoff,
10
+ parseStage2ReqText,
11
+ SPEC_HANDOFF_CONTRACT_VERSION,
12
+ SPEC_INPUT_FORMAT,
13
+ validateSpecInputText,
14
+ } from "./spec-handoff.js";
15
+ export const STAGE2_USAGE = `Usage (default, --mode subagent, ADR-0005):
16
+ pnpm stage2 -- --input <successful-stage1-run-directory> \\
17
+ --project-root <android-project> [--output <directory>] \\
18
+ [--per-feature] [--with-unreachable-area] [--spec-output <directory>]
19
+
20
+ Writes rendering/<unit>/{task.md,expected.json} for every renderable Area
21
+ (or Feature, with --per-feature) and prints a manifest — it does NOT spawn
22
+ any backend. Turning each manifest entry into a rendering/<unit>/output.json
23
+ is done by dispatching the Claude Code Agent tool outside this process; see
24
+ ../references/subagent-render.md for the full protocol
25
+ (self-check via scripts/reqgen-selfcheck.mjs, retry/skip/abort policy).
26
+
27
+ Usage (--mode shell):
28
+ pnpm stage2 -- --mode shell --input <successful-stage1-run-directory> \\
29
+ --project-root <android-project> \\
30
+ [--backend claude|opencode] [--agent-name reqgen-renderer] \\
31
+ [--ai-command <executable>] [--concurrency 8] \\
32
+ [--max-retries 5] [--backoff-ms 5000] [--output <directory> | --resume <directory>] \\
33
+ [--per-feature] [--with-unreachable-area] [--spec-output <directory>]
34
+
35
+ Renders REQ prose by spawning a backend (claude, opencode, or --ai-command)
36
+ per unit, then writes BOTH req.md (markdown) and per-area req-{idx}.txt
37
+ (plain text, no markdown) into the output directory. --backend defaults to
38
+ "claude" (the \`claude\` CLI's own -p/--print headless mode, dispatched with
39
+ --agent <agent-name> so it picks up the same .claude/agents/<agent-name>.md
40
+ config the default subagent mode uses). The default output directory is
41
+ <input>/generation.
42
+
43
+ The default Claude backend uses the unified protocol (task.md +
44
+ self-check/finalize via reqgen-selfcheck.mjs); custom --ai-command and
45
+ opencode use the inline-prompt path. Use --mode shell-legacy to force the
46
+ old inline-prompt path for Claude as well.
47
+
48
+ --per-feature render one session per Feature instead of the
49
+ default one session per Area.
50
+ --with-unreachable-area keep Areas judged unreachable in output (sunk
51
+ after every reachable Area) instead of dropping
52
+ them. In per-feature mode the judgment falls back
53
+ to static analysis (canonicalPath.status).
54
+ --resume <directory> resume --mode shell from an existing output directory;
55
+ finalized units are reused and unfinished units rerun.
56
+
57
+ Usage (--mode shell-legacy):
58
+ pnpm stage2 -- --mode shell-legacy --input <successful-stage1-run-directory> \\
59
+ --project-root <android-project> \\
60
+ [--backend claude|opencode] [--agent-name reqgen-renderer] \\
61
+ [--ai-command <executable>] [--concurrency 8] \\
62
+ [--max-retries 5] [--backoff-ms 5000] [--output <directory>] \\
63
+ [--per-feature] [--with-unreachable-area] [--spec-output <directory>]
64
+
65
+ Pre-normalize shell interaction: ALL backends (including claude) use the
66
+ inline-prompt path — the full prompt is passed on stdin, JSON is parsed
67
+ from stdout, and validation/retry is done by this process. Unlike --mode
68
+ shell (which uses task.md + self-check/finalize for the claude backend),
69
+ this mode does NOT delegate validation to the subprocess. Same output as
70
+ unified self-check protocol is not available in the target environment.
71
+
72
+ Usage (either mode):
73
+ pnpm stage2 -- --input <run-directory> --snapshot \\
74
+ [--output <directory>] [--per-feature] [--with-unreachable-area] \\
75
+ [--spec-output <directory>]
76
+
77
+ --snapshot merges already-persisted rendering results offline into both
78
+ a timestamped req-<timestamp>.md and per-area req-{idx}.txt files, without
79
+ contacting any backend.
80
+
81
+ --spec-output <directory> additionally writes page-local
82
+ <batch>/REQ.txt files and manifest.json for $a2h-spec-generate after the
83
+ Stage 2 merge. Each batch contains one Area/page boundary; the option may
84
+ be used with shell rendering or --snapshot. It is rejected during the
85
+ subagent preparation-only phase because no finalized output exists yet.
86
+
87
+ When --output is explicitly set (all modes except --snapshot), the target
88
+ directory must be empty or non-existent. Use --resume <directory> with
89
+ --mode shell to continue an interrupted run; finalized output.json files
90
+ are reused and the remaining units are rendered. --snapshot is exempt
91
+ because it must read existing output.json. Default paths are never guarded.
92
+ `;
93
+ const defaultIo = {
94
+ stdout: (text) => process.stdout.write(text),
95
+ stderr: (text) => process.stderr.write(text),
96
+ };
97
+ export async function runStage2Cli(argv, io = defaultIo) {
98
+ if (argv.includes("--help") || argv.includes("-h")) {
99
+ io.stdout(STAGE2_USAGE);
100
+ return 0;
101
+ }
102
+ return runAgentStage2(argv, io);
103
+ }
104
+ const isDirectExecution =
105
+ process.argv[1] !== undefined &&
106
+ resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
107
+ if (isDirectExecution) {
108
+ process.exitCode = await runStage2Cli(process.argv.slice(2));
109
+ }
@@ -0,0 +1,419 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import {
4
+ GAP_AUDIT_CONTRACT_VERSION,
5
+ semanticCompletenessValues,
6
+ } from "./contracts/index.js";
7
+ import { sha256Text } from "./pipeline/output.js";
8
+ export class GenerationError extends Error {
9
+ code;
10
+ status;
11
+ details;
12
+ constructor(code, message, options = {}) {
13
+ super(message, { cause: options.cause });
14
+ this.name = "GenerationError";
15
+ this.code = code;
16
+ this.status = options.status;
17
+ this.details = options.details;
18
+ }
19
+ }
20
+ export function isRecord(value) {
21
+ return value !== null && typeof value === "object" && !Array.isArray(value);
22
+ }
23
+ export function parseJson(text, filename) {
24
+ try {
25
+ return JSON.parse(text);
26
+ } catch {
27
+ throw new GenerationError(
28
+ "STAGE2_INPUT_JSON_INVALID",
29
+ `${filename} is not valid JSON`,
30
+ );
31
+ }
32
+ }
33
+ async function readInputFiles(inputDirectory) {
34
+ try {
35
+ const [reportText, reqModelText] = await Promise.all([
36
+ readFile(join(inputDirectory, "run-report.json"), "utf8"),
37
+ readFile(join(inputDirectory, "req-model.json"), "utf8"),
38
+ ]);
39
+ return { reportText, reqModelText };
40
+ } catch (error) {
41
+ throw new GenerationError(
42
+ "STAGE2_INPUT_MISSING",
43
+ "Stage 2 requires run-report.json and req-model.json from one Stage 1 run",
44
+ { cause: error },
45
+ );
46
+ }
47
+ }
48
+ function completenessOf(value) {
49
+ if (!isRecord(value) || !Object.hasOwn(value, "semanticCompleteness")) {
50
+ return undefined;
51
+ }
52
+ return typeof value.semanticCompleteness === "string"
53
+ ? value.semanticCompleteness
54
+ : undefined;
55
+ }
56
+ function assertReportCompleteness(reportValue) {
57
+ const reportCompleteness = completenessOf(reportValue);
58
+ const invalidReport =
59
+ Object.hasOwn(reportValue, "semanticCompleteness") &&
60
+ (reportCompleteness === undefined ||
61
+ !semanticCompletenessValues.includes(reportCompleteness));
62
+ const inconsistentReport =
63
+ (reportValue.publishable === false &&
64
+ reportCompleteness !== "incomplete-known") ||
65
+ (reportValue.publishable === true &&
66
+ reportCompleteness === "incomplete-known");
67
+ if (invalidReport || inconsistentReport) {
68
+ throw new GenerationError(
69
+ "STAGE2_INPUT_QUALITY_STATE_INVALID",
70
+ "run-report.json has an inconsistent publishable/semanticCompleteness state",
71
+ );
72
+ }
73
+ }
74
+ function assertReqModelCompleteness(reportValue, reqModelValue) {
75
+ const reqModelCompleteness = completenessOf(reqModelValue);
76
+ const invalidReqModel =
77
+ Object.hasOwn(reqModelValue, "semanticCompleteness") &&
78
+ (reqModelCompleteness === undefined ||
79
+ !semanticCompletenessValues.includes(reqModelCompleteness));
80
+ if (
81
+ invalidReqModel ||
82
+ (reportValue.publishable === true &&
83
+ reqModelCompleteness === "incomplete-known")
84
+ ) {
85
+ throw new GenerationError(
86
+ "STAGE2_INPUT_QUALITY_STATE_INVALID",
87
+ "req-model.json has an inconsistent semanticCompleteness state",
88
+ );
89
+ }
90
+ }
91
+ function assertReportReady(reportValue) {
92
+ if (
93
+ !isRecord(reportValue) ||
94
+ reportValue.status !== "succeeded" ||
95
+ (reportValue.publishable !== true && reportValue.publishable !== false) ||
96
+ reportValue.completedStage !== "validate" ||
97
+ !Array.isArray(reportValue.artifacts)
98
+ ) {
99
+ throw new GenerationError(
100
+ "STAGE2_INPUT_NOT_READY",
101
+ "run-report.json does not describe a successful, validated Stage 1 run",
102
+ );
103
+ }
104
+ }
105
+ function assertReqModelEnvelope(reqModelValue) {
106
+ if (
107
+ !isRecord(reqModelValue) ||
108
+ reqModelValue.schemaVersion !== "1.0.0" ||
109
+ reqModelValue.artifactKind !== "req-model" ||
110
+ typeof reqModelValue.caseId !== "string" ||
111
+ !isRecord(reqModelValue.analysisContext) ||
112
+ !Array.isArray(reqModelValue.areas) ||
113
+ !Array.isArray(reqModelValue.unresolved)
114
+ ) {
115
+ throw new GenerationError(
116
+ "STAGE2_REQ_MODEL_INVALID",
117
+ "req-model.json has an unsupported envelope",
118
+ );
119
+ }
120
+ }
121
+ function assertReqModelHash(reportValue, reqModelText) {
122
+ const reqArtifact = reportValue.artifacts.find(
123
+ (artifact) =>
124
+ isRecord(artifact) &&
125
+ artifact.kind === "req-model" &&
126
+ artifact.relativePath === "req-model.json",
127
+ );
128
+ if (
129
+ !isRecord(reqArtifact) ||
130
+ typeof reqArtifact.sha256 !== "string" ||
131
+ reqArtifact.sha256 !== sha256Text(reqModelText)
132
+ ) {
133
+ throw new GenerationError(
134
+ "STAGE2_REQ_MODEL_HASH_MISMATCH",
135
+ "req-model.json does not match the artifact hash in run-report.json",
136
+ );
137
+ }
138
+ }
139
+ function assertSameContext(report, reqModel) {
140
+ if (
141
+ report.caseId !== reqModel.caseId ||
142
+ report.target === undefined ||
143
+ report.target.applicationModule !==
144
+ reqModel.analysisContext.applicationModule ||
145
+ report.target.buildVariant !== reqModel.analysisContext.buildVariant ||
146
+ JSON.stringify(report.target.resourceConfiguration) !==
147
+ JSON.stringify(reqModel.analysisContext.resourceConfiguration)
148
+ ) {
149
+ throw new GenerationError(
150
+ "STAGE2_INPUT_CONTEXT_MISMATCH",
151
+ "run-report.json and req-model.json do not describe the same analysis context",
152
+ );
153
+ }
154
+ }
155
+ function assertAreaGraphEnvelope(graphValue) {
156
+ if (
157
+ !isRecord(graphValue) ||
158
+ graphValue.schemaVersion !== "1.0.0" ||
159
+ graphValue.artifactKind !== "area-graph" ||
160
+ typeof graphValue.caseId !== "string" ||
161
+ !isRecord(graphValue.analysisContext) ||
162
+ !Array.isArray(graphValue.areas) ||
163
+ !Array.isArray(graphValue.controls) ||
164
+ !Array.isArray(graphValue.unresolved)
165
+ ) {
166
+ throw new GenerationError(
167
+ "STAGE2_AREA_GRAPH_INVALID",
168
+ "area-graph.json has an unsupported envelope",
169
+ );
170
+ }
171
+ }
172
+ function assertAreaGraphHash(report, graphText) {
173
+ const graphArtifact = report.artifacts.find(
174
+ (artifact) =>
175
+ artifact.kind === "area-graph" &&
176
+ artifact.relativePath === "area-graph.json",
177
+ );
178
+ if (
179
+ graphArtifact === undefined ||
180
+ graphArtifact.sha256 !== sha256Text(graphText)
181
+ ) {
182
+ throw new GenerationError(
183
+ "STAGE2_AREA_GRAPH_HASH_MISMATCH",
184
+ "area-graph.json does not match the artifact hash in run-report.json",
185
+ );
186
+ }
187
+ }
188
+ function assertAreaGraphContext(graph, reqModel) {
189
+ if (
190
+ graph.caseId !== reqModel.caseId ||
191
+ JSON.stringify(graph.analysisContext) !==
192
+ JSON.stringify(reqModel.analysisContext)
193
+ ) {
194
+ throw new GenerationError(
195
+ "STAGE2_AREA_GRAPH_CONTEXT_MISMATCH",
196
+ "area-graph.json does not match req-model.json",
197
+ );
198
+ }
199
+ }
200
+ /** Required alongside req-model.json to recompute the Stage 1 deterministic
201
+ * draft (renderReqDraft) as the merge template — recomputing instead of
202
+ * reading generation/req.md keeps the template hash-verified against
203
+ * the same req-model instead of trusting a plain text file on disk. */
204
+ export async function loadStage2AreaGraph(inputDirectory, report, reqModel) {
205
+ let graphText;
206
+ try {
207
+ graphText = await readFile(join(inputDirectory, "area-graph.json"), "utf8");
208
+ } catch (error) {
209
+ throw new GenerationError(
210
+ "STAGE2_AREA_GRAPH_MISSING",
211
+ "Stage 2 requires area-graph.json from the same Stage 1 run",
212
+ { cause: error },
213
+ );
214
+ }
215
+ const graphValue = parseJson(graphText, "area-graph.json");
216
+ assertAreaGraphEnvelope(graphValue);
217
+ assertAreaGraphHash(report, graphText);
218
+ const graph = graphValue;
219
+ assertAreaGraphContext(graph, reqModel);
220
+ return graph;
221
+ }
222
+ function assertGapAuditEnvelope(gapAuditValue) {
223
+ if (
224
+ !isRecord(gapAuditValue) ||
225
+ gapAuditValue.contractVersion !== GAP_AUDIT_CONTRACT_VERSION ||
226
+ gapAuditValue.artifactKind !== "gap-audit" ||
227
+ typeof gapAuditValue.caseId !== "string" ||
228
+ !Array.isArray(gapAuditValue.categories) ||
229
+ !isRecord(gapAuditValue.summary) ||
230
+ !semanticCompletenessValues.includes(gapAuditValue.semanticCompleteness) ||
231
+ !Array.isArray(gapAuditValue.semanticLimitations)
232
+ ) {
233
+ throw new GenerationError(
234
+ "STAGE2_GAP_AUDIT_INVALID",
235
+ "gap-audit.json has an unsupported envelope",
236
+ );
237
+ }
238
+ }
239
+ function assertGapAuditHash(report, gapAuditText) {
240
+ const gapAuditArtifact = report.artifacts.find(
241
+ (artifact) =>
242
+ artifact.kind === "gap-audit" &&
243
+ artifact.relativePath === "gap-audit.json",
244
+ );
245
+ if (
246
+ gapAuditArtifact === undefined ||
247
+ gapAuditArtifact.sha256 !== sha256Text(gapAuditText)
248
+ ) {
249
+ throw new GenerationError(
250
+ "STAGE2_GAP_AUDIT_HASH_MISMATCH",
251
+ "gap-audit.json does not match the artifact hash in run-report.json",
252
+ );
253
+ }
254
+ }
255
+ function assertGapAuditContext(gapAudit, reqModel) {
256
+ if (gapAudit.caseId !== reqModel.caseId) {
257
+ throw new GenerationError(
258
+ "STAGE2_GAP_AUDIT_CONTEXT_MISMATCH",
259
+ "gap-audit.json does not match req-model.json",
260
+ );
261
+ }
262
+ }
263
+ /** Required alongside req-model.json so the AI Agent rendering route can
264
+ * surface gap-audit's unresolved/limited candidates in `## unresolved` —
265
+ * a purely additive Stage 2 signal (ADR-0004, PRD-0007). Mandatory like
266
+ * loadStage2AreaGraph: no silent-skip path when the file is missing. */
267
+ export async function loadStage2GapAudit(inputDirectory, report, reqModel) {
268
+ let gapAuditText;
269
+ try {
270
+ gapAuditText = await readFile(
271
+ join(inputDirectory, "gap-audit.json"),
272
+ "utf8",
273
+ );
274
+ } catch (error) {
275
+ throw new GenerationError(
276
+ "STAGE2_GAP_AUDIT_MISSING",
277
+ "Stage 2 requires gap-audit.json from the same Stage 1 run",
278
+ { cause: error },
279
+ );
280
+ }
281
+ const gapAuditValue = parseJson(gapAuditText, "gap-audit.json");
282
+ assertGapAuditEnvelope(gapAuditValue);
283
+ assertGapAuditHash(report, gapAuditText);
284
+ const gapAudit = gapAuditValue;
285
+ assertGapAuditContext(gapAudit, reqModel);
286
+ return gapAudit;
287
+ }
288
+ /** True only when every branch a target could resolve to is a "kind: area"
289
+ * ReqFeatureAreaTarget — i.e. the destination is always one of the Areas
290
+ * already rendered elsewhere in this req-model. "history-back" and
291
+ * "conditional-area-set" are built (analysis/req-model/build.ts) only from
292
+ * resolved Area edges, so they qualify unconditionally; "external" and an
293
+ * unresolved target/branch never do, since no Area entry describes them.
294
+ * "alternative-target-set" is the one shape that can legitimately mix an
295
+ * Area branch with an external or unresolved branch (built as the fallback
296
+ * whenever an Action's edges resolve to more than one target kind, or include
297
+ * an unresolved edge) — such a Feature must not be skipped, since part of its
298
+ * behavior (the non-Area branch) has no Area entry to fall back on. */
299
+ function targetResolvesOnlyToAreas(target) {
300
+ if ("status" in target) return false;
301
+ switch (target.kind) {
302
+ case "area":
303
+ case "history-back":
304
+ case "conditional-area-set":
305
+ return true;
306
+ case "external":
307
+ return false;
308
+ case "alternative-target-set":
309
+ return target.branches.every((branch) =>
310
+ isAreaBranchTarget(branch.target),
311
+ );
312
+ }
313
+ }
314
+ function isAreaBranchTarget(target) {
315
+ return !("status" in target) && target.kind === "area";
316
+ }
317
+ function isNavigationOnlyInteraction(interaction) {
318
+ return (
319
+ interaction.target !== undefined &&
320
+ interaction.effects.length === 0 &&
321
+ targetResolvesOnlyToAreas(interaction.target)
322
+ );
323
+ }
324
+ /** A Feature whose only observable behavior, across every way it can be
325
+ * triggered, is navigating to Area(s) that are themselves already rendered
326
+ * carries no independent requirement content — the destination Area already
327
+ * describes itself. Areas with no remaining Features are still rendered
328
+ * (static description + navigation), so this only ever removes Features,
329
+ * never their owning Area. */
330
+ function isNavigationOnlyFeature(feature) {
331
+ const interactions = feature.interactions ?? [
332
+ { effects: feature.effects, target: feature.target },
333
+ ];
334
+ return (
335
+ interactions.length > 0 && interactions.every(isNavigationOnlyInteraction)
336
+ );
337
+ }
338
+ /**
339
+ * Filters what should not be rendered, unconditionally:
340
+ * - Features whose only behavior, on every trigger, is navigating to Area(s)
341
+ * already rendered elsewhere (no independent req content). A Feature that
342
+ * can also navigate to something that is not an Area in this req-model
343
+ * (an external endpoint, or an unresolved target/branch) is kept, since
344
+ * that part of its behavior has nowhere else to be described.
345
+ *
346
+ * Areas with unreachable canonicalPath are always kept — the Agent is asked
347
+ * to determine the actual access path (or confirm unreachability with
348
+ * evidence) itself rather than being skipped in favor of the deterministic
349
+ * "无法到达" placeholder.
350
+ *
351
+ * Areas are never dropped for having zero remaining Features — an Area with
352
+ * no dynamic Features still gets rendered for its static description and
353
+ * navigation.
354
+ */
355
+ export function filterRenderableAreas(reqModel) {
356
+ return {
357
+ ...reqModel,
358
+ areas: reqModel.areas.map((area) => ({
359
+ ...area,
360
+ features: area.features.filter(
361
+ (feature) => !isNavigationOnlyFeature(feature),
362
+ ),
363
+ })),
364
+ };
365
+ }
366
+ /**
367
+ * Stable partition: Areas with a resolved/unresolved canonicalPath first (in
368
+ * their original relative order), followed by unreachable Areas (in their
369
+ * original relative order). Must run once, before both the deterministic
370
+ * template (renderReqDraft) and the Agent-render walk (filterRenderableAreas,
371
+ * merge*) see the req-model — they all read `reqModel.areas` positionally,
372
+ * so reordering later or only in one of them would desync the merge walk
373
+ * from the template it is walking.
374
+ */
375
+ export function sortAreasReachableFirst(reqModel) {
376
+ const reachable = reqModel.areas.filter(
377
+ (area) => area.canonicalPath.status !== "unreachable",
378
+ );
379
+ const unreachable = reqModel.areas.filter(
380
+ (area) => area.canonicalPath.status === "unreachable",
381
+ );
382
+ return { ...reqModel, areas: [...reachable, ...unreachable] };
383
+ }
384
+ export async function loadStage2Input(inputDirectory) {
385
+ return (await loadStage2InputWithReport(inputDirectory)).reqModel;
386
+ }
387
+ export async function loadStage2InputWithReport(inputDirectory) {
388
+ const { reportText, reqModelText } = await readInputFiles(inputDirectory);
389
+ const reportValue = parseJson(reportText, "run-report.json");
390
+ const reqModelValue = parseJson(reqModelText, "req-model.json");
391
+ assertReportReady(reportValue);
392
+ assertReportCompleteness(reportValue);
393
+ assertReqModelHash(reportValue, reqModelText);
394
+ assertReqModelEnvelope(reqModelValue);
395
+ assertReqModelCompleteness(reportValue, reqModelValue);
396
+ const report = reportValue;
397
+ const reqModel = reqModelValue;
398
+ if (
399
+ report.publishable === false &&
400
+ reqModel.semanticCompleteness !== "incomplete-known"
401
+ ) {
402
+ throw new GenerationError(
403
+ "STAGE2_INPUT_QUALITY_STATE_INVALID",
404
+ "req-model.json does not match the incomplete Stage 1 quality state",
405
+ );
406
+ }
407
+ if (
408
+ report.semanticCompleteness !== undefined &&
409
+ reqModel.semanticCompleteness !== undefined &&
410
+ report.semanticCompleteness !== reqModel.semanticCompleteness
411
+ ) {
412
+ throw new GenerationError(
413
+ "STAGE2_SEMANTIC_COMPLETENESS_MISMATCH",
414
+ "run-report.json and req-model.json disagree about semantic completeness",
415
+ );
416
+ }
417
+ assertSameContext(report, reqModel);
418
+ return { reqModel, report };
419
+ }
@@ -0,0 +1,95 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { sha256Text } from "./pipeline/output.js";
4
+ import { GenerationError, isRecord, parseJson } from "./stage2-input.js";
5
+ async function readOptionalText(path) {
6
+ try {
7
+ return await readFile(path, "utf8");
8
+ } catch (error) {
9
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
10
+ return undefined;
11
+ throw error;
12
+ }
13
+ }
14
+ /**
15
+ * Source locations are intentionally not copied into req-model.json. Read the
16
+ * two sibling Stage 1 artifacts only for the optional name-traceability
17
+ * output (human audit in openai-compatible mode, title/citation grounding in
18
+ * agent mode).
19
+ */
20
+ export async function loadStage2NameTraceabilityInput(
21
+ inputDirectory,
22
+ reqModel,
23
+ ) {
24
+ const [sourceText, graphText] = await Promise.all([
25
+ readOptionalText(join(inputDirectory, "source-facts.json")),
26
+ readOptionalText(join(inputDirectory, "area-graph.json")),
27
+ ]);
28
+ if (sourceText === undefined && graphText === undefined) return undefined;
29
+ if (sourceText === undefined || graphText === undefined) {
30
+ throw new GenerationError(
31
+ "STAGE2_NAME_TRACE_INPUT_INCOMPLETE",
32
+ "Name traceability requires both source-facts.json and area-graph.json",
33
+ );
34
+ }
35
+ const sourceValue = parseJson(sourceText, "source-facts.json");
36
+ const graphValue = parseJson(graphText, "area-graph.json");
37
+ const reportValue = parseJson(
38
+ await readFile(join(inputDirectory, "run-report.json"), "utf8"),
39
+ "run-report.json",
40
+ );
41
+ if (
42
+ !isRecord(sourceValue) ||
43
+ sourceValue.schemaVersion !== "1.0.0" ||
44
+ sourceValue.artifactKind !== "source-facts" ||
45
+ !Array.isArray(sourceValue.evidence) ||
46
+ !Array.isArray(sourceValue.facts) ||
47
+ !Array.isArray(sourceValue.unresolved) ||
48
+ !isRecord(graphValue) ||
49
+ graphValue.schemaVersion !== "1.0.0" ||
50
+ graphValue.artifactKind !== "area-graph" ||
51
+ !Array.isArray(graphValue.areas) ||
52
+ !Array.isArray(graphValue.controls) ||
53
+ !Array.isArray(graphValue.unresolved)
54
+ ) {
55
+ throw new GenerationError(
56
+ "STAGE2_NAME_TRACE_INPUT_INVALID",
57
+ "source-facts.json or area-graph.json has an unsupported envelope",
58
+ );
59
+ }
60
+ const artifactHashMatches = (kind, relativePath, text) =>
61
+ isRecord(reportValue) &&
62
+ Array.isArray(reportValue.artifacts) &&
63
+ reportValue.artifacts.some(
64
+ (artifact) =>
65
+ isRecord(artifact) &&
66
+ artifact.kind === kind &&
67
+ artifact.relativePath === relativePath &&
68
+ artifact.sha256 === sha256Text(text),
69
+ );
70
+ if (
71
+ !artifactHashMatches("source-facts", "source-facts.json", sourceText) ||
72
+ !artifactHashMatches("area-graph", "area-graph.json", graphText)
73
+ ) {
74
+ throw new GenerationError(
75
+ "STAGE2_NAME_TRACE_HASH_MISMATCH",
76
+ "Name traceability inputs do not match the hashes in run-report.json",
77
+ );
78
+ }
79
+ const source = sourceValue;
80
+ const graph = graphValue;
81
+ if (
82
+ source.caseId !== reqModel.caseId ||
83
+ graph.caseId !== reqModel.caseId ||
84
+ JSON.stringify(source.analysisContext) !==
85
+ JSON.stringify(reqModel.analysisContext) ||
86
+ JSON.stringify(graph.analysisContext) !==
87
+ JSON.stringify(reqModel.analysisContext)
88
+ ) {
89
+ throw new GenerationError(
90
+ "STAGE2_NAME_TRACE_CONTEXT_MISMATCH",
91
+ "Name traceability inputs do not match req-model.json",
92
+ );
93
+ }
94
+ return { source, graph };
95
+ }