create-yss-spec 2.2.10 → 3.0.0

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 (381) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/template/.agents/skills/ask-matt/SKILL.md +1 -1
  4. package/template/.agents/skills/wayfinder/SKILL.md +2 -2
  5. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
  6. package/template/.agents/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  7. package/template/.agents/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
  8. package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
  9. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
  10. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  11. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
  12. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  13. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
  14. package/template/.agents/skills/yss-domain/SKILL.md +2 -1
  15. package/template/.agents/skills/yss-domain/agents/openai.yaml +2 -2
  16. package/template/.agents/skills/yss-domain/references/domain-layer-guide.md +2 -0
  17. package/template/.agents/skills/yss-dto/SKILL.md +6 -0
  18. package/template/.agents/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  19. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +1 -1
  20. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  21. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -10
  22. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  23. package/template/.agents/skills/yss-repository/SKILL.md +15 -13
  24. package/template/.agents/skills/yss-repository/agents/openai.yaml +2 -2
  25. package/template/.agents/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  26. package/template/.agents/skills/yss-research/SKILL.md +73 -0
  27. package/template/.agents/skills/yss-research/agents/openai.yaml +3 -0
  28. package/template/.agents/skills/yss-research/assets/evidence-template.yaml +68 -0
  29. package/template/.agents/skills/yss-research/assets/research-brief-template.md +36 -0
  30. package/template/.agents/skills/yss-research/references/evidence-contract.md +42 -0
  31. package/template/.agents/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  32. package/template/.agents/skills/yss-research/tests/run-scenarios.mjs +81 -0
  33. package/template/.agents/skills/yss-router/references/router-contract.yaml +7 -3
  34. package/template/.agents/skills/yss-stage-decision/SKILL.md +2 -1
  35. package/template/.agents/skills/yss-web-controller/SKILL.md +16 -10
  36. package/template/.agents/skills/yss-web-controller/agents/openai.yaml +2 -2
  37. package/template/.agents/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  38. package/template/.agents/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  39. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  40. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  41. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  42. package/template/.agents/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  43. package/template/.agents/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  44. package/template/.agents/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  45. package/template/.agents/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  46. package/template/.claude/skills/ask-matt/SKILL.md +1 -1
  47. package/template/.claude/skills/wayfinder/SKILL.md +2 -2
  48. package/template/.claude/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
  49. package/template/.claude/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  50. package/template/.claude/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
  51. package/template/.claude/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
  52. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
  53. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  54. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
  55. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  56. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
  57. package/template/.claude/skills/yss-domain/SKILL.md +2 -1
  58. package/template/.claude/skills/yss-domain/agents/openai.yaml +2 -2
  59. package/template/.claude/skills/yss-domain/references/domain-layer-guide.md +2 -0
  60. package/template/.claude/skills/yss-dto/SKILL.md +6 -0
  61. package/template/.claude/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  62. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +1 -1
  63. package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  64. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -10
  65. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  66. package/template/.claude/skills/yss-repository/SKILL.md +15 -13
  67. package/template/.claude/skills/yss-repository/agents/openai.yaml +2 -2
  68. package/template/.claude/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  69. package/template/.claude/skills/yss-research/SKILL.md +73 -0
  70. package/template/.claude/skills/yss-research/agents/openai.yaml +3 -0
  71. package/template/.claude/skills/yss-research/assets/evidence-template.yaml +68 -0
  72. package/template/.claude/skills/yss-research/assets/research-brief-template.md +36 -0
  73. package/template/.claude/skills/yss-research/references/evidence-contract.md +42 -0
  74. package/template/.claude/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  75. package/template/.claude/skills/yss-research/tests/run-scenarios.mjs +81 -0
  76. package/template/.claude/skills/yss-router/references/router-contract.yaml +7 -3
  77. package/template/.claude/skills/yss-stage-decision/SKILL.md +2 -1
  78. package/template/.claude/skills/yss-web-controller/SKILL.md +16 -10
  79. package/template/.claude/skills/yss-web-controller/agents/openai.yaml +2 -2
  80. package/template/.claude/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  81. package/template/.claude/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  82. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  83. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  84. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  85. package/template/.claude/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  86. package/template/.claude/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  87. package/template/.claude/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  88. package/template/.claude/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  89. package/template/.codex/skills/ask-matt/SKILL.md +1 -1
  90. package/template/.codex/skills/product-design/package.json +4 -2
  91. package/template/.codex/skills/product-design/skills/research/SKILL.md +22 -0
  92. package/template/.codex/skills/product-design/skills/research/assets/evidence-template.yaml +65 -0
  93. package/template/.codex/skills/product-design/skills/research/assets/research-brief-template.md +33 -0
  94. package/template/.codex/skills/product-design/skills/research/references/evidence-audited.md +80 -0
  95. package/template/.codex/skills/product-design/skills/research/scripts/validate-research-package.mjs +149 -0
  96. package/template/.codex/skills/product-design/skills/research/tests/run-scenarios.mjs +72 -0
  97. package/template/.codex/skills/wayfinder/SKILL.md +2 -2
  98. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
  99. package/template/.codex/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  100. package/template/.codex/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
  101. package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
  102. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
  103. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  104. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
  105. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  106. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
  107. package/template/.codex/skills/yss-domain/SKILL.md +2 -1
  108. package/template/.codex/skills/yss-domain/agents/openai.yaml +2 -2
  109. package/template/.codex/skills/yss-domain/references/domain-layer-guide.md +2 -0
  110. package/template/.codex/skills/yss-dto/SKILL.md +6 -0
  111. package/template/.codex/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  112. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +1 -1
  113. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  114. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -10
  115. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  116. package/template/.codex/skills/yss-repository/SKILL.md +15 -13
  117. package/template/.codex/skills/yss-repository/agents/openai.yaml +2 -2
  118. package/template/.codex/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  119. package/template/.codex/skills/yss-research/SKILL.md +73 -0
  120. package/template/.codex/skills/yss-research/agents/openai.yaml +3 -0
  121. package/template/.codex/skills/yss-research/assets/evidence-template.yaml +68 -0
  122. package/template/.codex/skills/yss-research/assets/research-brief-template.md +36 -0
  123. package/template/.codex/skills/yss-research/references/evidence-contract.md +42 -0
  124. package/template/.codex/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  125. package/template/.codex/skills/yss-research/tests/run-scenarios.mjs +81 -0
  126. package/template/.codex/skills/yss-router/references/router-contract.yaml +7 -3
  127. package/template/.codex/skills/yss-stage-decision/SKILL.md +2 -1
  128. package/template/.codex/skills/yss-web-controller/SKILL.md +16 -10
  129. package/template/.codex/skills/yss-web-controller/agents/openai.yaml +2 -2
  130. package/template/.codex/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  131. package/template/.codex/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  132. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  133. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  134. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  135. package/template/.codex/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  136. package/template/.codex/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  137. package/template/.codex/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  138. package/template/.codex/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  139. package/template/.cursor/skills/ask-matt/SKILL.md +1 -1
  140. package/template/.cursor/skills/wayfinder/SKILL.md +2 -2
  141. package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
  142. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  143. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
  144. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
  145. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
  146. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  147. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
  148. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  149. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
  150. package/template/.cursor/skills/yss-domain/SKILL.md +2 -1
  151. package/template/.cursor/skills/yss-domain/agents/openai.yaml +2 -2
  152. package/template/.cursor/skills/yss-domain/references/domain-layer-guide.md +2 -0
  153. package/template/.cursor/skills/yss-dto/SKILL.md +6 -0
  154. package/template/.cursor/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  155. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +1 -1
  156. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  157. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -10
  158. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  159. package/template/.cursor/skills/yss-repository/SKILL.md +15 -13
  160. package/template/.cursor/skills/yss-repository/agents/openai.yaml +2 -2
  161. package/template/.cursor/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  162. package/template/.cursor/skills/yss-research/SKILL.md +73 -0
  163. package/template/.cursor/skills/yss-research/agents/openai.yaml +3 -0
  164. package/template/.cursor/skills/yss-research/assets/evidence-template.yaml +68 -0
  165. package/template/.cursor/skills/yss-research/assets/research-brief-template.md +36 -0
  166. package/template/.cursor/skills/yss-research/references/evidence-contract.md +42 -0
  167. package/template/.cursor/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  168. package/template/.cursor/skills/yss-research/tests/run-scenarios.mjs +81 -0
  169. package/template/.cursor/skills/yss-router/references/router-contract.yaml +7 -3
  170. package/template/.cursor/skills/yss-stage-decision/SKILL.md +2 -1
  171. package/template/.cursor/skills/yss-web-controller/SKILL.md +16 -10
  172. package/template/.cursor/skills/yss-web-controller/agents/openai.yaml +2 -2
  173. package/template/.cursor/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  174. package/template/.cursor/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  175. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  176. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  177. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  178. package/template/.cursor/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  179. package/template/.cursor/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  180. package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  181. package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  182. package/template/.hermes/skills/ask-matt/SKILL.md +1 -1
  183. package/template/.hermes/skills/wayfinder/SKILL.md +2 -2
  184. package/template/.hermes/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
  185. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  186. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
  187. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
  188. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
  189. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  190. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
  191. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  192. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
  193. package/template/.hermes/skills/yss-domain/SKILL.md +2 -1
  194. package/template/.hermes/skills/yss-domain/agents/openai.yaml +2 -2
  195. package/template/.hermes/skills/yss-domain/references/domain-layer-guide.md +2 -0
  196. package/template/.hermes/skills/yss-dto/SKILL.md +6 -0
  197. package/template/.hermes/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  198. package/template/.hermes/skills/yss-product-lifecycle/SKILL.md +1 -1
  199. package/template/.hermes/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  200. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -10
  201. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  202. package/template/.hermes/skills/yss-repository/SKILL.md +15 -13
  203. package/template/.hermes/skills/yss-repository/agents/openai.yaml +2 -2
  204. package/template/.hermes/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  205. package/template/.hermes/skills/yss-research/SKILL.md +73 -0
  206. package/template/.hermes/skills/yss-research/agents/openai.yaml +3 -0
  207. package/template/.hermes/skills/yss-research/assets/evidence-template.yaml +68 -0
  208. package/template/.hermes/skills/yss-research/assets/research-brief-template.md +36 -0
  209. package/template/.hermes/skills/yss-research/references/evidence-contract.md +42 -0
  210. package/template/.hermes/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  211. package/template/.hermes/skills/yss-research/tests/run-scenarios.mjs +81 -0
  212. package/template/.hermes/skills/yss-router/references/router-contract.yaml +7 -3
  213. package/template/.hermes/skills/yss-stage-decision/SKILL.md +2 -1
  214. package/template/.hermes/skills/yss-web-controller/SKILL.md +16 -10
  215. package/template/.hermes/skills/yss-web-controller/agents/openai.yaml +2 -2
  216. package/template/.hermes/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  217. package/template/.hermes/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  218. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  219. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  220. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  221. package/template/.hermes/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  222. package/template/.hermes/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  223. package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  224. package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  225. package/template/.pi/skills/ask-matt/SKILL.md +1 -1
  226. package/template/.pi/skills/wayfinder/SKILL.md +2 -2
  227. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
  228. package/template/.pi/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  229. package/template/.pi/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
  230. package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
  231. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
  232. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  233. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
  234. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  235. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
  236. package/template/.pi/skills/yss-domain/SKILL.md +2 -1
  237. package/template/.pi/skills/yss-domain/agents/openai.yaml +2 -2
  238. package/template/.pi/skills/yss-domain/references/domain-layer-guide.md +2 -0
  239. package/template/.pi/skills/yss-dto/SKILL.md +6 -0
  240. package/template/.pi/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  241. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +1 -1
  242. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  243. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -10
  244. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  245. package/template/.pi/skills/yss-repository/SKILL.md +15 -13
  246. package/template/.pi/skills/yss-repository/agents/openai.yaml +2 -2
  247. package/template/.pi/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  248. package/template/.pi/skills/yss-research/SKILL.md +73 -0
  249. package/template/.pi/skills/yss-research/agents/openai.yaml +3 -0
  250. package/template/.pi/skills/yss-research/assets/evidence-template.yaml +68 -0
  251. package/template/.pi/skills/yss-research/assets/research-brief-template.md +36 -0
  252. package/template/.pi/skills/yss-research/references/evidence-contract.md +42 -0
  253. package/template/.pi/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  254. package/template/.pi/skills/yss-research/tests/run-scenarios.mjs +81 -0
  255. package/template/.pi/skills/yss-router/references/router-contract.yaml +7 -3
  256. package/template/.pi/skills/yss-stage-decision/SKILL.md +2 -1
  257. package/template/.pi/skills/yss-web-controller/SKILL.md +16 -10
  258. package/template/.pi/skills/yss-web-controller/agents/openai.yaml +2 -2
  259. package/template/.pi/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  260. package/template/.pi/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  261. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  262. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  263. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  264. package/template/.pi/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  265. package/template/.pi/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  266. package/template/.pi/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  267. package/template/.pi/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  268. package/template/.qoder/skills/ask-matt/SKILL.md +1 -1
  269. package/template/.qoder/skills/wayfinder/SKILL.md +2 -2
  270. package/template/.qoder/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
  271. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  272. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
  273. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
  274. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
  275. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  276. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
  277. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  278. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
  279. package/template/.qoder/skills/yss-domain/SKILL.md +2 -1
  280. package/template/.qoder/skills/yss-domain/agents/openai.yaml +2 -2
  281. package/template/.qoder/skills/yss-domain/references/domain-layer-guide.md +2 -0
  282. package/template/.qoder/skills/yss-dto/SKILL.md +6 -0
  283. package/template/.qoder/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  284. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +1 -1
  285. package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  286. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -10
  287. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  288. package/template/.qoder/skills/yss-repository/SKILL.md +15 -13
  289. package/template/.qoder/skills/yss-repository/agents/openai.yaml +2 -2
  290. package/template/.qoder/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  291. package/template/.qoder/skills/yss-research/SKILL.md +73 -0
  292. package/template/.qoder/skills/yss-research/agents/openai.yaml +3 -0
  293. package/template/.qoder/skills/yss-research/assets/evidence-template.yaml +68 -0
  294. package/template/.qoder/skills/yss-research/assets/research-brief-template.md +36 -0
  295. package/template/.qoder/skills/yss-research/references/evidence-contract.md +42 -0
  296. package/template/.qoder/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  297. package/template/.qoder/skills/yss-research/tests/run-scenarios.mjs +81 -0
  298. package/template/.qoder/skills/yss-router/references/router-contract.yaml +7 -3
  299. package/template/.qoder/skills/yss-stage-decision/SKILL.md +2 -1
  300. package/template/.qoder/skills/yss-web-controller/SKILL.md +16 -10
  301. package/template/.qoder/skills/yss-web-controller/agents/openai.yaml +2 -2
  302. package/template/.qoder/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  303. package/template/.qoder/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  304. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  305. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  306. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  307. package/template/.qoder/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  308. package/template/.qoder/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  309. package/template/.qoder/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  310. package/template/.qoder/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  311. package/template/.trae/skills/ask-matt/SKILL.md +1 -1
  312. package/template/.trae/skills/wayfinder/SKILL.md +2 -2
  313. package/template/.trae/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
  314. package/template/.trae/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  315. package/template/.trae/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
  316. package/template/.trae/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
  317. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
  318. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  319. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
  320. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  321. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
  322. package/template/.trae/skills/yss-domain/SKILL.md +2 -1
  323. package/template/.trae/skills/yss-domain/agents/openai.yaml +2 -2
  324. package/template/.trae/skills/yss-domain/references/domain-layer-guide.md +2 -0
  325. package/template/.trae/skills/yss-dto/SKILL.md +6 -0
  326. package/template/.trae/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  327. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +1 -1
  328. package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  329. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -10
  330. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  331. package/template/.trae/skills/yss-repository/SKILL.md +15 -13
  332. package/template/.trae/skills/yss-repository/agents/openai.yaml +2 -2
  333. package/template/.trae/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  334. package/template/.trae/skills/yss-research/SKILL.md +73 -0
  335. package/template/.trae/skills/yss-research/agents/openai.yaml +3 -0
  336. package/template/.trae/skills/yss-research/assets/evidence-template.yaml +68 -0
  337. package/template/.trae/skills/yss-research/assets/research-brief-template.md +36 -0
  338. package/template/.trae/skills/yss-research/references/evidence-contract.md +42 -0
  339. package/template/.trae/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  340. package/template/.trae/skills/yss-research/tests/run-scenarios.mjs +81 -0
  341. package/template/.trae/skills/yss-router/references/router-contract.yaml +7 -3
  342. package/template/.trae/skills/yss-stage-decision/SKILL.md +2 -1
  343. package/template/.trae/skills/yss-web-controller/SKILL.md +16 -10
  344. package/template/.trae/skills/yss-web-controller/agents/openai.yaml +2 -2
  345. package/template/.trae/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  346. package/template/.trae/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  347. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  348. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  349. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  350. package/template/.trae/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  351. package/template/.trae/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  352. package/template/.trae/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  353. package/template/.trae/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  354. package/template/AGENTS.md +2 -2
  355. package/template/CONTEXT.md +1 -1
  356. package/template/docs/agents/digital-human-roles.yaml +2 -2
  357. package/template/docs/agents/yss-skill-registry.yaml +18 -3
  358. package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +1 -0
  359. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
  360. package/template/scripts/lib/scenario-checks.mjs +8 -4
  361. package/template/scripts/lib/skill-registry.mjs +49 -5
  362. package/template/scripts/lib/skill-supply-chain.mjs +3 -2
  363. package/template/scripts/verify-yss-dto-openapi-profile +8 -0
  364. package/template/skills-lock.json +30 -30
  365. package/template.snapshot.json +4 -4
  366. package/template/.agents/skills/research/SKILL.md +0 -12
  367. package/template/.agents/skills/research/agents/openai.yaml +0 -3
  368. package/template/.claude/skills/research/SKILL.md +0 -12
  369. package/template/.claude/skills/research/agents/openai.yaml +0 -3
  370. package/template/.codex/skills/research/SKILL.md +0 -12
  371. package/template/.codex/skills/research/agents/openai.yaml +0 -3
  372. package/template/.cursor/skills/research/SKILL.md +0 -12
  373. package/template/.cursor/skills/research/agents/openai.yaml +0 -3
  374. package/template/.hermes/skills/research/SKILL.md +0 -12
  375. package/template/.hermes/skills/research/agents/openai.yaml +0 -3
  376. package/template/.pi/skills/research/SKILL.md +0 -12
  377. package/template/.pi/skills/research/agents/openai.yaml +0 -3
  378. package/template/.qoder/skills/research/SKILL.md +0 -12
  379. package/template/.qoder/skills/research/agents/openai.yaml +0 -3
  380. package/template/.trae/skills/research/SKILL.md +0 -12
  381. package/template/.trae/skills/research/agents/openai.yaml +0 -3
@@ -1,18 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { createHash } from "node:crypto";
3
+ import { constants, access, copyFile, mkdir, mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
3
5
  import path from "node:path";
4
6
  import { fileURLToPath } from "node:url";
5
7
 
8
+ import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
9
+
6
10
  const TYPE_MAPPING = { bigint: "Long", int: "Integer", integer: "Integer", smallint: "Integer", tinyint: "Integer", number: "Long", numeric: "BigDecimal", decimal: "BigDecimal", float: "Float", double: "Double", real: "Float", varchar: "String", varchar2: "String", nvarchar2: "String", char: "String", nchar: "String", text: "String", clob: "String", nclob: "String", date: "LocalDateTime", datetime: "LocalDateTime", timestamp: "LocalDateTime", "timestamp with time zone": "LocalDateTime", boolean: "Boolean", bool: "Boolean", bit: "Boolean", json: "String", jsonb: "String", uuid: "String" };
7
11
  const PLATFORM_VALIDATION_NAMESPACES = {
8
12
  "spring-boot-2.7-jdk8": "javax",
9
13
  "spring-boot-3-jdk17": "jakarta"
10
14
  };
11
- const required = ["metadata-file", "contract-file", "base-package", "module-name", "domain-segment"];
12
- const valued = new Set([...required, "output-dir", "web-project-dir", "web-output-dir", "author", "application-service-package", "validation-namespace"]);
15
+ const required = ["metadata-file", "contract-file", "dto-wire-profile-file", "base-package", "module-name", "domain-segment"];
16
+ const valued = new Set([...required, "output-dir", "web-project-dir", "web-output-dir", "author", "application-service-package", "validation-namespace", "scaffold-manifest-file"]);
13
17
 
14
18
  function usage() {
15
- return "Usage: node generate_controller.mjs --metadata-file FILE --contract-file FILE --base-package PACKAGE --module-name NAME --domain-segment SEGMENT [--output-dir DIR] [--web-project-dir DIR] [--web-output-dir DIR] [--author NAME] [--application-service-package PACKAGE] [--validation-namespace javax|jakarta]";
19
+ return "Usage: node generate_controller.mjs --metadata-file FILE --contract-file FILE --dto-wire-profile-file FILE --base-package PACKAGE --module-name NAME --domain-segment SEGMENT [--output-dir DIR] [--web-project-dir DIR] [--web-output-dir DIR] [--scaffold-manifest-file FILE] [--author NAME] [--application-service-package PACKAGE] [--validation-namespace javax|jakarta]";
16
20
  }
17
21
  export function parseArgs(argv) {
18
22
  const args = { "output-dir": "./output", author: "System", "validation-namespace": "javax", force: false };
@@ -51,6 +55,114 @@ function fields(columns, selected, { command = false } = {}) {
51
55
  }
52
56
  function render(template, context) { return template.replace(/\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))/g, (_, braced, plain) => { const key = braced || plain; if (!(key in context)) throw new Error(`missing template field: ${key}`); return context[key]; }); }
53
57
  async function exists(target) { try { await access(target); return true; } catch { return false; } }
58
+ const present = (value) => value !== undefined && value !== null && value !== "";
59
+ function within(root, target) {
60
+ const relative = path.relative(path.resolve(root), path.resolve(target));
61
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
62
+ }
63
+ function simpleJavaName(javaType) {
64
+ return javaType.replace(/<.*$/, "").split(".").at(-1);
65
+ }
66
+ function paginationFields(selected, dtoProfile) {
67
+ const definitions = dtoProfile.page_query.fields;
68
+ const unique = [...new Set(selected)];
69
+ const unsupported = unique.filter((name) => !(name in definitions) || dtoProfile.page_query.forbidden_client_fields.includes(name));
70
+ if (unsupported.length) throw new Error(`unsupported pagination fields: ${unsupported.join(", ")}`);
71
+ return unique.map((name) => {
72
+ const definition = definitions[name];
73
+ const wireType = definition.wire_types.find((item) => item !== "null");
74
+ const javaType = wireType === "integer" ? (definition.nullable ? "Integer" : "int") : wireType === "string" ? "String" : null;
75
+ if (!javaType) throw new Error(`unsupported yss-dto pagination wire type for ${name}: ${definition.wire_types.join(", ")}`);
76
+ const annotation = definition.enum?.length
77
+ ? ` @Pattern(regexp = "${definition.enum.join("|")}", message = "${name} must be one of ${definition.enum.join(", ")}")\n`
78
+ : "";
79
+ const initializer = definition.default === undefined ? "" : ` = ${typeof definition.default === "string" ? JSON.stringify(definition.default) : definition.default}`;
80
+ return `${annotation} private ${javaType} ${name}${initializer};`;
81
+ }).join("\n");
82
+ }
83
+
84
+ async function loadDtoWireProfile(contract, args) {
85
+ const profilePath = path.resolve(args["dto-wire-profile-file"]);
86
+ if (profilePath !== path.resolve(contract.dto_wire_profile_ref ?? "")) throw new Error("--dto-wire-profile-file does not match the approved Web generation contract");
87
+ if (!await exists(profilePath)) throw new Error(`yss-dto wire profile not found: ${profilePath}`);
88
+ const source = await readFile(profilePath);
89
+ const digest = createHash("sha256").update(source).digest("hex");
90
+ if (digest !== contract.dto_wire_profile_digest) throw new Error("yss-dto wire profile digest does not match the approved Web generation contract");
91
+ const document = parseDocument(source.toString("utf8"), { maxAliasCount: 0, uniqueKeys: true });
92
+ if (document.errors.length) throw new Error(`invalid yss-dto wire profile: ${document.errors[0].message}`);
93
+ const profile = document.toJS({ maxAliasCount: 0 });
94
+ if (profile?.schema_version !== 1 || profile?.kind !== "yss-dto-openapi-wire-profile") throw new Error("unsupported yss-dto wire profile");
95
+ const resultPackage = profile.canonical?.java_package;
96
+ const pageResult = profile.wrappers?.PageResult;
97
+ const singleResult = profile.wrappers?.SingleResult;
98
+ const commandDtoType = profile.request_bases?.command?.java_type;
99
+ const queryDtoType = profile.request_bases?.query?.java_type;
100
+ if (![resultPackage, pageResult?.java_type, singleResult?.java_type, commandDtoType, queryDtoType].every(present)) throw new Error("incomplete yss-dto wire profile type mapping");
101
+ if (![pageResult.java_type, singleResult.java_type].every((type) => type.startsWith(`${resultPackage}.`))) throw new Error("yss-dto result wrapper package is inconsistent");
102
+ if (!profile.page_query?.fields || !Array.isArray(profile.page_query?.forbidden_client_fields)) throw new Error("incomplete yss-dto page_query mapping");
103
+ const factory = pageResult.java_factory;
104
+ const singleFactory = singleResult.java_factory;
105
+ const javaIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
106
+ if (!javaIdentifier.test(factory?.method ?? "") || !Array.isArray(factory.arguments) || !factory.arguments.length) throw new Error("incomplete yss-dto PageResult factory mapping");
107
+ if (!javaIdentifier.test(singleFactory?.method ?? "") || JSON.stringify(singleFactory.arguments) !== JSON.stringify(["data"])) throw new Error("incomplete yss-dto SingleResult factory mapping");
108
+ const expressionByArgument = { data: "toResponses(source.getData())", totalCount: "source.getTotalCount()", pageSize: "source.getPageSize()", pageIndex: "source.getPageIndex()" };
109
+ const unsupportedArguments = factory.arguments.filter((name) => !(name in expressionByArgument));
110
+ if (unsupportedArguments.length) throw new Error(`unsupported yss-dto PageResult factory arguments: ${unsupportedArguments.join(", ")}`);
111
+ return {
112
+ profile,
113
+ resultPackage,
114
+ pageResultType: pageResult.java_type.replace(/<.*$/, ""),
115
+ singleResultType: singleResult.java_type.replace(/<.*$/, ""),
116
+ pageResultFactoryMethod: factory.method,
117
+ singleResultFactoryMethod: singleFactory.method,
118
+ commandDtoType,
119
+ queryDtoType,
120
+ pageResultFactoryArguments: factory.arguments.map((name, index) => ` ${expressionByArgument[name]}${index < factory.arguments.length - 1 ? "," : ""}`).join("\n")
121
+ };
122
+ }
123
+
124
+ async function validateContract(contract, args) {
125
+ if (contract.schema_version !== 2) throw new Error(`web generation contract schema_version=${contract.schema_version} is unsupported; only schema_version=2 is accepted`);
126
+ if (contract.status !== "approved") throw new Error("web generation contract must be approved");
127
+ const requiredFields = ["contract_id", "contract_version", "current_version", "slice_id", "integration_mode", "implementation_project_root", "base_package", "module_name", "domain_segment", "application_service_package", "architecture_profile", "platform_profile", "validation_namespace", "dto_placement", "dto_wire_profile_ref", "dto_wire_profile_digest", "openapi_freeze_ref", "fields", "allowed_write_paths", "expected_evidence_files", "verification_commands"];
128
+ const missing = requiredFields.filter((name) => !present(contract[name]));
129
+ if (missing.length) throw new Error(`approved web generation contract is incomplete: ${missing.join(", ")}`);
130
+ if (contract.current_version !== contract.contract_version) throw new Error("web generation contract is not the current approved version");
131
+ if (!Number.isInteger(contract.contract_version) || contract.contract_version < 1) throw new Error("web generation contract_version must be a positive integer");
132
+ if (!["existing-project", "scaffold-v2"].includes(contract.integration_mode)) throw new Error("unsupported web generation integration_mode");
133
+ if (!["allowed_write_paths", "expected_evidence_files", "verification_commands"].every((name) => Array.isArray(contract[name]) && contract[name].length)) throw new Error("web generation contract requires non-empty allowed_write_paths, expected_evidence_files, and verification_commands");
134
+ if (contract.architecture_profile !== "target-domain-model" || contract.dto_placement !== "web") throw new Error("web generation only supports target-domain-model with dto_placement=web");
135
+ if (contract.base_package !== args["base-package"] || contract.module_name !== args["module-name"] || contract.domain_segment !== args["domain-segment"]) throw new Error("CLI generation identity does not match the approved web generation contract");
136
+ const applicationPackage = args["application-service-package"] || `${args["base-package"]}.application.service`;
137
+ if (contract.application_service_package !== applicationPackage) throw new Error("--application-service-package does not match the approved web generation contract");
138
+ if (!(contract.platform_profile in PLATFORM_VALIDATION_NAMESPACES)) throw new Error("unsupported platform_profile in approved web generation contract");
139
+ if (PLATFORM_VALIDATION_NAMESPACES[contract.platform_profile] !== contract.validation_namespace) throw new Error("platform_profile and validation_namespace are inconsistent");
140
+ if (contract.validation_namespace !== args["validation-namespace"]) throw new Error("--validation-namespace must match the approved web generation contract");
141
+
142
+ const implementationRoot = path.resolve(contract.implementation_project_root);
143
+ if (contract.integration_mode === "existing-project") {
144
+ if (args["scaffold-manifest-file"] || present(contract.scaffold_manifest_ref)) throw new Error("existing-project Web generation must not claim a scaffold manifest");
145
+ return { applicationPackage, implementationRoot };
146
+ }
147
+
148
+ if (!args["scaffold-manifest-file"] || !present(contract.scaffold_manifest_ref)) throw new Error("scaffold-v2 Web generation requires --scaffold-manifest-file and scaffold_manifest_ref");
149
+ const manifestPath = path.resolve(args["scaffold-manifest-file"]);
150
+ if (manifestPath !== path.resolve(contract.scaffold_manifest_ref)) throw new Error("--scaffold-manifest-file does not match the approved web generation contract");
151
+ if (!await exists(manifestPath)) throw new Error(`scaffold manifest not found: ${manifestPath}`);
152
+ const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
153
+ if (manifest.schema_version !== 2) throw new Error("scaffold manifest must use schema_version=2");
154
+ if (!["empty-scaffold-verified", "first-slice-verified"].includes(manifest.completion_level)) throw new Error("scaffold manifest must be empty-scaffold-verified before Web generation");
155
+ const scaffoldRoot = path.dirname(path.dirname(manifestPath));
156
+ if (implementationRoot !== scaffoldRoot) throw new Error("implementation_project_root does not match the scaffold manifest project root");
157
+ const expectedWebProject = path.join(scaffoldRoot, `${manifest.project_name}-adapter`, `${manifest.project_name}-web`);
158
+ if (!args["web-project-dir"] || path.resolve(args["web-project-dir"]) !== expectedWebProject) throw new Error("--web-project-dir does not match the scaffold manifest project layout");
159
+ const profilePairs = [["architecture", "architecture_profile"], ["platform", "platform_profile"], ["validation_namespace", "validation_namespace"], ["dto_placement", "dto_placement"]];
160
+ for (const [manifestField, contractField] of profilePairs) {
161
+ if (manifest.profiles?.[manifestField] !== contract[contractField]) throw new Error(`scaffold manifest ${manifestField} profile does not match the approved Web contract`);
162
+ }
163
+ if (manifest.base_package !== contract.base_package) throw new Error("scaffold manifest base_package does not match the approved Web contract");
164
+ return { applicationPackage, implementationRoot };
165
+ }
54
166
 
55
167
  export async function generate(args, logger = console) {
56
168
  if (!await exists(args["metadata-file"])) throw new Error(`Metadata file not found: ${args["metadata-file"]}`);
@@ -59,14 +171,8 @@ export async function generate(args, logger = console) {
59
171
  try { metadata = JSON.parse(await readFile(args["metadata-file"], "utf8")); } catch (error) { throw new Error(`Error reading metadata file: ${error.message}`); }
60
172
  let contract;
61
173
  try { contract = JSON.parse(await readFile(args["contract-file"], "utf8")); } catch (error) { throw new Error(`Error reading contract file: ${error.message}`); }
62
- if (contract.schema_version !== 1 || contract.status !== "approved") throw new Error("web generation contract must be schema_version=1 and approved");
63
- if (contract.architecture_profile !== "target-domain-model" || contract.dto_placement !== "web") throw new Error("web generation only supports target-domain-model with dto_placement=web");
64
- if (!contract.openapi_freeze_ref || !contract.fields || typeof contract.fields !== "object") throw new Error("approved web generation contract requires openapi_freeze_ref and fields");
65
- if (!contract.base_package || !contract.module_name || !contract.domain_segment) throw new Error("approved web generation contract requires base_package, module_name, and domain_segment");
66
- if (contract.base_package !== args["base-package"] || contract.module_name !== args["module-name"] || contract.domain_segment !== args["domain-segment"]) throw new Error("CLI generation identity does not match the approved web generation contract");
67
- if (!(contract.platform_profile in PLATFORM_VALIDATION_NAMESPACES)) throw new Error("unsupported platform_profile in approved web generation contract");
68
- if (PLATFORM_VALIDATION_NAMESPACES[contract.platform_profile] !== contract.validation_namespace) throw new Error("platform_profile and validation_namespace are inconsistent");
69
- if (contract.validation_namespace !== args["validation-namespace"]) throw new Error("--validation-namespace must match the approved web generation contract");
174
+ const { applicationPackage, implementationRoot } = await validateContract(contract, args);
175
+ const dto = await loadDtoWireProfile(contract, args);
70
176
  const tables = metadata.tables || [];
71
177
  if (!tables.length) { logger.warn("Warning: No tables found in metadata."); return []; }
72
178
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
@@ -82,22 +188,37 @@ export async function generate(args, logger = console) {
82
188
  for (const table of tables) {
83
189
  const domainClass = pascal(table.table_name); const domainVar = lowerCamel(table.table_name); const domainDesc = String(table.table_comment || domainClass).trim().replaceAll("\n", " ");
84
190
  const approvedFields = contract.fields[table.table_name];
85
- if (!approvedFields || ["create", "update", "query", "response"].some((name) => !Array.isArray(approvedFields[name]))) throw new Error(`approved web field contract is missing for table: ${table.table_name}`);
86
- const applicationPackage = args["application-service-package"] || `${args["base-package"]}.application.service`;
87
- const context = { base_package: args["base-package"], module_name: args["module-name"], domain_class: domainClass, domain_var: domainVar, domain_desc: domainDesc, domain_url_path: kebab(table.table_name), domain_pkg_name: args["domain-segment"], author: args.author, dto_imports: [`import ${args["base-package"]}.rest.dto.request.${domainClass}CreateRequest;`, `import ${args["base-package"]}.rest.dto.request.${domainClass}UpdateRequest;`, `import ${args["base-package"]}.rest.dto.request.${domainClass}PageRequest;`, `import ${args["base-package"]}.rest.dto.response.${domainClass}Response;`].join("\n"), application_type_imports: [`import ${args["base-package"]}.application.command.${domainClass}CreateCommand;`, `import ${args["base-package"]}.application.command.${domainClass}UpdateCommand;`, `import ${args["base-package"]}.application.query.${domainClass}PageQuery;`, `import ${args["base-package"]}.application.result.${domainClass}Result;`].join("\n"), application_service_import: `import ${applicationPackage}.${domainClass}Service;`, web_convertor_import: `import ${args["base-package"]}.rest.convertor.${domainClass}WebConvertor;`, web_convertor_class: `${domainClass}WebConvertor`, application_service_field: `private final ${domainClass}Service ${domainVar}Service;`, query_call: `${domainVar}Service.page(webConvertor.toPageQuery(request))`, detail_call: `${domainVar}Service.detail(id)`, add_call: `${domainVar}Service.add(webConvertor.toCreateCommand(request))`, update_call: `${domainVar}Service.update(webConvertor.toUpdateCommand(request))`, delete_call: `${domainVar}Service.delete(id)`, response_class: `${domainClass}Response`, create_request_class: `${domainClass}CreateRequest`, update_request_class: `${domainClass}UpdateRequest`, page_request_class: `${domainClass}PageRequest`, validation_namespace: args["validation-namespace"] };
191
+ if (!approvedFields || ["create", "update", "query", "pagination", "response"].some((name) => !Array.isArray(approvedFields[name]))) throw new Error(`approved web field contract is missing create/update/query/pagination/response allowlists for table: ${table.table_name}`);
192
+ const context = { base_package: args["base-package"], module_name: args["module-name"], domain_class: domainClass, domain_var: domainVar, domain_desc: domainDesc, domain_url_path: kebab(table.table_name), domain_pkg_name: args["domain-segment"], author: args.author, dto_imports: [`import ${args["base-package"]}.rest.dto.request.${domainClass}CreateRequest;`, `import ${args["base-package"]}.rest.dto.request.${domainClass}UpdateRequest;`, `import ${args["base-package"]}.rest.dto.request.${domainClass}PageRequest;`, `import ${args["base-package"]}.rest.dto.response.${domainClass}Response;`].join("\n"), application_type_imports: [`import ${args["base-package"]}.application.command.${domainClass}CreateCommand;`, `import ${args["base-package"]}.application.command.${domainClass}UpdateCommand;`, `import ${args["base-package"]}.application.query.${domainClass}PageQuery;`, `import ${args["base-package"]}.application.result.${domainClass}Result;`].join("\n"), application_service_import: `import ${applicationPackage}.${domainClass}Service;`, web_convertor_import: `import ${args["base-package"]}.rest.convertor.${domainClass}WebConvertor;`, web_convertor_class: `${domainClass}WebConvertor`, application_service_field: `private final ${domainClass}Service ${domainVar}Service;`, query_call: `${domainVar}Service.page(webConvertor.toPageQuery(request))`, detail_call: `${domainVar}Service.detail(id)`, add_call: `${domainVar}Service.add(webConvertor.toCreateCommand(request))`, update_call: `${domainVar}Service.update(webConvertor.toUpdateCommand(request))`, delete_call: `${domainVar}Service.delete(id)`, response_class: `${domainClass}Response`, create_request_class: `${domainClass}CreateRequest`, update_request_class: `${domainClass}UpdateRequest`, page_request_class: `${domainClass}PageRequest`, validation_namespace: args["validation-namespace"], result_package: dto.resultPackage, page_result_type: dto.pageResultType, page_result_simple_name: simpleJavaName(dto.pageResultType), page_result_factory_method: dto.pageResultFactoryMethod, single_result_type: dto.singleResultType, single_result_simple_name: simpleJavaName(dto.singleResultType), single_result_factory_method: dto.singleResultFactoryMethod, command_dto_type: dto.commandDtoType, command_dto_simple_name: simpleJavaName(dto.commandDtoType), query_dto_type: dto.queryDtoType, query_dto_simple_name: simpleJavaName(dto.queryDtoType), page_result_factory_arguments: dto.pageResultFactoryArguments };
88
193
  plan(path.join(webRoot, `${domainClass}Controller.java`), render(templates["Controller.java.template"], context));
89
194
  plan(path.join(webRoot, "convertor", `${domainClass}WebConvertor.java`), render(templates["WebConvertor.java.template"], context));
90
- for (const [templateName, target, declaration] of [["web/dto/Response.java.template", path.join(webRoot, "dto", "response", `${domainClass}Response.java`), fields(table.columns || [], approvedFields.response)], ["web/dto/CreateRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}CreateRequest.java`), fields(table.columns || [], approvedFields.create, { command: true })], ["web/dto/UpdateRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}UpdateRequest.java`), fields(table.columns || [], approvedFields.update, { command: true })], ["web/dto/PageRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}PageRequest.java`), fields(table.columns || [], approvedFields.query)]]) plan(target, render(templates[templateName], { ...context, field_declarations: declaration }));
195
+ for (const [templateName, target, declaration] of [["web/dto/Response.java.template", path.join(webRoot, "dto", "response", `${domainClass}Response.java`), fields(table.columns || [], approvedFields.response)], ["web/dto/CreateRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}CreateRequest.java`), fields(table.columns || [], approvedFields.create, { command: true })], ["web/dto/UpdateRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}UpdateRequest.java`), fields(table.columns || [], approvedFields.update, { command: true })], ["web/dto/PageRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}PageRequest.java`), fields(table.columns || [], approvedFields.query)]]) plan(target, render(templates[templateName], { ...context, field_declarations: declaration, pagination_field_declarations: paginationFields(approvedFields.pagination, dto.profile) }));
91
196
  }
92
197
  const targets = planned.map(({ target }) => target);
93
198
  if (new Set(targets).size !== targets.length) throw new Error("approved metadata produces duplicate target files");
94
199
  const existing = [];
95
200
  for (const target of targets) if (await exists(target)) existing.push(target);
96
201
  if (existing.length) throw new Error(`initialize-only Web generation refuses existing targets: ${existing.join(", ")}`);
97
- for (const { target, contents } of planned) {
98
- await mkdir(path.dirname(target), { recursive: true });
99
- await writeFile(target, contents, "utf8");
100
- logger.log(`Generated: ${target}`);
202
+ const allowedRoots = contract.allowed_write_paths.map((item) => path.resolve(implementationRoot, String(item)));
203
+ const outside = targets.filter((target) => !allowedRoots.some((root) => within(root, target)));
204
+ if (outside.length) throw new Error(`planned Web targets are outside allowed_write_paths: ${outside.join(", ")}`);
205
+ const staging = await mkdtemp(path.join(tmpdir(), "yss-web-generation-"));
206
+ const created = [];
207
+ try {
208
+ for (const [index, { contents }] of planned.entries()) await writeFile(path.join(staging, String(index)), contents, "utf8");
209
+ for (const [index, { target }] of planned.entries()) {
210
+ await mkdir(path.dirname(target), { recursive: true });
211
+ await copyFile(path.join(staging, String(index)), target, constants.COPYFILE_EXCL);
212
+ created.push(target);
213
+ logger.log(`Generated: ${target}`);
214
+ }
215
+ } catch (error) {
216
+ for (const target of created.reverse()) {
217
+ try { await unlink(target); } catch (cleanupError) { if (cleanupError.code !== "ENOENT") error.cleanupError = cleanupError.message; }
218
+ }
219
+ throw error;
220
+ } finally {
221
+ await rm(staging, { recursive: true, force: true });
101
222
  }
102
223
  return targets;
103
224
  }
@@ -1,60 +1,156 @@
1
1
  import assert from "node:assert/strict";
2
+ import { createHash } from "node:crypto";
2
3
  import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
3
4
  import { tmpdir } from "node:os";
4
5
  import path from "node:path";
5
6
  import test from "node:test";
7
+ import { fileURLToPath } from "node:url";
6
8
  import { generate, parseArgs } from "./generate_controller.mjs";
7
9
 
8
- test("generates application-service controller with requested validation profile", async () => {
10
+ const scripts = path.dirname(fileURLToPath(import.meta.url));
11
+ const dtoWireProfileFile = path.resolve(scripts, "../../yss-dto/references/openapi-wire-profile.yaml");
12
+ const dtoWireProfileDigest = createHash("sha256").update(await readFile(dtoWireProfileFile)).digest("hex");
13
+
14
+ const metadataValue = {
15
+ tables: [{
16
+ table_name: "quality_rule",
17
+ table_comment: "质量规则",
18
+ columns: [
19
+ { name: "id", sql_type: "bigint", primary: true, nullable: false },
20
+ { name: "rule_name", sql_type: "varchar(64)", nullable: false }
21
+ ]
22
+ }]
23
+ };
24
+
25
+ function webContract(root, overrides = {}) {
26
+ return {
27
+ schema_version: 2,
28
+ contract_id: "slice-quality-web-1",
29
+ contract_version: 1,
30
+ current_version: 1,
31
+ slice_id: "quality-rule-create",
32
+ status: "approved",
33
+ integration_mode: "existing-project",
34
+ implementation_project_root: root,
35
+ base_package: "com.yss.demo",
36
+ module_name: "demo",
37
+ domain_segment: "quality",
38
+ application_service_package: "com.yss.demo.application.service",
39
+ architecture_profile: "target-domain-model",
40
+ platform_profile: "spring-boot-3-jdk17",
41
+ validation_namespace: "jakarta",
42
+ dto_placement: "web",
43
+ dto_wire_profile_ref: dtoWireProfileFile,
44
+ dto_wire_profile_digest: dtoWireProfileDigest,
45
+ openapi_freeze_ref: "openapi://quality-rule@1",
46
+ allowed_write_paths: [root],
47
+ expected_evidence_files: ["evidence/web-generation.json"],
48
+ verification_commands: ["./mvnw test", "./mvnw package"],
49
+ fields: {
50
+ quality_rule: {
51
+ create: ["rule_name"],
52
+ update: ["id", "rule_name"],
53
+ query: ["rule_name"],
54
+ pagination: ["pageIndex", "pageSize"],
55
+ response: ["id", "rule_name"]
56
+ }
57
+ },
58
+ ...overrides
59
+ };
60
+ }
61
+
62
+ async function fixture(overrides = {}) {
9
63
  const root = await mkdtemp(path.join(tmpdir(), "yss-controller-"));
10
64
  const metadata = path.join(root, "metadata.json");
11
65
  const contract = path.join(root, "web-contract.json");
12
- await writeFile(metadata, JSON.stringify({ tables: [{ table_name: "quality_rule", table_comment: "质量规则", columns: [{ name: "id", sql_type: "bigint", primary: true, nullable: false }, { name: "rule_name", sql_type: "varchar(64)", nullable: false }] }] }));
13
- await writeFile(contract, JSON.stringify({ schema_version: 1, status: "approved", base_package: "com.yss.demo", module_name: "demo", domain_segment: "quality", architecture_profile: "target-domain-model", platform_profile: "spring-boot-3-jdk17", validation_namespace: "jakarta", dto_placement: "web", openapi_freeze_ref: "openapi://quality-rule@1", fields: { quality_rule: { create: ["rule_name"], update: ["id", "rule_name"], query: ["rule_name"], response: ["id", "rule_name"] } } }));
14
- await generate(parseArgs(["--metadata-file", metadata, "--contract-file", contract, "--base-package", "com.yss.demo", "--module-name", "demo", "--domain-segment", "quality", "--output-dir", path.join(root, "out"), "--application-service-package", "com.yss.demo.application.service", "--validation-namespace", "jakarta"]), { log() {}, warn() {} });
15
- const controller = await readFile(path.join(root, "out", "com", "yss", "demo", "rest", "QualityRuleController.java"), "utf8");
16
- const convertor = await readFile(path.join(root, "out", "com", "yss", "demo", "rest", "convertor", "QualityRuleWebConvertor.java"), "utf8");
17
- const createRequest = await readFile(path.join(root, "out", "com", "yss", "demo", "rest", "dto", "request", "QualityRuleCreateRequest.java"), "utf8");
66
+ const output = path.join(root, "out");
67
+ await writeFile(metadata, JSON.stringify(metadataValue));
68
+ await writeFile(contract, JSON.stringify(webContract(root, overrides)));
69
+ const args = ["--metadata-file", metadata, "--contract-file", contract, "--dto-wire-profile-file", dtoWireProfileFile, "--base-package", "com.yss.demo", "--module-name", "demo", "--domain-segment", "quality", "--output-dir", output, "--application-service-package", "com.yss.demo.application.service", "--validation-namespace", overrides.validation_namespace ?? "jakarta"];
70
+ return { root, metadata, contract, output, args };
71
+ }
72
+
73
+ test("generates approved standalone Web code and exposes only approved pagination fields", async () => {
74
+ const data = await fixture();
75
+ await generate(parseArgs(data.args), { log() {}, warn() {} });
76
+ const rest = path.join(data.output, "com", "yss", "demo", "rest");
77
+ const controller = await readFile(path.join(rest, "QualityRuleController.java"), "utf8");
78
+ const convertor = await readFile(path.join(rest, "convertor", "QualityRuleWebConvertor.java"), "utf8");
79
+ const createRequest = await readFile(path.join(rest, "dto", "request", "QualityRuleCreateRequest.java"), "utf8");
80
+ const pageRequest = await readFile(path.join(rest, "dto", "request", "QualityRulePageRequest.java"), "utf8");
18
81
  assert.match(controller, /import com\.yss\.demo\.application\.service\.QualityRuleService;/);
82
+ assert.match(controller, /SingleResult\.of\(/);
19
83
  assert.match(controller, /import jakarta\.validation\.Valid;/);
20
- assert.match(controller, /webConvertor\.toCreateCommand\(request\)/);
21
84
  assert.match(convertor, /@Mapper\(componentModel = "spring"\)/);
22
- assert.match(convertor, /application\.command\.QualityRuleCreateCommand/);
23
- assert.match(convertor, /application\.result\.QualityRuleResult/);
85
+ assert.match(convertor, /PageResult\.of\(\s*toResponses\(source\.getData\(\)\),\s*source\.getTotalCount\(\),\s*source\.getPageSize\(\),\s*source\.getPageIndex\(\)/s);
24
86
  assert.doesNotMatch(`${controller}\n${convertor}`, /Gateway|\.domain\.|Mappers\.getMapper|INSTANCE/);
25
87
  assert.match(createRequest, /private String ruleName;/);
26
88
  assert.doesNotMatch(createRequest, /private Long id;|@Data/);
89
+ assert.match(pageRequest, /private int pageIndex = 1;/);
90
+ assert.match(pageRequest, /private int pageSize = 10;/);
91
+ assert.doesNotMatch(pageRequest, /orderBy|orderDirection|groupBy|offset|needTotalCount|tempTotalCount|extends PageQuery/);
27
92
  });
28
93
 
29
- test("rejects missing required CLI arguments", () => assert.throws(() => parseArgs(["--module-name", "demo"]), /required/));
94
+ test("rejects a yss-dto wire profile whose digest differs from the approved contract", async () => {
95
+ const data = await fixture({ dto_wire_profile_digest: "0".repeat(64) });
96
+ await assert.rejects(() => generate(parseArgs(data.args), { log() {}, warn() {} }), /yss-dto wire profile digest/);
97
+ });
30
98
 
31
- test("requires an approved target-profile field contract and refuses force overwrite", async () => {
32
- const root = await mkdtemp(path.join(tmpdir(), "yss-controller-contract-"));
33
- const metadata = path.join(root, "metadata.json");
34
- const contract = path.join(root, "web-contract.json");
35
- await writeFile(metadata, JSON.stringify({ tables: [{ table_name: "quality_rule", columns: [{ name: "id", sql_type: "bigint", primary: true, nullable: false }] }] }));
36
- await writeFile(contract, JSON.stringify({ schema_version: 1, status: "draft", base_package: "com.yss.demo", module_name: "demo", domain_segment: "quality", architecture_profile: "target-domain-model", platform_profile: "spring-boot-2.7-jdk8", validation_namespace: "javax", dto_placement: "web", openapi_freeze_ref: "openapi://quality-rule@1", fields: { quality_rule: { create: [], update: ["id"], query: [], response: ["id"] } } }));
37
- const base = ["--metadata-file", metadata, "--contract-file", contract, "--base-package", "com.yss.demo", "--module-name", "demo", "--domain-segment", "quality", "--output-dir", path.join(root, "out")];
38
- await assert.rejects(() => generate(parseArgs(base), { log() {}, warn() {} }), /approved/);
39
- await writeFile(contract, JSON.stringify({ schema_version: 1, status: "approved", base_package: "com.yss.demo", module_name: "demo", domain_segment: "quality", architecture_profile: "target-domain-model", platform_profile: "spring-boot-2.7-jdk8", validation_namespace: "javax", dto_placement: "web", openapi_freeze_ref: "openapi://quality-rule@1", fields: { quality_rule: { create: [], update: ["id"], query: [], response: ["id"] } } }));
40
- await writeFile(contract, JSON.stringify({ schema_version: 1, status: "approved", base_package: "com.yss.demo", module_name: "demo", domain_segment: "quality", architecture_profile: "target-domain-model", platform_profile: "spring-boot-3-jdk17", validation_namespace: "javax", dto_placement: "web", openapi_freeze_ref: "openapi://quality-rule@1", fields: { quality_rule: { create: [], update: ["id"], query: [], response: ["id"] } } }));
41
- await assert.rejects(() => generate(parseArgs(base), { log() {}, warn() {} }), /platform_profile and validation_namespace/);
42
- assert.throws(() => parseArgs([...base, "--force"]), /initialize-only/);
99
+ test("rejects missing CLI arguments, schema v1, draft contracts, and inconsistent platform profiles", async () => {
100
+ assert.throws(() => parseArgs(["--module-name", "demo"]), /required/);
101
+ const v1 = await fixture({ schema_version: 1 });
102
+ await assert.rejects(() => generate(parseArgs(v1.args), { log() {}, warn() {} }), /schema_version=1.*unsupported/);
103
+ const draft = await fixture({ status: "draft" });
104
+ await assert.rejects(() => generate(parseArgs(draft.args), { log() {}, warn() {} }), /approved/);
105
+ const mismatch = await fixture({ platform_profile: "spring-boot-3-jdk17", validation_namespace: "javax" });
106
+ await assert.rejects(() => generate(parseArgs(mismatch.args), { log() {}, warn() {} }), /platform_profile and validation_namespace/);
107
+ assert.throws(() => parseArgs([...draft.args, "--force"]), /initialize-only/);
43
108
  });
44
109
 
45
- test("binds generation identity to the approved contract and fails before writing when any target exists", async () => {
46
- const root = await mkdtemp(path.join(tmpdir(), "yss-controller-initialize-only-"));
47
- const metadata = path.join(root, "metadata.json");
48
- const contract = path.join(root, "web-contract.json");
49
- const output = path.join(root, "out");
50
- await writeFile(metadata, JSON.stringify({ tables: [{ table_name: "quality_rule", columns: [{ name: "id", sql_type: "bigint", nullable: false }] }] }));
51
- await writeFile(contract, JSON.stringify({ schema_version: 1, status: "approved", base_package: "com.yss.demo", module_name: "demo", domain_segment: "quality", architecture_profile: "target-domain-model", platform_profile: "spring-boot-2.7-jdk8", validation_namespace: "javax", dto_placement: "web", openapi_freeze_ref: "openapi://quality-rule@1", fields: { quality_rule: { create: [], update: ["id"], query: [], response: ["id"] } } }));
52
- const args = ["--metadata-file", metadata, "--contract-file", contract, "--base-package", "com.yss.demo", "--module-name", "demo", "--domain-segment", "quality", "--output-dir", output];
53
- await assert.rejects(() => generate(parseArgs(args.map((value) => value === "demo" ? "other" : value)), { log() {}, warn() {} }), /approved web generation contract/);
54
- const existing = path.join(output, "com/yss/demo/rest/QualityRuleController.java");
110
+ test("binds identity and every target to the approved write scope before writing", async () => {
111
+ const data = await fixture();
112
+ await assert.rejects(() => generate(parseArgs(data.args.map((value) => value === "demo" ? "other" : value)), { log() {}, warn() {} }), /approved web generation contract/);
113
+ await writeFile(data.contract, JSON.stringify(webContract(data.root, { allowed_write_paths: [path.join(data.root, "different-root")] })));
114
+ await assert.rejects(() => generate(parseArgs(data.args), { log() {}, warn() {} }), /allowed_write_paths/);
115
+ await writeFile(data.contract, JSON.stringify(webContract(data.root)));
116
+ const existing = path.join(data.output, "com/yss/demo/rest/QualityRuleController.java");
55
117
  await mkdir(path.dirname(existing), { recursive: true });
56
118
  await writeFile(existing, "user-owned\n");
57
- await assert.rejects(() => generate(parseArgs(args), { log() {}, warn() {} }), /initialize-only/);
119
+ await assert.rejects(() => generate(parseArgs(data.args), { log() {}, warn() {} }), /initialize-only/);
58
120
  assert.equal(await readFile(existing, "utf8"), "user-owned\n");
59
- await assert.rejects(() => readFile(path.join(output, "com/yss/demo/rest/convertor/QualityRuleWebConvertor.java"), "utf8"), /ENOENT/);
121
+ await assert.rejects(() => readFile(path.join(data.output, "com/yss/demo/rest/convertor/QualityRuleWebConvertor.java"), "utf8"), /ENOENT/);
122
+ });
123
+
124
+ test("scaffold-v2 mode requires an empty-scaffold-verified manifest with matching profile and project layout", async () => {
125
+ const data = await fixture({ platform_profile: "spring-boot-2.7-jdk8", validation_namespace: "javax" });
126
+ const projectRoot = path.join(data.root, "demo-service");
127
+ const webProject = path.join(projectRoot, "demo-service-adapter", "demo-service-web");
128
+ const manifest = path.join(projectRoot, ".yss", "scaffold-generation.json");
129
+ await mkdir(path.dirname(manifest), { recursive: true });
130
+ await writeFile(manifest, JSON.stringify({ schema_version: 2, project_name: "demo-service", base_package: "com.yss.demo", completion_level: "empty-scaffold-verified", profiles: { architecture: "target-domain-model", platform: "spring-boot-2.7-jdk8", validation_namespace: "javax", dto_placement: "web" } }));
131
+ const contractValue = webContract(projectRoot, {
132
+ integration_mode: "scaffold-v2",
133
+ implementation_project_root: projectRoot,
134
+ scaffold_manifest_ref: manifest,
135
+ platform_profile: "spring-boot-3-jdk17",
136
+ validation_namespace: "jakarta",
137
+ allowed_write_paths: [path.join(webProject, "src/main/java/com/yss/demo/rest")]
138
+ });
139
+ await writeFile(data.contract, JSON.stringify(contractValue));
140
+ const args = ["--metadata-file", data.metadata, "--contract-file", data.contract, "--dto-wire-profile-file", dtoWireProfileFile, "--scaffold-manifest-file", manifest, "--base-package", "com.yss.demo", "--module-name", "demo", "--domain-segment", "quality", "--web-project-dir", webProject, "--application-service-package", "com.yss.demo.application.service", "--validation-namespace", "jakarta"];
141
+ await assert.rejects(() => generate(parseArgs(args), { log() {}, warn() {} }), /scaffold manifest.*platform/i);
142
+ contractValue.platform_profile = "spring-boot-2.7-jdk8";
143
+ contractValue.validation_namespace = "javax";
144
+ await writeFile(data.contract, JSON.stringify(contractValue));
145
+ await generate(parseArgs(args.map((value) => value === "jakarta" ? "javax" : value)), { log() {}, warn() {} });
146
+ });
147
+
148
+ test("rolls back files written before a filesystem failure", async () => {
149
+ const data = await fixture();
150
+ const rest = path.join(data.output, "com/yss/demo/rest");
151
+ await mkdir(rest, { recursive: true });
152
+ await writeFile(path.join(rest, "convertor"), "blocking-file\n");
153
+ await assert.rejects(() => generate(parseArgs(data.args), { log() {}, warn() {} }));
154
+ await assert.rejects(() => readFile(path.join(rest, "QualityRuleController.java"), "utf8"), /ENOENT/);
155
+ assert.equal(await readFile(path.join(rest, "convertor"), "utf8"), "blocking-file\n");
60
156
  });
@@ -53,7 +53,7 @@ README、用户指南、根目录 `CLAUDE.md` 和其他说明文档只引用或
53
53
  | 主阶段(registry) | 原生工作单元 | 技能入口(按影响面裁剪) | 关键产物 / 门禁 |
54
54
  |---|---|---|---|
55
55
  | 入口分诊 `stage.entry-triage` | `work-unit.entry-triage` | `yss-product-lifecycle` | 仓库身份、影响面、最近可信阶段;`gate.repository-identity-valid` |
56
- | Discovery `stage.discovery` | `work-unit.discovery-opportunity`、`work-unit.discovery-requirements`、`work-unit.domain-strategy-design`、`work-unit.stage-decision` | `yss-product-lifecycle`;按事实 / 领域影响使用 `competitive-intelligence`、`research`、`grilling`、`domain-modeling`、`yss-stage-decision` | Discovery、用户/MVP/非目标/成功标准、测试 seam;必要时 DDD 战略设计与阶段决策包;`gate.domain-strategy-approved`、`gate.stage-decision-package-approved` |
56
+ | Discovery `stage.discovery` | `work-unit.discovery-opportunity`、`work-unit.discovery-requirements`、`work-unit.domain-strategy-design`、`work-unit.stage-decision` | `yss-product-lifecycle`;按事实 / 领域影响使用 `competitive-intelligence`、`yss-research`、`grilling`、`domain-modeling`、`yss-stage-decision` | Discovery、用户/MVP/非目标/成功标准、测试 seam;必要时 DDD 战略设计与阶段决策包;`gate.domain-strategy-approved`、`gate.stage-decision-package-approved` |
57
57
  | Spec / 功能架构 `stage.spec-architecture` | `work-unit.spec-synthesis` | `yss-product-lifecycle`;`to-spec` 仅为显式兼容入口 | Spec、产品总体设计、功能架构;必要时 Spec Delta;`gate.spec-baseline-approved` |
58
58
  | 产品设计 `stage.product-design` | `work-unit.prototype-design` | `yss-prototype-stage` 为主合同,内部依次消费 `yss-design-system`、低保真、独立 `prototype-review`、`yss-antd-design` pre-build facts、Codex `product-design:index` focused workflow、post-build/浏览器/无障碍验证;前端落地改用 `yss-ui` | 交互说明、低 / 高保真原型、状态矩阵;`gate.prototype-reviewed`、`gate.prototype-verified`、`gate.user-confirmation` |
59
59
  | 系统 / 数据架构与工程契约 `stage.system-data-engineering` | `work-unit.technical-analysis` | `yss-router`;按影响使用 `yss-openapi-governance`、`yss-openapi-draft-review`、`codebase-design`、`implementation-repo-onboarding`、`yss-tactical-design` | OpenAPI Draft / Freeze、数据架构、工程基线、架构审查;无 API 影响记录;必要时 Tactical DDD Check;`gate.openapi-draft-reviewed`、`gate.design-reviewed`、`gate.openapi-frozen`、`gate.engineering-baseline-accepted`、`gate.architecture-reviewed` |
@@ -95,7 +95,7 @@ README、用户指南、根目录 `CLAUDE.md` 和其他说明文档只引用或
95
95
 
96
96
  | 触发情形 | 必须使用 |
97
97
  |---|---|
98
- | 技术事实、标准、第三方 API 或框架行为影响决策 | `research` 或等价的一手资料记录 |
98
+ | 技术事实、标准、第三方 API 或框架行为影响决策,或外部证据进入领域战略 / 阶段决策 | `yss-research`;技术事实使用 `technical-evidence`,战略决策证据使用 `strategy-evidence`;旧名 `research` 仅为 deprecated alias |
99
99
  | 竞品、市场或用户口碑事实 | `competitive-intelligence` |
100
100
  | UI 设计、原型、组件或主题 | `yss-prototype-stage` 持有阶段合同:`yss-design-system` → 低保真 → 独立 `prototype-review` → `yss-antd-design` pre-build facts → Codex `product-design:index` focused workflow → post-build/浏览器/无障碍验证;其他 Agent 交付等价合同资产。前端代码落地改用 `yss-ui`,不要继续调用 `yss-antd-design` |
101
101
  | Bug、测试失败或性能回退 | `diagnosing-bugs` 建立可复现反馈,再用 `tdd` |
@@ -79,7 +79,7 @@
79
79
  | 显式兼容入口 | 为已有用户操作习惯保留的 user-invoked 工作流入口,写入前由生命周期编排器预检,结果回交其验收。 | — | 不是默认路径、过时别名或可以越过门禁的第二套生命周期。 |
80
80
  | 技能成熟度 | 描述技能从 `draft`、`verified`、`supported` 到 `deprecated` 的治理状态。 | — | 不等同于文件存在、已被投影或能够安装。 |
81
81
  | 技能注册表 | 记录技能身份、别名、分层、适用影响面、成熟度、默认可发现性和 Agent 运行时入口的机器可读路由资产。 | — | 不替代 `skills-lock.json` 的来源、hash 和投影完整性职责。当前 `docs/agents/yss-skill-registry.yaml` 为 `active`,Router、生命周期编排器和实例发现面必须消费通过校验的 canonical 技能及其 alias 解析结果。 |
82
- | LLM Wiki | 由 `raw/`、`wiki/` 与 `.wiki-manifest.json` 组成的本地持久知识库。 | — | 不是 `research` 一次性笔记,也不替代权威源。`ingest` 只把用户点名的外源或已落盘研究笔记编进 IR,不改 live 权威文件。 |
82
+ | LLM Wiki | 由 `raw/`、`wiki/` 与 `.wiki-manifest.json` 组成的本地持久知识库。 | — | 不是 `yss-research` 一次性研究包,也不替代权威源。`ingest` 只把用户点名的外源或已落盘研究笔记编进 IR,不改 live 权威文件。 |
83
83
  | 生态发布清单 | 关联模板 schema 与 commit、CLI 版本与快照、公开技能来源与导出 hash 的跨仓发布证据。 | — | 不要求尚未生成的仓库 commit 互相循环引用。 |
84
84
  | 研发管理仓库 | 承载 Spec、OpenAPI、架构、Ticket、验证、发布和复盘等研发管理资产的仓库。 | — | 不等同于前端 / 后端代码 monorepo。 |
85
85
  | 实现仓库 | 承载前端、后端或其他运行时代码及其 Git、CI、MR / PR、测试命令和发布流水线的仓库。 | — | 不要把实现仓库的源码所有权混入研发管理仓库。 |
@@ -86,7 +86,7 @@ roles:
86
86
  title: YSS 需求经理
87
87
  stages: [stage.discovery, stage.spec-architecture]
88
88
  draft_artifact_ids: [artifact.discovery-record, artifact.spec]
89
- core_skills: [grilling, domain-modeling, research, competitive-intelligence, grill-with-docs]
89
+ core_skills: [grilling, domain-modeling, yss-research, competitive-intelligence, grill-with-docs]
90
90
  forbidden_skills: [yss-ui, yss-domain, yss-web-controller, yss-repository, yss-ddd-scaffold-generator, yss-frontend-scaffold-generator, implement, tdd]
91
91
  description: >-
92
92
  Own Discovery language, users, MVP, non-goals, success criteria, test
@@ -101,7 +101,7 @@ roles:
101
101
  title: YSS 产品经理
102
102
  stages: [stage.discovery, stage.spec-architecture, stage.product-design, stage.verification-release-retrospective]
103
103
  draft_artifact_ids: [artifact.product-overview, artifact.functional-architecture, artifact.interaction-spec, artifact.state-matrix, artifact.prototype-confirmation]
104
- core_skills: [yss-design-system, yss-prototype-stage, prototype-review, yss-antd-design, research, competitive-intelligence]
104
+ core_skills: [yss-design-system, yss-prototype-stage, prototype-review, yss-antd-design, yss-research, competitive-intelligence]
105
105
  forbidden_skills: [yss-ui, yss-domain, yss-repository, yss-web-controller, yss-ddd-scaffold-generator, implement, tdd]
106
106
  description: >-
107
107
  Own priority, product-design impact, interaction, state matrix, prototype
@@ -37,7 +37,22 @@ invocation_contract:
37
37
  maintainer-only:
38
38
  invocation_mode: user
39
39
  primary_output: maintenance-operation
40
- overrides: {}
40
+ overrides:
41
+ yss-ddd-scaffold-generator:
42
+ required_dependencies: [yss-backend-scaffold-parent, alibaba-java-code-style]
43
+ optional_dependencies: []
44
+ yss-domain:
45
+ required_dependencies: [alibaba-java-code-style]
46
+ optional_dependencies: [domain-modeling, yss-tactical-design, lombok]
47
+ yss-application:
48
+ required_dependencies: [alibaba-java-code-style]
49
+ optional_dependencies: [mapstruct, lombok]
50
+ yss-repository:
51
+ required_dependencies: [yss-mybatis, mapstruct, lombok, alibaba-java-code-style]
52
+ optional_dependencies: []
53
+ yss-web-controller:
54
+ required_dependencies: [yss-dto, yss-validation, mapstruct, lombok, alibaba-java-code-style]
55
+ optional_dependencies: [yss-exception]
41
56
  agent_runtime_roots:
42
57
  claude: .claude/skills
43
58
  codex: .codex/skills
@@ -197,11 +212,11 @@ skills:
197
212
  instance_default_discoverable: true
198
213
  aliases: []
199
214
  impacts: [prototype]
200
- - id: research
215
+ - id: yss-research
201
216
  layer: core
202
217
  maturity: supported
203
218
  instance_default_discoverable: true
204
- aliases: []
219
+ aliases: [research]
205
220
  impacts: [discovery]
206
221
  - id: resolving-merge-conflicts
207
222
  layer: core
@@ -7,4 +7,5 @@ Matt Pocock Engineering Skills 提供轻量的澄清、实现、TDD、诊断和
7
7
  - Matt user-invoked 的 `grill-with-docs`、`to-spec`、`to-tickets` 和 `implement` 必须由用户显式启动,任何 skill 不得自动调用它们;YSS 生命周期只准备、校验和验收其结果。所有 Matt model-invoked skill 仍须遵守 `AGENTS.md` 的阶段、门禁和证据要求,并只能在生命周期允许的工作单元中调用。
8
8
  - `code-review` 保持唯一默认审查入口。YSS 适配把 Slice `required_skills`、`alibaba-java-code-style` 与命中的 `yss-ui` / `yss-domain` 等专项 skill 编译为 Standards 检查输入,并要求实现仓已有的 `pnpm` / `./mvnw` 机器检查;禁止再增加第二个通用审查 skill 或专用审查 Cloud 环境来代替这条接线。审查 finding 按编排合同分流:实现者修复后全轴复审,或合同 `stale` 后回 Router;审查者不得写实现。
9
9
  - `template-source` 只执行模板维护流程,不生成具体产品 Spec、原型、OpenAPI 或垂直切片 Ticket。
10
+ - Matt 的 `research` 已适配为 canonical `yss-research`,旧名只作为 deprecated alias。YSS 版本提供 `technical-evidence` / `strategy-evidence` profile、`quick` / `evidence-audited` 模式、证据合同和确定性校验,同时保留上游 revision 与原始 hash 以便追溯。
10
11
  - Agent 完成工作单元后返回可核验的 evidence refs、变更资产、验证命令、残余风险和下一路由。
@@ -536,7 +536,7 @@ Agent 应返回合同版本、修改文件、实际命令、退出码和证据
536
536
  | 已确认 | 已由业务方、产品或权威资料确认 | 可以进入后续文档 |
537
537
  | 待确认 | 暂时没有答案 | 写负责人和确认时间,不让 Agent 猜 |
538
538
  | 非目标 | 本次明确不做 | 用来控制范围 |
539
- | 外部事实 | 来自法规、第三方 API、竞品或技术资料 | 用 `research` 或 `competitive-intelligence` 核对 |
539
+ | 外部事实 | 来自法规、第三方 API、竞品或技术资料 | 用 `yss-research` 或 `competitive-intelligence` 核对;`research` 仅为兼容旧名 |
540
540
  | 猜测 | 还没有人确认的推测 | 保持为待确认 |
541
541
 
542
542
  ### 处理稳定术语
@@ -24,7 +24,7 @@ function validateMattContract(data) {
24
24
  ensure(includesAll(setup?.preserves, ["lifecycle.status", "gate.status", "ticket.role"]) && setup?.legacy_artifacts_detected?.action === "migration-check" && setup.legacy_artifacts_detected.setup === "forbidden" && setup.legacy_artifacts_detected.write === "paused", "setup 暂停或旧资产迁移暂停契约不完整");
25
25
  const grill = data.grill_exit;
26
26
  ensure(includesAll(grill?.required, ["frontier_empty", "facts_resolved_or_routed", "decisions_confirmed", "shared_understanding_confirmed", "no_unresolved_runnable_blocker"]) && grill?.user_confirmation_required === true, "grill_exit 缺少 frontier、事实路由、决策、共同理解、用户确认或 runnable blocker 条件");
27
- ensure(grill?.facts_resolved_or_routed?.technical_fact === "research" && grill.facts_resolved_or_routed.runnable_question === "handoff-prototype-handoff" && grill.facts_resolved_or_routed.external_decision === "external-input-required", "grill_exit 的事实、runnable 问题或外部决策路由不完整");
27
+ ensure(grill?.facts_resolved_or_routed?.technical_fact === "yss-research" && grill.facts_resolved_or_routed.runnable_question === "handoff-prototype-handoff" && grill.facts_resolved_or_routed.external_decision === "external-input-required", "grill_exit 的事实、runnable 问题或外部决策路由不完整");
28
28
  const git = data.git_authorization;
29
29
  for (const action of ["commit", "push"]) {
30
30
  const prefix = action === "commit" ? "commit" : "push";
@@ -40,8 +40,8 @@ function validateMattContract(data) {
40
40
  function validateInvocationBoundary(data) {
41
41
  const boundary = data.matt_invocation_boundary;
42
42
  const expectedUserInvoked = ["ask-matt", "grill-me", "grill-with-docs", "handoff", "implement", "improve-codebase-architecture", "loop-me", "setup-matt-pocock-skills", "setup-ts-deep-modules", "teach", "to-questionnaire", "to-spec", "to-tickets", "triage", "wait-what", "wayfinder", "writing-beats", "writing-fragments", "writing-shape"];
43
- const expectedModelInvoked = ["code-review", "codebase-design", "diagnosing-bugs", "domain-modeling", "grilling", "migrate-to-shoehorn", "prototype", "research", "resolving-merge-conflicts", "scaffold-exercises", "setup-pre-commit", "tdd", "writing-for-agents"];
44
- const expectedLifecycleModelInvoked = ["code-review", "codebase-design", "diagnosing-bugs", "domain-modeling", "grilling", "prototype", "research", "tdd"];
43
+ const expectedModelInvoked = ["code-review", "codebase-design", "diagnosing-bugs", "domain-modeling", "grilling", "migrate-to-shoehorn", "prototype", "resolving-merge-conflicts", "scaffold-exercises", "setup-pre-commit", "tdd", "writing-for-agents", "yss-research"];
44
+ const expectedLifecycleModelInvoked = ["code-review", "codebase-design", "diagnosing-bugs", "domain-modeling", "grilling", "prototype", "tdd", "yss-research"];
45
45
  ensure(JSON.stringify(boundary?.user_invoked_skills) === JSON.stringify(expectedUserInvoked), "Matt user-invoked skills 清单不完整或已漂移");
46
46
  ensure(JSON.stringify(boundary?.lifecycle_managed_user_entries) === JSON.stringify(["setup-matt-pocock-skills", "grill-with-docs", "to-spec", "to-tickets", "implement"]), "生命周期管理的显式用户入口清单不完整");
47
47
  ensure(boundary?.lifecycle_may_invoke_user_invoked === false && boundary?.formal_artifact_owner === "explicit-user-entry", "生命周期仍可能自动调用 user-invoked skill 或产出其正式资产");
@@ -59,7 +59,11 @@ function validateInvocationBoundary(data) {
59
59
  ensure(JSON.stringify(native?.user_confirmation_required_at) === JSON.stringify(["spec-baseline", "prototype-confirmation", "openapi-freeze", "merge-or-release"]), "生命周期人工门禁集合已漂移");
60
60
  const routes = data.work_unit_routes;
61
61
  ensure(routes?.["work-unit.discovery-requirements"]?.skills?.includes("grilling") && routes?.["work-unit.discovery-requirements"]?.skills?.includes("domain-modeling"), "需求分析工作单元缺少 grilling/domain-modeling");
62
- ensure(routes?.["work-unit.discovery-opportunity"]?.route_by?.market_or_competitor_fact === "competitive-intelligence" && routes["work-unit.discovery-opportunity"].route_by.technical_or_standard_fact === "research", "机会调研事实路由不准确");
62
+ ensure(routes?.["work-unit.discovery-opportunity"]?.route_by?.market_or_competitor_fact === "competitive-intelligence" && routes["work-unit.discovery-opportunity"].route_by.technical_or_standard_fact === "yss-research:technical-evidence" && routes["work-unit.discovery-opportunity"].route_by.strategy_fact === "yss-research:strategy-evidence", "机会调研事实路由不准确");
63
+ const strategyResearch = routes?.["work-unit.domain-strategy-design"]?.research_contract;
64
+ ensure(strategyResearch?.profile === "strategy-evidence" && strategyResearch.mode_before_gate === "evidence-audited" && strategyResearch.artifact_owner === "yss-research" && strategyResearch.downstream_owner === "yss-stage-decision", "领域战略研究合同缺少 profile、门禁前审计或资产所有权边界");
65
+ const stageDecisionResearch = routes?.["work-unit.stage-decision"]?.research_contract;
66
+ ensure(routes?.["work-unit.stage-decision"]?.skills?.includes("yss-research") && stageDecisionResearch?.profile === "strategy-evidence" && stageDecisionResearch.mode_before_gate === "evidence-audited" && stageDecisionResearch.artifact_owner === "yss-research" && stageDecisionResearch.downstream_owner === "yss-stage-decision", "阶段决策工作单元缺少 evidence-audited 战略研究合同");
63
67
  ensure(routes?.["work-unit.slice-implementation"]?.skills?.includes("tdd") && routes?.["work-unit.slice-implementation"]?.skills?.includes("yss-ui") && routes?.["work-unit.slice-implementation"]?.skills?.includes("yss-ui-business-page-generation"), "原生实现工作单元缺少 TDD、UI 或业务页面生成路由");
64
68
  const frontendRoute = routes?.["work-unit.slice-implementation"]?.frontend_route;
65
69
  ensure(frontendRoute?.primary_skill === "yss-ui" && frontendRoute?.page_generation_skill === "yss-ui-business-page-generation" && frontendRoute?.page_orchestration_skill === "yss-page-module-development", "前端实现路由缺少 yss-ui 主入口、业务页面生成或页面编排技能");