create-yss-spec 3.4.1 → 3.4.3

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 (373) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -2
  3. package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
  4. package/template/.agents/skills/archify/SKILL.md +1 -1
  5. package/template/.agents/skills/code-review/SKILL.md +12 -8
  6. package/template/.agents/skills/file-export-download/SKILL.md +1 -1
  7. package/template/.agents/skills/frontend-commit/SKILL.md +1 -1
  8. package/template/.agents/skills/java-backend-commit/SKILL.md +1 -1
  9. package/template/.agents/skills/llm-wiki/SKILL.md +1 -6
  10. package/template/.agents/skills/lombok/SKILL.md +10 -336
  11. package/template/.agents/skills/lombok/references/annotation-examples.md +307 -0
  12. package/template/.agents/skills/maintaining-skills/SKILL.md +2 -1
  13. package/template/.agents/skills/maintaining-skills/references/authoring.md +27 -0
  14. package/template/.agents/skills/mapstruct/SKILL.md +11 -318
  15. package/template/.agents/skills/mapstruct/references/annotation-examples.md +300 -0
  16. package/template/.agents/skills/page-form-module/SKILL.md +1 -1
  17. package/template/.agents/skills/page-skeleton/SKILL.md +1 -1
  18. package/template/.agents/skills/theme-token-usage/SKILL.md +1 -1
  19. package/template/.agents/skills/to-tickets/SKILL.md +1 -1
  20. package/template/.agents/skills/using-git-worktrees/SKILL.md +1 -1
  21. package/template/.agents/skills/wayfinder/SKILL.md +1 -1
  22. package/template/.agents/skills/yedit-table-usage/SKILL.md +1 -1
  23. package/template/.agents/skills/yss-api-integration/SKILL.md +1 -1
  24. package/template/.agents/skills/yss-audit-log/SKILL.md +1 -1
  25. package/template/.agents/skills/yss-cache/SKILL.md +1 -1
  26. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +15 -2
  27. package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +1 -1
  28. package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +15 -10
  29. package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +3 -3
  30. package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -3
  31. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +5 -1
  32. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +5 -2
  33. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +13 -10
  34. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +2 -1
  35. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +35 -3
  36. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +1 -1
  37. package/template/.agents/skills/yss-distributed-id/SKILL.md +1 -1
  38. package/template/.agents/skills/yss-dto/SKILL.md +5 -1
  39. package/template/.agents/skills/yss-excel-mvc/SKILL.md +1 -1
  40. package/template/.agents/skills/yss-exception/SKILL.md +5 -1
  41. package/template/.agents/skills/yss-formily-schema-generator/SKILL.md +1 -1
  42. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +13 -5
  43. package/template/.agents/skills/yss-implementation-contract-compiler/agents/openai.yaml +1 -1
  44. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -0
  45. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +214 -0
  46. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +66 -204
  47. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/SKILL.md +15 -2
  48. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +28 -19
  49. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +10 -10
  50. package/template/.agents/skills/yss-mybatis/SKILL.md +4 -0
  51. package/template/.agents/skills/yss-openapi-draft-review/SKILL.md +1 -1
  52. package/template/.agents/skills/yss-openapi-governance/SKILL.md +1 -1
  53. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +4 -4
  54. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +7 -2
  55. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +4 -0
  56. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +2 -0
  57. package/template/.agents/skills/yss-product-lifecycle/references/user-decisions.md +23 -3
  58. package/template/.agents/skills/yss-prototype-stage/SKILL.md +1 -1
  59. package/template/.agents/skills/yss-repository/SKILL.md +4 -0
  60. package/template/.agents/skills/yss-research/SKILL.md +1 -1
  61. package/template/.agents/skills/yss-resilience4j/SKILL.md +1 -1
  62. package/template/.agents/skills/yss-security-algorithm/SKILL.md +1 -1
  63. package/template/.agents/skills/yss-tactical-design/SKILL.md +4 -0
  64. package/template/.agents/skills/yss-technical-design/SKILL.md +4 -0
  65. package/template/.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs +2 -2
  66. package/template/.agents/skills/yss-ui/SKILL.md +1 -1
  67. package/template/.agents/skills/yss-ui-business-page-generation/SKILL.md +1 -1
  68. package/template/.agents/skills/yss-up-springboot3/SKILL.md +1 -1
  69. package/template/.agents/skills/yss-use-table-height/SKILL.md +1 -1
  70. package/template/.agents/skills/yss-use-tree-height/SKILL.md +1 -1
  71. package/template/.agents/skills/yss-userinfo/SKILL.md +1 -1
  72. package/template/.agents/skills/yss-validation/SKILL.md +5 -1
  73. package/template/.agents/skills/yss-web-controller/SKILL.md +5 -1
  74. package/template/.agents/skills/yss-web-controller/scripts/generate_controller.mjs +5 -2
  75. package/template/.agents/skills/ytable-usage/SKILL.md +1 -1
  76. package/template/.agents/skills/ytree-usage/SKILL.md +1 -1
  77. package/template/.codex/skills/archify/SKILL.md +1 -1
  78. package/template/.codex/skills/code-review/SKILL.md +12 -8
  79. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +1 -1
  80. package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +1 -1
  81. package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +1 -1
  82. package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +1 -1
  83. package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +1 -1
  84. package/template/.codex/skills/data-analytics/skills/index/SKILL.md +1 -1
  85. package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +1 -1
  86. package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +1 -1
  87. package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +1 -1
  88. package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +1 -1
  89. package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +1 -1
  90. package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +1 -1
  91. package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +1 -1
  92. package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +1 -1
  93. package/template/.codex/skills/file-export-download/SKILL.md +1 -1
  94. package/template/.codex/skills/frontend-commit/SKILL.md +1 -1
  95. package/template/.codex/skills/java-backend-commit/SKILL.md +1 -1
  96. package/template/.codex/skills/llm-wiki/SKILL.md +1 -6
  97. package/template/.codex/skills/lombok/SKILL.md +10 -336
  98. package/template/.codex/skills/lombok/references/annotation-examples.md +307 -0
  99. package/template/.codex/skills/maintaining-skills/SKILL.md +2 -1
  100. package/template/.codex/skills/maintaining-skills/references/authoring.md +27 -0
  101. package/template/.codex/skills/mapstruct/SKILL.md +11 -318
  102. package/template/.codex/skills/mapstruct/references/annotation-examples.md +300 -0
  103. package/template/.codex/skills/page-form-module/SKILL.md +1 -1
  104. package/template/.codex/skills/page-skeleton/SKILL.md +1 -1
  105. package/template/.codex/skills/product-design/skills/audit/SKILL.md +1 -1
  106. package/template/.codex/skills/product-design/skills/design-qa/SKILL.md +1 -1
  107. package/template/.codex/skills/product-design/skills/get-context/SKILL.md +1 -1
  108. package/template/.codex/skills/product-design/skills/ideate/SKILL.md +1 -1
  109. package/template/.codex/skills/product-design/skills/image-to-code/SKILL.md +1 -1
  110. package/template/.codex/skills/product-design/skills/index/SKILL.md +1 -1
  111. package/template/.codex/skills/product-design/skills/prototype/SKILL.md +1 -1
  112. package/template/.codex/skills/product-design/skills/research/SKILL.md +1 -1
  113. package/template/.codex/skills/product-design/skills/user-context/SKILL.md +1 -1
  114. package/template/.codex/skills/theme-token-usage/SKILL.md +1 -1
  115. package/template/.codex/skills/to-tickets/SKILL.md +1 -1
  116. package/template/.codex/skills/using-git-worktrees/SKILL.md +1 -1
  117. package/template/.codex/skills/wayfinder/SKILL.md +1 -1
  118. package/template/.codex/skills/yedit-table-usage/SKILL.md +1 -1
  119. package/template/.codex/skills/yss-api-integration/SKILL.md +1 -1
  120. package/template/.codex/skills/yss-audit-log/SKILL.md +1 -1
  121. package/template/.codex/skills/yss-cache/SKILL.md +1 -1
  122. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +15 -2
  123. package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +1 -1
  124. package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +15 -10
  125. package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +3 -3
  126. package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -3
  127. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +5 -1
  128. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +5 -2
  129. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +13 -10
  130. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +2 -1
  131. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +35 -3
  132. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +1 -1
  133. package/template/.codex/skills/yss-distributed-id/SKILL.md +1 -1
  134. package/template/.codex/skills/yss-dto/SKILL.md +5 -1
  135. package/template/.codex/skills/yss-excel-mvc/SKILL.md +1 -1
  136. package/template/.codex/skills/yss-exception/SKILL.md +5 -1
  137. package/template/.codex/skills/yss-formily-schema-generator/SKILL.md +1 -1
  138. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +13 -5
  139. package/template/.codex/skills/yss-implementation-contract-compiler/agents/openai.yaml +1 -1
  140. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -0
  141. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +214 -0
  142. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +66 -204
  143. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/SKILL.md +15 -2
  144. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +28 -19
  145. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +10 -10
  146. package/template/.codex/skills/yss-mybatis/SKILL.md +4 -0
  147. package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +1 -1
  148. package/template/.codex/skills/yss-openapi-governance/SKILL.md +1 -1
  149. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +4 -4
  150. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +7 -2
  151. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +4 -0
  152. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +2 -0
  153. package/template/.codex/skills/yss-product-lifecycle/references/user-decisions.md +23 -3
  154. package/template/.codex/skills/yss-prototype-stage/SKILL.md +1 -1
  155. package/template/.codex/skills/yss-repository/SKILL.md +4 -0
  156. package/template/.codex/skills/yss-research/SKILL.md +1 -1
  157. package/template/.codex/skills/yss-resilience4j/SKILL.md +1 -1
  158. package/template/.codex/skills/yss-security-algorithm/SKILL.md +1 -1
  159. package/template/.codex/skills/yss-tactical-design/SKILL.md +4 -0
  160. package/template/.codex/skills/yss-technical-design/SKILL.md +4 -0
  161. package/template/.codex/skills/yss-technical-design/scripts/validate-technical-design.mjs +2 -2
  162. package/template/.codex/skills/yss-ui/SKILL.md +1 -1
  163. package/template/.codex/skills/yss-ui-business-page-generation/SKILL.md +1 -1
  164. package/template/.codex/skills/yss-up-springboot3/SKILL.md +1 -1
  165. package/template/.codex/skills/yss-use-table-height/SKILL.md +1 -1
  166. package/template/.codex/skills/yss-use-tree-height/SKILL.md +1 -1
  167. package/template/.codex/skills/yss-userinfo/SKILL.md +1 -1
  168. package/template/.codex/skills/yss-validation/SKILL.md +5 -1
  169. package/template/.codex/skills/yss-web-controller/SKILL.md +5 -1
  170. package/template/.codex/skills/yss-web-controller/scripts/generate_controller.mjs +5 -2
  171. package/template/.codex/skills/ytable-usage/SKILL.md +1 -1
  172. package/template/.codex/skills/ytree-usage/SKILL.md +1 -1
  173. package/template/.cursor/skills/archify/SKILL.md +1 -1
  174. package/template/.cursor/skills/code-review/SKILL.md +12 -8
  175. package/template/.cursor/skills/file-export-download/SKILL.md +1 -1
  176. package/template/.cursor/skills/frontend-commit/SKILL.md +1 -1
  177. package/template/.cursor/skills/java-backend-commit/SKILL.md +1 -1
  178. package/template/.cursor/skills/llm-wiki/SKILL.md +1 -6
  179. package/template/.cursor/skills/lombok/SKILL.md +10 -336
  180. package/template/.cursor/skills/lombok/references/annotation-examples.md +307 -0
  181. package/template/.cursor/skills/maintaining-skills/SKILL.md +2 -1
  182. package/template/.cursor/skills/maintaining-skills/references/authoring.md +27 -0
  183. package/template/.cursor/skills/mapstruct/SKILL.md +11 -318
  184. package/template/.cursor/skills/mapstruct/references/annotation-examples.md +300 -0
  185. package/template/.cursor/skills/page-form-module/SKILL.md +1 -1
  186. package/template/.cursor/skills/page-skeleton/SKILL.md +1 -1
  187. package/template/.cursor/skills/theme-token-usage/SKILL.md +1 -1
  188. package/template/.cursor/skills/to-tickets/SKILL.md +1 -1
  189. package/template/.cursor/skills/using-git-worktrees/SKILL.md +1 -1
  190. package/template/.cursor/skills/wayfinder/SKILL.md +1 -1
  191. package/template/.cursor/skills/yedit-table-usage/SKILL.md +1 -1
  192. package/template/.cursor/skills/yss-api-integration/SKILL.md +1 -1
  193. package/template/.cursor/skills/yss-audit-log/SKILL.md +1 -1
  194. package/template/.cursor/skills/yss-cache/SKILL.md +1 -1
  195. package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +15 -2
  196. package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +1 -1
  197. package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +15 -10
  198. package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +3 -3
  199. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -3
  200. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +5 -1
  201. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +5 -2
  202. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +13 -10
  203. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +2 -1
  204. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +35 -3
  205. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +1 -1
  206. package/template/.cursor/skills/yss-distributed-id/SKILL.md +1 -1
  207. package/template/.cursor/skills/yss-dto/SKILL.md +5 -1
  208. package/template/.cursor/skills/yss-excel-mvc/SKILL.md +1 -1
  209. package/template/.cursor/skills/yss-exception/SKILL.md +5 -1
  210. package/template/.cursor/skills/yss-formily-schema-generator/SKILL.md +1 -1
  211. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +13 -5
  212. package/template/.cursor/skills/yss-implementation-contract-compiler/agents/openai.yaml +1 -1
  213. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -0
  214. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +214 -0
  215. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +66 -204
  216. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/SKILL.md +15 -2
  217. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +28 -19
  218. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +10 -10
  219. package/template/.cursor/skills/yss-mybatis/SKILL.md +4 -0
  220. package/template/.cursor/skills/yss-openapi-draft-review/SKILL.md +1 -1
  221. package/template/.cursor/skills/yss-openapi-governance/SKILL.md +1 -1
  222. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +4 -4
  223. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +7 -2
  224. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +4 -0
  225. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +2 -0
  226. package/template/.cursor/skills/yss-product-lifecycle/references/user-decisions.md +23 -3
  227. package/template/.cursor/skills/yss-prototype-stage/SKILL.md +1 -1
  228. package/template/.cursor/skills/yss-repository/SKILL.md +4 -0
  229. package/template/.cursor/skills/yss-research/SKILL.md +1 -1
  230. package/template/.cursor/skills/yss-resilience4j/SKILL.md +1 -1
  231. package/template/.cursor/skills/yss-security-algorithm/SKILL.md +1 -1
  232. package/template/.cursor/skills/yss-tactical-design/SKILL.md +4 -0
  233. package/template/.cursor/skills/yss-technical-design/SKILL.md +4 -0
  234. package/template/.cursor/skills/yss-technical-design/scripts/validate-technical-design.mjs +2 -2
  235. package/template/.cursor/skills/yss-ui/SKILL.md +1 -1
  236. package/template/.cursor/skills/yss-ui-business-page-generation/SKILL.md +1 -1
  237. package/template/.cursor/skills/yss-up-springboot3/SKILL.md +1 -1
  238. package/template/.cursor/skills/yss-use-table-height/SKILL.md +1 -1
  239. package/template/.cursor/skills/yss-use-tree-height/SKILL.md +1 -1
  240. package/template/.cursor/skills/yss-userinfo/SKILL.md +1 -1
  241. package/template/.cursor/skills/yss-validation/SKILL.md +5 -1
  242. package/template/.cursor/skills/yss-web-controller/SKILL.md +5 -1
  243. package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.mjs +5 -2
  244. package/template/.cursor/skills/ytable-usage/SKILL.md +1 -1
  245. package/template/.cursor/skills/ytree-usage/SKILL.md +1 -1
  246. package/template/.pi/skills/archify/SKILL.md +1 -1
  247. package/template/.pi/skills/code-review/SKILL.md +12 -8
  248. package/template/.pi/skills/file-export-download/SKILL.md +1 -1
  249. package/template/.pi/skills/frontend-commit/SKILL.md +1 -1
  250. package/template/.pi/skills/java-backend-commit/SKILL.md +1 -1
  251. package/template/.pi/skills/llm-wiki/SKILL.md +1 -6
  252. package/template/.pi/skills/lombok/SKILL.md +10 -336
  253. package/template/.pi/skills/lombok/references/annotation-examples.md +307 -0
  254. package/template/.pi/skills/maintaining-skills/SKILL.md +2 -1
  255. package/template/.pi/skills/maintaining-skills/references/authoring.md +27 -0
  256. package/template/.pi/skills/mapstruct/SKILL.md +11 -318
  257. package/template/.pi/skills/mapstruct/references/annotation-examples.md +300 -0
  258. package/template/.pi/skills/page-form-module/SKILL.md +1 -1
  259. package/template/.pi/skills/page-skeleton/SKILL.md +1 -1
  260. package/template/.pi/skills/theme-token-usage/SKILL.md +1 -1
  261. package/template/.pi/skills/to-tickets/SKILL.md +1 -1
  262. package/template/.pi/skills/using-git-worktrees/SKILL.md +1 -1
  263. package/template/.pi/skills/wayfinder/SKILL.md +1 -1
  264. package/template/.pi/skills/yedit-table-usage/SKILL.md +1 -1
  265. package/template/.pi/skills/yss-api-integration/SKILL.md +1 -1
  266. package/template/.pi/skills/yss-audit-log/SKILL.md +1 -1
  267. package/template/.pi/skills/yss-cache/SKILL.md +1 -1
  268. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +15 -2
  269. package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +1 -1
  270. package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +15 -10
  271. package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +3 -3
  272. package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -3
  273. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +5 -1
  274. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +5 -2
  275. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +13 -10
  276. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +2 -1
  277. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +35 -3
  278. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +1 -1
  279. package/template/.pi/skills/yss-distributed-id/SKILL.md +1 -1
  280. package/template/.pi/skills/yss-dto/SKILL.md +5 -1
  281. package/template/.pi/skills/yss-excel-mvc/SKILL.md +1 -1
  282. package/template/.pi/skills/yss-exception/SKILL.md +5 -1
  283. package/template/.pi/skills/yss-formily-schema-generator/SKILL.md +1 -1
  284. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +13 -5
  285. package/template/.pi/skills/yss-implementation-contract-compiler/agents/openai.yaml +1 -1
  286. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -0
  287. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +214 -0
  288. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +66 -204
  289. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/SKILL.md +15 -2
  290. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +28 -19
  291. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +10 -10
  292. package/template/.pi/skills/yss-mybatis/SKILL.md +4 -0
  293. package/template/.pi/skills/yss-openapi-draft-review/SKILL.md +1 -1
  294. package/template/.pi/skills/yss-openapi-governance/SKILL.md +1 -1
  295. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +4 -4
  296. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +7 -2
  297. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +4 -0
  298. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +2 -0
  299. package/template/.pi/skills/yss-product-lifecycle/references/user-decisions.md +23 -3
  300. package/template/.pi/skills/yss-prototype-stage/SKILL.md +1 -1
  301. package/template/.pi/skills/yss-repository/SKILL.md +4 -0
  302. package/template/.pi/skills/yss-research/SKILL.md +1 -1
  303. package/template/.pi/skills/yss-resilience4j/SKILL.md +1 -1
  304. package/template/.pi/skills/yss-security-algorithm/SKILL.md +1 -1
  305. package/template/.pi/skills/yss-tactical-design/SKILL.md +4 -0
  306. package/template/.pi/skills/yss-technical-design/SKILL.md +4 -0
  307. package/template/.pi/skills/yss-technical-design/scripts/validate-technical-design.mjs +2 -2
  308. package/template/.pi/skills/yss-ui/SKILL.md +1 -1
  309. package/template/.pi/skills/yss-ui-business-page-generation/SKILL.md +1 -1
  310. package/template/.pi/skills/yss-up-springboot3/SKILL.md +1 -1
  311. package/template/.pi/skills/yss-use-table-height/SKILL.md +1 -1
  312. package/template/.pi/skills/yss-use-tree-height/SKILL.md +1 -1
  313. package/template/.pi/skills/yss-userinfo/SKILL.md +1 -1
  314. package/template/.pi/skills/yss-validation/SKILL.md +5 -1
  315. package/template/.pi/skills/yss-web-controller/SKILL.md +5 -1
  316. package/template/.pi/skills/yss-web-controller/scripts/generate_controller.mjs +5 -2
  317. package/template/.pi/skills/ytable-usage/SKILL.md +1 -1
  318. package/template/.pi/skills/ytree-usage/SKILL.md +1 -1
  319. package/template/AGENTS.md +1 -0
  320. package/template/CONTEXT.md +2 -1
  321. package/template/docs/agents/backend-architecture-profiles.md +1 -1
  322. package/template/docs/agents/digital-human-roles.yaml +25 -1
  323. package/template/docs/agents/issue-tracker.md +2 -0
  324. package/template/docs/engineering/backend-platforms.json +174 -0
  325. package/template/docs/engineering/backend-platforms.md +77 -0
  326. package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +1 -0
  327. package/template/docs/process/harness-process-tailoring.md +9 -1
  328. package/template/docs/process/lifecycle-artifact-map.md +4 -4
  329. package/template/docs/process/lifecycle-registry-baseline.json +1 -1
  330. package/template/docs/process/lifecycle-registry.yaml +4 -4
  331. package/template/docs/process/schemas/backend-architecture-identity.schema.json +91 -0
  332. package/template/docs/process/schemas/digital-human-task-package.schema.json +2 -1
  333. package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +4 -0
  334. package/template/docs/process/schemas/project-scaffold-contract.schema.json +602 -83
  335. package/template/docs/process/schemas/scaffold-architecture-decisions.schema.json +323 -39
  336. package/template/docs/process/schemas/slice-implementation-contract-v3.schema.json +925 -0
  337. package/template/docs/templates/approval-record-template.yaml +2 -0
  338. package/template/docs/templates/vertical-slice-ticket-template.md +22 -31
  339. package/template/scripts/backend-platforms +14 -0
  340. package/template/scripts/lib/approval-record.mjs +32 -6
  341. package/template/scripts/lib/approved-execution-context.mjs +31 -8
  342. package/template/scripts/lib/backend-architecture.mjs +6 -0
  343. package/template/scripts/lib/backend-platform-provenance.mjs +67 -0
  344. package/template/scripts/lib/backend-platform-verification.mjs +210 -0
  345. package/template/scripts/lib/backend-platform.mjs +175 -0
  346. package/template/scripts/lib/backend-scaffold-prerequisites.mjs +1 -1
  347. package/template/scripts/lib/decision-continuation.mjs +79 -0
  348. package/template/scripts/lib/delivery-preflight.mjs +6 -5
  349. package/template/scripts/lib/digital-human-roles.mjs +10 -0
  350. package/template/scripts/lib/frontend-delivery-boundary.mjs +3 -0
  351. package/template/scripts/lib/implementation-contract-compiler.mjs +42 -9
  352. package/template/scripts/lib/lifecycle-controls.mjs +2 -2
  353. package/template/scripts/lib/lifecycle-transition.mjs +3 -1
  354. package/template/scripts/lib/plan-spec-entry.mjs +3 -3
  355. package/template/scripts/lib/scaffold-local-database.mjs +1 -0
  356. package/template/scripts/lib/slice-applicability.mjs +10 -0
  357. package/template/scripts/lib/slice-contract-preparation.mjs +257 -0
  358. package/template/scripts/lib/slice-contract-views.mjs +52 -0
  359. package/template/scripts/lib/slice-contract.mjs +211 -0
  360. package/template/scripts/lib/slice-repositories.mjs +49 -0
  361. package/template/scripts/lib/slice-task-package.mjs +66 -0
  362. package/template/scripts/lib/strategic-handoff.mjs +12 -1
  363. package/template/scripts/lib/task-package.mjs +6 -2
  364. package/template/scripts/lib/technical-design-boundary.mjs +1 -0
  365. package/template/scripts/lib/user-decision-reuse.mjs +1 -0
  366. package/template/scripts/lib/user-decision.mjs +4 -1
  367. package/template/scripts/preflight-delivery +2 -2
  368. package/template/scripts/slice-contract +30 -0
  369. package/template/scripts/verify-backend-platform-matrix +69 -0
  370. package/template/scripts/verify-scaffold-architecture-decisions +5 -0
  371. package/template/scripts/verify-user-decision +7 -2
  372. package/template/skills-lock.json +60 -55
  373. package/template.snapshot.json +3 -3
@@ -16,6 +16,8 @@ notes: 会签必须可读取;起草者不得出现在 countersigner_role_ids
16
16
  subject_ref: null
17
17
  approval_scope: []
18
18
  user_decision_ref: null
19
+ # 已有交付范围授权时可引用独立当前审查支持的延续证明;不能仅填写 approved。
20
+ continuation_ref: null
19
21
  # 聚合门禁 / 内部专业审查绑定的当前主体字节 SHA-256(64 位十六进制)。
20
22
  subject_digest: null
21
23
  # 专业检查必须与起草者为不同执行实例;不仅仅比较职称。
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  status: ready-for-human
3
+ requirement_version: v1
3
4
  ---
4
5
 
5
6
  # 垂直切片 Ticket:<标题>
6
7
 
7
- 填写前读取[中文写作规范](../process/document-writing.md)和 [Ticket 对照](examples/lifecycle-writing-examples.md#3-ticket把交付行为与实现就绪区分清楚)。状态只在 frontmatter 维护,正文记录依据。
8
+ 填写前读取[中文写作规范](../process/document-writing.md)和 [Ticket 对照](examples/lifecycle-writing-examples.md#3-ticket把交付行为与实现就绪区分清楚)。冻结前在 frontmatter 记录状态。新 Slice 绑定本文件的需求版本与原始字节;冻结后 frontmatter 仅代表冻结时状态,当前执行状态由项目已配置的主 tracker 维护,平台不可用时使用现有任务包和待发布记录。不得回写本文件的状态、验收勾选或执行结果。需求变化另存新版本并重新编译、审查与批准,旧文件和批准不自动迁移。
8
9
 
9
10
  ## 父级
10
11
 
@@ -33,11 +34,11 @@ status: ready-for-human
33
34
 
34
35
  ## 验收标准
35
36
 
36
- 每项写条件、操作和可观察结果,关联上游规则;保留必要例外。测试未执行前保持未勾选。
37
+ 每项写条件、操作和可观察结果,关联上游规则;保留必要例外。需求基线不记录执行结果;实际验收结论写入 tracker / 任务包。
37
38
 
38
- - [ ] 标准 1
39
- - [ ] 标准 2
40
- - [ ] 标准 3
39
+ - AC-1:标准 1
40
+ - AC-2:标准 2
41
+ - AC-3:标准 3
41
42
 
42
43
  ## 测试 Seam
43
44
 
@@ -55,33 +56,19 @@ status: ready-for-human
55
56
  | contract_id | |
56
57
  | contract_version | |
57
58
  | contract_ref | |
58
- | 实现合同编译器 状态 | draft / blocked / ready-for-lifecycle-review |
59
- | 生命周期批准状态 | pending / approved / rejected |
59
+ | 编译与批准记录 | 在现有 checkpoint / 任务包中维护,不回写冻结 Ticket |
60
60
  | suggested_owner_role_id | `role.frontend-engineer` / `role.backend-engineer` / `role.test-engineer`(实现合同编译器 建议,编排器派活) |
61
61
  | Build Architecture Checklist | |
62
62
 
63
- > 实现合同编译器 不得自行将合同批准或将本 Ticket 推进为 `ready-for-agent`。只有生命周期编排器核验并持久化当前版本合同、清除阻塞边后,才能推进状态。
63
+ > 实现合同编译器 不得自行将合同批准或将本 Ticket 推进为 `ready-for-agent`。只有生命周期编排器核验并持久化当前版本合同、清除阻塞边后,才能在主 tracker 推进执行状态。
64
64
 
65
- ### 工作单元
65
+ ### 工作单元与工程约束
66
66
 
67
- | work_unit_id | 验收行为 / 目标 | primary_skill | supporting_skills | tdd_mode | allowed_write_paths | expected_evidence | verification_commands | Execution Result 引用 | 状态 |
68
- |---|---|---|---|---|---|---|---|---|---|
69
- | | | | | behavior-tdd / controlled-generation | | | | | pending / running / blocked / completed / stale |
67
+ 新切片使用 Slice v3。Ticket 保留行为、验收、测试 seam 与合同引用;工作单元、Skill、写范围、验证命令在权威 YAML 中保存一次,不再在此复制表格。
70
68
 
71
- `controlled-generation` 仅允许机械脚手架、样板、冻结客户端或配置,并记录 exception reason、生成器输入和生成后行为测试;业务规则、状态机、事务、权限、错误映射、复杂查询和用户可见交互必须使用 `behavior-tdd`。只有需求明确包含权限业务行为时才把它写入工作单元,不另设安全 / 权限姿态。
69
+ Agent 使用 `scripts/slice-contract view <合同.yaml>` 展示审阅说明,使用 `--unit <id>` 展示任务和完整工程约束。本切片新增细化、例外或缺口由 Agent 整理,独立专业审查核验;已有用户确认覆盖且无实质变化时复用。任务进度和 Execution Result 引用进入现有 tracker / 任务包。
72
70
 
73
- ## YSS 技能与后端实现合同
74
-
75
- > 涉及后端时必须填写;不涉及时写明 `not-applicable`。不得只写“符合 YSS”。
76
-
77
- | 影响面 | 必需 skill | 需要 / 不需要的理由 | 预期证据 |
78
- |---|---|---|---|
79
- | Domain / 领域行为 | `yss-domain` | | |
80
- | Application / 用例编排 | `yss-application` | | |
81
- | Infrastructure / Repository | `yss-repository` / `yss-mybatis` | | |
82
- | Web Adapter / DTO | `yss-web-controller` / `yss-dto` | | |
83
- | POJO / 对象转换 | `lombok` / `mapstruct` | | |
84
- | Java 规范 | `alibaba-java-code-style` | | |
71
+ 涉及后端时,适用设计从已批准 Technical Design 绑定取得。`controlled-generation` 只适用于机械生成,并保留例外与验证;业务行为使用 `behavior-tdd`。
85
72
 
86
73
  ### 后端阻断规则
87
74
 
@@ -98,7 +85,7 @@ status: ready-for-human
98
85
 
99
86
  列出实际依赖、未决项、责任人和解除条件;尚未核验时写“待核验”。只有确认没有阻塞时才写“无”,无依赖不等于已通过实现就绪核验。
100
87
 
101
- ## 重路由状态
88
+ ## 重路由记录位置
102
89
 
103
90
  | 字段 | 内容 |
104
91
  |---|---|
@@ -110,19 +97,21 @@ status: ready-for-human
110
97
  | return_stage | 实现合同编译器 / Architecture Re-check / 产品设计 / OpenAPI Draft-Review-Freeze / 系统或数据架构 / other |
111
98
  | recovery_conditions | |
112
99
 
113
- 出现 `drift`、`violation` 或非空 `new_impacts` 时暂停受影响工作单元,不得先完成代码再补合同;更新合同版本并通过生命周期审查后才能恢复。
100
+ 本表作为 tracker / 任务包的记录字段说明,冻结后不在本文件填写。出现 `drift`、`violation` 或非空 `new_impacts` 时暂停受影响工作单元,不得先完成代码再补合同;更新合同版本并通过生命周期审查后才能恢复。
114
101
 
115
102
  ## 会签
116
103
 
104
+ 冻结后会签结果保存在现有批准记录与 checkpoint,不修改以下冻结时引用。
105
+
117
106
  | 门禁 | 记录路径 | 会签角色 | 状态 |
118
107
  |---|---|---|---|
119
108
  | | `docs/.scratch/<feature>/gates/<gate-id>-approval.yaml` | 见 `docs/agents/digital-human-roles.yaml` | pending / approved / blocked / not-applicable |
120
109
 
121
110
  ## 状态
122
111
 
123
- 状态依据:<当前版本合同、批准和就绪核验记录引用,以及尚未关闭的阻塞>。
112
+ 冻结时状态依据:<当时的合同、批准和就绪核验记录引用,以及尚未关闭的阻塞>。当前状态从主 tracker / 任务包读取。
124
113
 
125
- > 默认 frontmatter 为 `ready-for-human`。仅在生命周期确认合同已批准且当前、阻塞已清除、全部适用门禁及就绪条件通过后更新为 `ready-for-agent`;本节不另维护第二个状态值。
114
+ > 默认 frontmatter 为 `ready-for-human`。仅在生命周期确认合同已批准且当前、阻塞已清除、全部适用门禁及就绪条件通过后在主 tracker 更新为 `ready-for-agent`;冻结文件不更新。
126
115
 
127
116
  ## AI / 人工审查点
128
117
 
@@ -132,12 +121,14 @@ status: ready-for-human
132
121
 
133
122
  ## 完成定义
134
123
 
124
+ 以下为完成判据;检查结果和勾选进入 tracker / 任务包,不回写本文件。
125
+
135
126
  - [ ] 如有需要,已基于冻结 OpenAPI Spec 拆分切片
136
127
  - [ ] 实现完成
137
128
  - [ ] 已新增测试且测试通过
138
129
  - [ ] 已移除调试 / 原型代码
139
- - [ ] 已回勾 `Backend Slice Implementation Contract` 和 `Build Architecture Checklist`
140
- - [ ] 已回勾当前 `contract_id` / `contract_version`、全部工作单元和对应 `YSS Skill Execution Result`
130
+ - [ ] 已在任务包核验 `Backend Slice Implementation Contract` 和 `Build Architecture Checklist`
131
+ - [ ] 已在任务包核验当前 `contract_id` / `contract_version`、全部工作单元和对应 `YSS Skill Execution Result`
141
132
  - [ ] 实际 changed files 均在合同允许路径内,预期证据齐全,验证结果包含执行时间
142
133
  - [ ] `new_impacts`、`drift`、`violation` 和重路由状态均有明确结论,合同未处于 `stale`
143
134
  - [ ] 如领域或架构决策变化,已更新 `CONTEXT.md` / ADR;新增业务术语含 PascalCase `英文标识`,代码与契约字段能追溯到该词干
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import { loadBackendPlatforms, platformBinding, resolveBackendPlatform } from "./lib/backend-platform.mjs";
3
+ try {
4
+ const catalog = loadBackendPlatforms();
5
+ const rows = catalog.profiles.map(profile => {
6
+ const combinations = catalog.compatibility.filter(entry => entry.profile_id === profile.id && entry.spring_boot_version === profile.spring_boot_version).map(entry => {
7
+ try { resolveBackendPlatform(platformBinding(profile, entry)); return { id: entry.id, status: "verified", parent: entry.parent, bom: entry.bom, architectures: entry.evidence.map(item => item.architecture_family), capabilities: entry.capabilities ?? [] }; }
8
+ catch (error) { return { id: entry.id, status: "blocked", reason: error.message }; }
9
+ });
10
+ return { profile_id: profile.id, spring_boot_version: profile.spring_boot_version, java_version: profile.java_version, recommended_java: profile.java_version === 8 || profile.java_version === 17, selectable: combinations.some(entry => entry.status === "verified"), combinations, blockers: combinations.some(entry => entry.status === "verified") ? [] : profile.candidate_blockers };
11
+ });
12
+ process.stdout.write(`${JSON.stringify({ schema_version: 1, profiles: rows }, null, 2)}\n`);
13
+ if (process.argv.includes("--require-verified") && rows.some(row => !row.selectable)) process.exitCode = 1;
14
+ } catch (error) { process.stderr.write(`${error.message}\n`); process.exitCode = 1; }
@@ -10,7 +10,7 @@ import {
10
10
  } from "./digital-human-roles.mjs";
11
11
  import { assertGateChecks } from "./lifecycle-controls.mjs";
12
12
  import { ROOT, loadRegistry } from "./lifecycle-registry.mjs";
13
- import { assertUserDecisionRequirement, assertWorkUnitUserDecision, assertImplementationDecision } from "./user-decision.mjs";
13
+ import { assertUserDecisionRequirement, assertWorkUnitUserDecision, assertImplementationDecision, decisionIO, decisionDigest } from "./user-decision.mjs";
14
14
 
15
15
  const DECISIONS = new Set(["approved", "rejected", "vetoed"]);
16
16
  const ACTOR_KINDS = new Set(["digital-human", "biological-human", "orchestrator"]);
@@ -38,8 +38,16 @@ function yamlFromFile(filePath, label) {
38
38
  return value;
39
39
  }
40
40
 
41
- export function loadApprovalRecord(filePath) {
42
- return yamlFromFile(filePath, "会签记录");
41
+ export function loadApprovalRecord(filePath, gateId) {
42
+ return selectApprovalRecord(yamlFromFile(filePath, "会签记录"), gateId);
43
+ }
44
+
45
+ export function selectApprovalRecord(record, gateId) {
46
+ if (record.kind !== 'review-bundle') return record;
47
+ if (record.schema_version !== 1 || !Array.isArray(record.reviews) || !record.reviews.length || new Set(record.reviews.map(x => x.gate_id)).size !== record.reviews.length) fail('组合审查身份无效或重复');
48
+ const selected = record.reviews.filter(x => x.gate_id === gateId);
49
+ if (selected.length !== 1) fail('组合审查缺少当前检查的明确结论');
50
+ return selected[0];
43
51
  }
44
52
 
45
53
  export function resolveApprovalRef(approvalRef, fromFile = ROOT) {
@@ -68,7 +76,12 @@ export function validateApprovalRecord(record, { rolesDoc, requireApproved = fal
68
76
  if (record.actor_kind === "orchestrator") fail("编排器门禁不使用会签记录关闭");
69
77
 
70
78
  if (!requireApproved && loadRegistry().id_policy.deprecated_ids.includes(record.gate_id)) return { bucket: "historical", gate: record.gate_id };
71
- const rule = countersignRuleForGate(registry.gate_policy, record.gate_id);
79
+ let rule = countersignRuleForGate(registry.gate_policy, record.gate_id);
80
+ const continuationReviewers = registry.gate_policy.continuation_reviews?.[record.gate_id];
81
+ if (record.continuation_ref && record.actor_kind === 'digital-human' && continuationReviewers) {
82
+ rule = { bucket: 'digital_human_review', gate: record.gate_id, countersigners: continuationReviewers };
83
+ if (!record.drafter_principal_ref || record.drafter_principal_ref === record.principal_ref) fail('延续批准必须由独立审查者核验');
84
+ }
72
85
  if (!rule) fail(`${record.gate_id} 不是会签门禁;evidence_only / orchestrator 门禁不写 approval-record`);
73
86
 
74
87
  if (requireApproved && record.decision !== "approved") {
@@ -107,7 +120,20 @@ function assertRecordUserDecision(record, registry, options) {
107
120
  }
108
121
 
109
122
  export function validateApprovalRecordFile(filePath, options = {}) {
110
- return validateApprovalRecord(loadApprovalRecord(filePath), options);
123
+ const record = yamlFromFile(filePath, "会签记录");
124
+ if (record.kind === 'review-bundle') {
125
+ if (!Array.isArray(record.reviews) || !record.reviews.length) fail('组合审查不能为空');
126
+ return record.reviews.map(item => {
127
+ const row = loadApprovalRecord(filePath, item.gate_id);
128
+ if (options.requireApproved) {
129
+ if (!row.drafter_principal_ref || row.drafter_principal_ref === row.principal_ref) fail('组合审查必须保留独立身份');
130
+ const io = decisionIO(options);
131
+ if (!row.subject_ref || decisionDigest(io.bytes(row.subject_ref)) !== `sha256:${row.subject_digest}`) fail('组合审查主体缺失或已过期');
132
+ }
133
+ return validateApprovalRecord(row, options);
134
+ });
135
+ }
136
+ return validateApprovalRecord(record, options);
111
137
  }
112
138
 
113
139
  export function assertApprovedGateHasValidApproval(gateId, gateState, { checkpointPath } = {}) {
@@ -121,7 +147,7 @@ export function assertApprovedGateHasValidApproval(gateId, gateState, { checkpoi
121
147
  }
122
148
  const resolved = resolveApprovalRef(gateState.approval_ref, checkpointPath || ROOT);
123
149
  if (!existsSync(resolved)) fail(`${gateId} 的 approval_ref 不可读: ${gateState.approval_ref}`);
124
- const record = loadApprovalRecord(resolved);
150
+ const record = loadApprovalRecord(resolved, gateId);
125
151
  if (record.gate_id !== gateId) fail(`${gateId} 的会签记录 gate_id 不匹配`);
126
152
  if (!gateState.subject_ref || gateState.subject_ref !== record.subject_ref || !gateState.approval_scope?.length || JSON.stringify([...gateState.approval_scope].sort()) !== JSON.stringify([...(record.approval_scope || [])].sort())) {
127
153
  fail(`${gateId} user-decision-subject-mismatch: 当前门禁资产与会签范围不匹配`);
@@ -1,3 +1,4 @@
1
+ import { selectSliceWorkUnit, normalizeSliceContract, sourceSliceContract, parseSliceYaml } from './slice-contract.mjs';
1
2
  import path from 'node:path';
2
3
  import { readFileSync } from 'node:fs';
3
4
  import { ROOT, read, parse, safe, hash, digest, schema } from './strategic-handoff-io.mjs';
@@ -29,11 +30,11 @@ export function verifySliceContractApproval(binding,{root=process.cwd(),contract
29
30
  check(binding?.ref&&binding?.digest&&binding?.approval_ref,'EXECUTION_APPROVAL_REQUIRED','需要当前持久化 Slice 的 ref/digest/approval_ref');
30
31
  const bytes=readFileSync(safe(root,binding.ref));
31
32
  check(hash(bytes)===binding.digest,'EXECUTION_CONTRACT_STALE','Slice 原始字节与绑定不一致');
32
- const document=parse(bytes),contract=document.slice_contract||document;
33
- check(contract.schema_version===2&&contract.status==='approved'&&typeof contract.contract_id==='string'&&contract.contract_id&&typeof contract.contract_version==='string'&&contract.contract_version&&typeof contract.slice_id==='string'&&contract.slice_id,'EXECUTION_CONTRACT_INVALID','需要已批准且有身份的持久化 Slice v2');
33
+ const document=parseSliceYaml(bytes),contract=normalizeSliceContract(document,{root});
34
+ check([2,3].includes(contract.schema_version)&&contract.status==='approved'&&typeof contract.contract_id==='string'&&contract.contract_id&&typeof contract.contract_version==='string'&&contract.contract_version&&typeof contract.slice_id==='string'&&contract.slice_id,'EXECUTION_CONTRACT_INVALID','需要已批准且有身份的持久化 Slice v2/v3');
34
35
  assertExistingSliceStructure(contract);
35
36
  check((!binding.id||binding.id===contract.contract_id)&&(!binding.version||binding.version===contract.contract_version),'EXECUTION_CONTRACT_CONFLICT','当前合同身份或版本不匹配');
36
- if(expected)check(same(expected.slice_contract||expected,contract),'EXECUTION_CONTRACT_CONFLICT','调用方合同与持久化原字节不一致');
37
+ if(expected)check(same(sourceSliceContract(expected),sourceSliceContract(contract)),'EXECUTION_CONTRACT_CONFLICT','调用方合同与持久化原字节不一致');
37
38
  const roles=read(safe(root,'docs/agents/digital-human-roles.yaml'));
38
39
  const approval=read(safe(root,binding.approval_ref));
39
40
  if(!countersignRuleForGate(roles.gate_policy,gateId)&&roles.gate_policy.orchestrator?.includes(gateId)) {
@@ -51,13 +52,14 @@ export function verifySliceContractApproval(binding,{root=process.cwd(),contract
51
52
  validateApprovalRecord(approval,{...ioFor(root),rolesDoc:roles,requireApproved:true});
52
53
  check(approval.gate_id===gateId&&approval.artifact_bindings?.some(item=>item.id===contract.contract_id&&item.version===contract.contract_version&&item.digest===binding.digest),'EXECUTION_APPROVAL_SCOPE','源会签未绑定当前 Slice 的身份、版本和字节');
53
54
  }
55
+ if(contract.schema_version===3) verifySliceProfessionalReview(approval,{...binding,id:contract.contract_id,version:contract.contract_version},roles,root);
54
56
  return {contract,binding:structuredClone(binding),root};
55
57
  }
56
58
 
57
59
  function executionBasis(verified,{allowCompilerDrift=false}={}) {
58
60
  const {contract}=verified,resolution=contract.resolution||{};
59
61
  const compiler=read(safe(ROOT,'.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml'));
60
- if(!allowCompilerDrift)for(const [section,fields] of Object.entries(compiler.slice_contract_required))for(const field of fields)check(Object.hasOwn(section==='root'?contract:contract[section]||{},field),'EXECUTION_CONTRACT_INVALID',`合同缺少 ${section}.${field}`);
62
+ if(!allowCompilerDrift&&contract.schema_version===2)for(const [section,fields] of Object.entries(compiler.slice_contract_required))for(const field of fields)check(Object.hasOwn(section==='root'?contract:contract[section]||{},field),'EXECUTION_CONTRACT_INVALID',`合同缺少 ${section}.${field}`);
61
63
  check(Array.isArray(contract.common?.allowed_write_paths)&&contract.common.allowed_write_paths.length>0&&contract.common.allowed_write_paths.every(value=>typeof value==='string'&&value.trim()),'EXECUTION_SCOPE_MISSING','合同缺少已批准写范围');
62
64
  check(Array.isArray(contract.readiness?.blockers)&&!contract.readiness.blockers.length&&Array.isArray(contract.readiness?.stale_inputs)&&!contract.readiness.stale_inputs.length,'EXECUTION_CONTRACT_STALE','合同就绪条件仍有阻断或过期输入');
63
65
  if(!allowCompilerDrift)check(resolution.registry_digest===digest(loadSkillRegistry()).slice(7)&&resolution.compiler_contract_digest===digest(compiler).slice(7),'EXECUTION_CONTRACT_STALE','编译依据已改变,应重新编译批准');
@@ -68,16 +70,18 @@ function executionBasis(verified,{allowCompilerDrift=false}={}) {
68
70
 
69
71
  export function createApprovedExecutionContext(binding,options={}) {
70
72
  const verified=executionBasis(verifySliceContractApproval(binding,options));
73
+ selectSliceWorkUnit(verified.contract,options.work_unit_id);
71
74
  const context=Object.freeze({kind:'approved-slice-execution-context'});
72
- contexts.set(context,{binding:verified.binding,root:verified.root,allowCompilerDrift:false});
75
+ contexts.set(context,{binding:verified.binding,root:verified.root,work_unit_id:options.work_unit_id,allowCompilerDrift:false});
73
76
  return context;
74
77
  }
75
78
 
76
79
  /** A prior approved scope may authenticate bounded output while current compiler facts are recomputed. */
77
80
  export function createApprovedRecompilationContext(binding,options={}) {
78
81
  const verified=executionBasis(verifySliceContractApproval(binding,options),{allowCompilerDrift:true});
82
+ selectSliceWorkUnit(verified.contract,options.work_unit_id);
79
83
  const context=Object.freeze({kind:'approved-slice-recompilation-context'});
80
- contexts.set(context,{binding:verified.binding,root:verified.root,allowCompilerDrift:true});
84
+ contexts.set(context,{binding:verified.binding,root:verified.root,work_unit_id:options.work_unit_id,allowCompilerDrift:true});
81
85
  return context;
82
86
  }
83
87
 
@@ -86,7 +90,7 @@ export function assertApprovedExecutionContext(context,{root=process.cwd(),contr
86
90
  const saved=contexts.get(context);
87
91
  check(saved&&saved.root===path.resolve(root),'EXECUTION_CONTEXT_UNTRUSTED','只允许从当前原始 Slice 与本地批准生成的执行上下文');
88
92
  const verified=executionBasis(verifySliceContractApproval(saved.binding,{root,contract}),{allowCompilerDrift:saved.allowCompilerDrift});
89
- const current=verified.contract,resolution=current.resolution;
93
+ const current=selectSliceWorkUnit(verified.contract,saved.work_unit_id),resolution=current.resolution;
90
94
  if(sliceId)check(current.slice_id===sliceId,'EXECUTION_SCOPE_CONFLICT','执行上下文属于另一切片');
91
95
  if(architectureIdentity)check(same(resolution.architecture_identity,architectureIdentity),'EXECUTION_INPUT_REPLACED','执行上下文不允许替换架构身份或固定源码输入');
92
96
  if(architectureEvidence)check(same(resolution.architecture_evidence,architectureEvidence),'EXECUTION_INPUT_REPLACED','执行上下文不允许替换原始架构证据');
@@ -102,5 +106,24 @@ export function assertApprovedExecutionContext(context,{root=process.cwd(),contr
102
106
  export function approvedExecutionBinding(context) {
103
107
  const saved=contexts.get(context);
104
108
  check(saved,'EXECUTION_CONTEXT_UNTRUSTED','不能序列化未验证的执行上下文');
105
- return structuredClone(saved.binding);
109
+ return {...structuredClone(saved.binding),...(saved.work_unit_id?{work_unit_id:saved.work_unit_id}:{})};
110
+ }
111
+
112
+ function verifySliceProfessionalReview(approval,binding,roles,root) {
113
+ const refs=approval.gates?.[gateId]?.evidence_refs || approval.evidence_refs || [];
114
+ let found=false;
115
+ for(const ref of refs) {
116
+ if(!/\.(yaml|yml|json)$/.test(ref))continue;
117
+ const record=read(safe(root,ref));
118
+ const rows=record.kind==='review-bundle'?record.reviews:[record];
119
+ for(const row of rows || []) {
120
+ if(row.gate_id!=='check.design-reviewed'||row.subject_ref!==binding.ref)continue;
121
+ validateApprovalRecord(row,{...ioFor(root),rolesDoc:roles,requireApproved:true});
122
+ check(row.drafter_principal_ref&&row.principal_ref!==row.drafter_principal_ref,'EXECUTION_REVIEW_NOT_INDEPENDENT','Slice 工程审查必须独立于起草者');
123
+ check(row.subject_digest===binding.digest.slice(7)&&row.artifact_bindings?.some(x=>x.id===binding.id&&x.version===binding.version&&x.digest===binding.digest),'EXECUTION_REVIEW_STALE','Slice 工程审查未绑定当前合同');
124
+ check(Array.isArray(row.findings)&&row.findings.every(x=>x.kind==='suggestion'?!!x.follow_up:['requirement-violation','missing-evidence','important-risk'].includes(x.kind)&&x.status==='resolved'),'EXECUTION_REVIEW_BLOCKED','Slice 工程审查有未关闭问题');
125
+ found=true;
126
+ }
127
+ }
128
+ check(found,'EXECUTION_REVIEW_REQUIRED','Slice v3 缺少当前合同的独立工程审查');
106
129
  }
@@ -1,3 +1,4 @@
1
+ import { platformProfile, resolveBackendPlatform } from "./backend-platform.mjs";
1
2
  import { createHash } from "node:crypto";
2
3
  import { loadSkillRegistry } from "./skill-registry.mjs";
3
4
  import { validateExistingArchitecture, verifyExistingArchitecture } from "./existing-backend-architecture.mjs";
@@ -12,6 +13,10 @@ export function validateArchitectureIdentity(identity, registry = loadSkillRegis
12
13
  if (!identity || typeof identity !== "object") throw new TypeError("缺少 architecture_identity;请从工程基线重新编译");
13
14
  if (identity.schema_version === 2) return validateExistingArchitecture(identity, registry);
14
15
  if (identity.schema_version !== undefined || identity.source_kind !== undefined) throw new TypeError("不支持的 architecture_identity 来源或版本");
16
+ if (identity.platform_configuration) {
17
+ const selected = platformProfile(identity.platform_configuration.profile_id, undefined, identity.platform_configuration.spring_boot_version);
18
+ if (selected.spring_boot_version !== identity.platform_configuration.spring_boot_version || selected.java_version !== identity.platform_configuration.java_version) throw new TypeError("backend-platform: invalid architecture platform binding");
19
+ }
15
20
  const profile = registry.architecture_profiles?.[identity.architecture_profile];
16
21
  if (!profile || identity.architecture_family !== profile.architecture_family || identity.generator_skill !== profile.generator_skill) {
17
22
  throw new TypeError("architecture_identity 的架构族、Profile 或生成器不匹配");
@@ -51,5 +56,6 @@ export function assertArchitectureAgreement(identity, evidence, registry) {
51
56
 
52
57
  export function architectureReady(identity, registry = loadSkillRegistry()) {
53
58
  const profile = validateArchitectureIdentity(identity, registry);
59
+ if (identity.platform_configuration) { try { resolveBackendPlatform(identity.platform_configuration); } catch { return false; } }
54
60
  return profile.maturity === "supported";
55
61
  }
@@ -0,0 +1,67 @@
1
+ /** Version qualification binds executable sources and generated bytes, not Skill prose. */
2
+ import { createHash } from 'node:crypto';
3
+ import { readFileSync, readdirSync, lstatSync } from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
7
+ const digest = bytes => `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
8
+ const fail = message => { throw new TypeError(`platform-provenance: ${message}`); };
9
+ function fileMap(root, refs) {
10
+ const files = {};
11
+ const visit = ref => {
12
+ const full = path.join(root, ref), info = lstatSync(full);
13
+ if (info.isSymbolicLink()) fail(`source symlink: ${ref}`);
14
+ if (info.isDirectory()) for (const name of readdirSync(full).sort()) visit(`${ref}/${name}`);
15
+ else files[ref] = { digest: digest(readFileSync(full)), executable: Boolean(info.mode & 0o111) };
16
+ };
17
+ refs.forEach(visit);
18
+ return Object.fromEntries(Object.entries(files).sort(([a], [b]) => a.localeCompare(b)));
19
+ }
20
+ export function platformSourceFingerprint(family, root = ROOT) {
21
+ const skill = family === 'domain-driven' ? 'yss-ddd-scaffold-generator' : family === 'layered-mvc' ? 'yss-layered-mvc-scaffold-generator' : null;
22
+ if (!skill) fail('unknown architecture');
23
+ const base = `.agents/skills/${skill}`;
24
+ const generator = fileMap(root, [`${base}/scripts/generate_scaffold.mjs`, ...(family === 'domain-driven' ? [`${base}/assets`] : ['.agents/skills/yss-ddd-scaffold-generator/assets/wrapper']), 'scripts/lib/backend-platform.mjs', 'scripts/lib/scaffold-local-database.mjs']);
25
+ const verifier = fileMap(root, ['scripts/lib/backend-platform-provenance.mjs', 'scripts/lib/backend-platform-verification.mjs', 'scripts/lib/command-runner.mjs', 'scripts/vendor/xml.mjs', '.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs']);
26
+ return { schema_version: 1, generator_digest: digest(JSON.stringify(generator)), verifier_digest: digest(JSON.stringify(verifier)) };
27
+ }
28
+ export function assertSourceFingerprint(value, family, root = ROOT) {
29
+ if (JSON.stringify(value) !== JSON.stringify(platformSourceFingerprint(family, root))) fail('generator/verifier source drift; requalify candidate');
30
+ }
31
+ export function generatedTreeDigest(projectRoot, manifest) {
32
+ const expected = manifest.ownership?.generated_files;
33
+ if (!Array.isArray(expected) || !expected.length) fail('missing generated file inventory');
34
+ const names = new Set();
35
+ const actual = {};
36
+ for (const file of expected) {
37
+ if (!file.path || path.isAbsolute(file.path) || file.path.split(/[\\/]/).some(p => p === '..' || p === '') || names.has(file.path)) fail('invalid generated file inventory');
38
+ names.add(file.path);
39
+ const full = path.join(projectRoot, file.path);
40
+ // Check every path component: file lstat alone does not detect a linked parent directory.
41
+ let current = projectRoot;
42
+ for (const part of file.path.split('/')) { current = path.join(current, part); if (lstatSync(current).isSymbolicLink()) fail(`generated symlink: ${file.path}`); }
43
+ const info = lstatSync(full);
44
+ if (!info.isFile()) fail(`generated file absent: ${file.path}`);
45
+ const hash = digest(readFileSync(full));
46
+ if (hash.replace('sha256:', '') !== file.sha256.replace('sha256:', '')) fail(`generated file drift: ${file.path}`);
47
+ actual[file.path] = { digest: hash, executable: Boolean(info.mode & 0o111) };
48
+ }
49
+ const visit = (directory, relative = '') => {
50
+ for (const item of readdirSync(directory, { withFileTypes: true })) {
51
+ const ref = relative ? `${relative}/${item.name}` : item.name;
52
+ if (item.isSymbolicLink()) fail(`unexpected symlink: ${ref}`);
53
+ if (item.isDirectory()) { if (!['target', '.git'].includes(item.name)) visit(path.join(directory, item.name), ref); }
54
+ else if (!names.has(ref) && ref !== '.yss/scaffold-generation.json' && ref !== '.yss/platform-dependencies.json' && !ref.endsWith('/.yss/platform-dependencies.json')) fail(`unexpected source file: ${ref}`);
55
+ }
56
+ };
57
+ visit(projectRoot);
58
+ return digest(JSON.stringify(Object.fromEntries(Object.entries(actual).sort(([a],[b]) => a.localeCompare(b)))));
59
+ }
60
+
61
+ export function assertResumableCandidate(projectRoot, contract, contractBytes) {
62
+ const manifest = JSON.parse(readFileSync(path.join(projectRoot, '.yss/scaffold-generation.json'), 'utf8'));
63
+ if (manifest.platform_verification !== 'candidate' || manifest.schema_version !== 4 || manifest.contract_id !== contract.contract_id || manifest.contract_version !== contract.contract_version || manifest.contract_digest.replace('sha256:', '') !== digest(contractBytes).replace('sha256:', '') || JSON.stringify(manifest.platform_configuration) !== JSON.stringify(contract.platform_configuration)) fail('resume requires the identical candidate contract/Manifest');
64
+ assertSourceFingerprint(manifest.source_fingerprint, contract.architecture_family);
65
+ if (generatedTreeDigest(projectRoot, manifest) !== manifest.generated_tree_digest) fail('resume generated tree drift');
66
+ return manifest;
67
+ }
@@ -0,0 +1,210 @@
1
+ import { readFile, readdir, writeFile, stat } from "node:fs/promises";
2
+ import { spawn } from "node:child_process";
3
+ import net from "node:net";
4
+ import path from "node:path";
5
+ import { parseXmlDocument } from "../vendor/xml.mjs";
6
+ import { runCommand } from "./command-runner.mjs";
7
+ import { assertJavaPlatform, platformDigest } from "./backend-platform.mjs";
8
+
9
+ const array = value => value === undefined ? [] : Array.isArray(value) ? value : [value];
10
+ const fail = message => { throw new TypeError(`platform-dependencies: ${message}`); };
11
+ const gav = item => `${item.groupId}:${item.artifactId}`;
12
+ export async function platformEvidenceArtifacts(evidenceDir) {
13
+ const artifacts = [];
14
+ for (const entry of await readdir(evidenceDir, { withFileTypes: true })) {
15
+ if (entry.isFile() && (entry.name.endsWith('.log') || ['effective-pom.xml', 'boot-bom-effective.xml', 'dependency-trees.json', 'platform-tests.xml'].includes(entry.name))) {
16
+ artifacts.push({ ref: entry.name, digest: platformDigest(await readFile(path.join(evidenceDir, entry.name))) });
17
+ }
18
+ }
19
+ return artifacts.sort((a, b) => a.ref.localeCompare(b.ref));
20
+ }
21
+ export function checkPlatformTests(source, capabilities = []) {
22
+ const suite = parseXmlDocument(source).testsuite;
23
+ const cases = array(suite?.testcase);
24
+ const required = ['httpJsonRoundTrip', 'httpValidationRejectsBlank', 'validationAndJsonAutoConfiguration', 'mybatisCanMapVerificationQuery', ...(capabilities.includes('feign-client') ? ['feignCanDecodeJson'] : [])];
25
+ if (!suite || required.some(name => !cases.some(item => item['@_name'] === name && !['failure', 'error', 'skipped'].some(key => Object.hasOwn(item, key))))) fail('integration tests missing, failed or skipped');
26
+ return { status: 'passed', test_count: cases.length };
27
+ }
28
+ export async function verifyPlatformTests(projectRoot, evidenceDir, manifest, startedAt) {
29
+ try {
30
+ const module = `${manifest.project_name}-${manifest.architecture_family === 'domain-driven' ? 'bootstrap' : 'server'}`;
31
+ const reportPath = path.join(projectRoot, module, 'target/surefire-reports', `TEST-${manifest.base_package}.PlatformIntegrationTest.xml`);
32
+ if (!startedAt || (await stat(reportPath)).mtimeMs < startedAt) fail("stale integration test report");
33
+ const source = await readFile(reportPath, 'utf8');
34
+ await writeFile(path.join(evidenceDir, 'platform-tests.xml'), source);
35
+ return checkPlatformTests(source, manifest.module_profile?.requested_capabilities);
36
+ } catch (error) { return { status: 'failed', error: error.message }; }
37
+ }
38
+ export function checkPlatformDependencies(profile, effectivePom, dependencyTrees, { bootBom, mainArtifact, components = {} } = {}) {
39
+ const xml = parseXmlDocument(effectivePom);
40
+ const projects = array(xml.projects?.project ?? xml.project);
41
+ if (!projects.length || !dependencyTrees.length) fail("missing effective POM or dependency tree");
42
+ const main = dependencyTrees.filter(item => gav(item) === mainArtifact);
43
+ if (main.length !== 1) fail("missing or duplicate startup-module runtime tree");
44
+ const nodes = [];
45
+ const visit = item => { if (!["compile", "runtime"].includes(item.scope ?? "compile")) return; nodes.push(item); array(item.children).forEach(visit); };
46
+ array(main[0].children).forEach(visit);
47
+ const bom = parseXmlDocument(bootBom ?? "<missing/>").project;
48
+ if (bom?.groupId !== "org.springframework.boot" || bom.artifactId !== "spring-boot-dependencies" || bom.version !== profile.spring_boot_version) fail("wrong or missing independent Boot BOM");
49
+ const managed = new Map(array(bom.dependencyManagement?.dependencies?.dependency).map(item => [gav(item), item.version]));
50
+ if (!managed.size) fail("empty Boot BOM dependency management");
51
+ for (const item of nodes) {
52
+ const expected = managed.get(gav(item)) ?? components[gav(item)];
53
+ if (/^(com\.baomidou|org\.springdoc|org\.springframework\.cloud|com\.yss\.cloud)$/.test(item.groupId) && !expected) fail(`missing component recipe: ${gav(item)}`);
54
+ if (expected && String(item.version) !== String(expected)) fail(`resolved version differs from BOM/recipe: ${gav(item)}`);
55
+ if (/^(org\.springframework(?:\.boot)?$|org\.apache\.tomcat\.embed|jakarta\.(servlet|validation)|javax\.(servlet|validation)|(?:tools|com\.fasterxml)\.jackson|org\.slf4j|ch\.qos\.logback)/.test(item.groupId) && !expected) fail(`unmanaged platform runtime artifact: ${gav(item)}`);
56
+ }
57
+ const requireArtifact = (group, artifact, match) => {
58
+ const hits = nodes.filter(item => gav(item) === `${group}:${artifact}`);
59
+ if (!hits.length || hits.some(item => !match(String(item.version)))) fail(`${group}:${artifact} absent or incompatible`);
60
+ };
61
+ const line = (version, prefix) => version === prefix || version.startsWith(`${prefix}.`);
62
+ requireArtifact("org.springframework.boot", profile.web_starter, version => version === profile.spring_boot_version);
63
+ requireArtifact("org.springframework", "spring-webmvc", version => line(version, profile.framework_line));
64
+ requireArtifact("org.apache.tomcat.embed", "tomcat-embed-core", version => line(version, profile.tomcat_line));
65
+ requireArtifact("com.baomidou", profile.mybatis_plus_starter, version => /^3\./.test(version));
66
+ requireArtifact(profile.jackson_major === 3 ? "tools.jackson.core" : "com.fasterxml.jackson.core", "jackson-databind", version => version.startsWith(`${profile.jackson_major}.`));
67
+ for (const item of nodes) {
68
+ if (item.groupId === "org.springframework.boot" && String(item.version) !== profile.spring_boot_version) fail(`Boot dependency mismatch: ${gav(item)}`);
69
+ if (item.groupId === "org.springframework" && !line(String(item.version), profile.framework_line)) fail(`Spring dependency mismatch: ${gav(item)}`);
70
+ if (item.groupId === "org.apache.tomcat.embed" && !line(String(item.version), profile.tomcat_line)) fail(`Tomcat dependency mismatch: ${gav(item)}`);
71
+ if (item.groupId === "org.springframework.cloud" && /openfeign/.test(item.artifactId) && !line(String(item.version), profile.openfeign_line)) fail("OpenFeign line mismatch");
72
+ if (item.groupId === "org.springdoc" && !String(item.version).startsWith(`${profile.springdoc_major}.`)) fail("springdoc major mismatch");
73
+ if (profile.validation_namespace === "jakarta" && ["javax.servlet", "javax.validation"].includes(item.groupId)) fail(`legacy Java EE API: ${gav(item)}`);
74
+ if (profile.validation_namespace === "jakarta" && item.groupId === "jakarta.validation" && !String(item.version).startsWith("3.")) fail("Jakarta Validation version mismatch");
75
+ if (item.groupId === "jakarta.servlet" && !line(String(item.version), profile.servlet_version)) fail("Servlet API version mismatch");
76
+ if (profile.validation_namespace === "javax" && item.groupId === "jakarta.validation" && !String(item.version).startsWith("2.")) fail("Boot 2 requires javax Validation classes");
77
+ if (profile.validation_namespace === "javax" && item.groupId === "javax.servlet" && !String(item.version).startsWith("4.")) fail("Boot 2 Servlet mismatch");
78
+ if (item.groupId === "com.baomidou" && /spring-boot[34]-starter|mybatis-plus-boot-starter/.test(item.artifactId) && item.artifactId !== profile.mybatis_plus_starter) fail("mixed MyBatis-Plus starters");
79
+ }
80
+ let bootPluginFound = false;
81
+ for (const project of projects) {
82
+ const target = project.properties?.["maven.compiler.release"] ?? project.properties?.["maven.compiler.target"] ?? project.properties?.["java.version"];
83
+ if (![String(profile.java_version), ...(profile.java_version === 8 ? ["1.8"] : [])].includes(String(target))) fail("effective Java target mismatch");
84
+ for (const plugin of array(project.build?.plugins?.plugin)) if (gav(plugin) === "org.springframework.boot:spring-boot-maven-plugin") {
85
+ bootPluginFound = true;
86
+ if (plugin.version !== profile.spring_boot_version) fail("Boot plugin version mismatch");
87
+ }
88
+ }
89
+ if (!bootPluginFound) fail("Boot plugin absent");
90
+ return { status: "passed", runtime_artifacts: nodes, artifact_count: nodes.length, effective_pom_digest: platformDigest(effectivePom), dependency_tree_digest: platformDigest(dependencyTrees) };
91
+ }
92
+
93
+ export async function platformCommand(projectRoot, evidenceDir, label, args, environment, options = {}) {
94
+ const stdout_ref = path.join(evidenceDir, `${label}.stdout.log`);
95
+ const stderr_ref = path.join(evidenceDir, `${label}.stderr.log`);
96
+ const executed_at = new Date().toISOString();
97
+ const start = Date.now();
98
+ const result = await runCommand(path.join(projectRoot, "mvnw"), args, { cwd: projectRoot, env: environment, timeoutMs: options.timeoutMs || 180000, signal: options.signal, stdoutFile: stdout_ref, stderrFile: stderr_ref, secrets: [environment.MAVEN_REPO_USERNAME, environment.MAVEN_REPO_PASSWORD], progress: true });
99
+ return { command: `./mvnw ${args.join(" ")}`, exit_code: result.status, termination: result.termination, executed_at, duration_ms: Date.now() - start, stdout_ref, stderr_ref, stdout: result.stdout, stderr: result.stderr };
100
+ }
101
+ export async function verifyPlatformDependencies(projectRoot, evidenceDir, profile, environment, options = {}) {
102
+ const commands = [];
103
+ const run = async (label, args) => {
104
+ const result = await platformCommand(projectRoot, evidenceDir, label, args, environment, options);
105
+ const { stdout, stderr, ...record } = result;
106
+ commands.push(record);
107
+ if (result.exit_code !== 0) fail(`${label} failed; see ${record.stderr_ref}`);
108
+ return result;
109
+ };
110
+ try {
111
+ const java = await run("platform-java", ["-version"]);
112
+ assertJavaPlatform(`${java.stdout}\n${java.stderr}`, { java_version: profile.java_version });
113
+ const pom = path.join(evidenceDir, "effective-pom.xml");
114
+ const bootBom = path.join(evidenceDir, "boot-bom-effective.xml");
115
+ await run("platform-boot-bom", ["org.apache.maven.plugins:maven-help-plugin:3.5.1:effective-pom", `-Dartifact=org.springframework.boot:spring-boot-dependencies:${profile.spring_boot_version}`, `-Doutput=${bootBom}`]);
116
+ await run("platform-effective-pom", ["org.apache.maven.plugins:maven-help-plugin:3.5.1:effective-pom", `-Doutput=${pom}`]);
117
+ await run("platform-dependency-tree", ["org.apache.maven.plugins:maven-dependency-plugin:3.8.1:tree", "-DoutputType=json", "-DoutputFile=.yss/platform-dependencies.json"]);
118
+ const trees = [];
119
+ const visited = new Set();
120
+ for (const directory of [projectRoot, ...(await readdir(projectRoot, { withFileTypes: true })).filter(item => item.isDirectory() && item.name.startsWith(`${path.basename(projectRoot)}-`)).map(item => path.join(projectRoot, item.name))]) {
121
+ const candidates = [directory];
122
+ // DDD adapter contains the nested web module.
123
+ for (const entry of await readdir(directory, { withFileTypes: true })) if (entry.isDirectory() && entry.name.startsWith(`${path.basename(projectRoot)}-`)) candidates.push(path.join(directory, entry.name));
124
+ for (const candidate of candidates) {
125
+ if (visited.has(candidate)) continue;
126
+ visited.add(candidate);
127
+ try { trees.push(JSON.parse(await readFile(path.join(candidate, ".yss/platform-dependencies.json"), "utf8"))); }
128
+ catch (error) { if (error.code !== "ENOENT") throw error; }
129
+ }
130
+ }
131
+ await writeFile(path.join(evidenceDir, "dependency-trees.json"), JSON.stringify(trees, null, 2));
132
+ return { ...checkPlatformDependencies(profile, await readFile(pom, "utf8"), trees, { bootBom: await readFile(bootBom, "utf8"), mainArtifact: `${options.manifest.maven_coordinates.group_id}:${options.manifest.project_name}-${options.manifest.architecture_family === "domain-driven" ? "bootstrap" : "server"}`, components: options.components }), commands };
133
+ } catch (error) { return { status: "failed", error: error.message, commands }; }
134
+ }
135
+
136
+ async function freePort() {
137
+ const server = net.createServer();
138
+ await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); });
139
+ const port = server.address().port;
140
+ await new Promise(resolve => server.close(resolve));
141
+ return port;
142
+ }
143
+ export function checkRuntimeArchive(entries, artifacts, { providedLombokVersion } = {}) {
144
+ if (!Array.isArray(artifacts) || !artifacts.length) fail("missing runtime artifact inventory");
145
+ const names = new Set(entries.split(/\r?\n/));
146
+ const expected = new Set();
147
+ for (const item of artifacts.filter(item => (item.type ?? "jar") === "jar")) {
148
+ const name = `BOOT-INF/lib/${item.artifactId}-${item.version}${item.classifier ? `-${item.classifier}` : ""}.jar`;
149
+ expected.add(name);
150
+ if (!names.has(name)) fail(`runtime jar missing ${name}`);
151
+ }
152
+ const bootVersion = artifacts.find(item => item.groupId === "org.springframework.boot")?.version;
153
+ // Boot's repackage goal injects its matching jarmode helper outside the dependency tree.
154
+ for (const name of names) if (name.startsWith("BOOT-INF/lib/") && name.endsWith(".jar") && !expected.has(name)
155
+ && name !== `BOOT-INF/lib/lombok-${providedLombokVersion}.jar`
156
+ && !["tools", "layertools"].some(kind => name === `BOOT-INF/lib/spring-boot-jarmode-${kind}-${bootVersion}.jar`)) fail(`unexpected packaged runtime jar: ${name}`);
157
+ return { status: "passed" };
158
+ }
159
+ async function inspectRuntimeJar(jar, evidenceDir, environment, options) {
160
+ const tool = environment.JAVA_HOME ? path.join(environment.JAVA_HOME, "bin/jar") : "jar";
161
+ const stdoutFile = path.join(evidenceDir, "runtime-jar-entries.log");
162
+ const stderrFile = path.join(evidenceDir, "runtime-jar.stderr.log");
163
+ const result = await runCommand(tool, ["tf", jar], { env: environment, timeoutMs: 30000, stdoutFile, stderrFile });
164
+ if (result.status !== 0) fail("cannot inspect packaged runtime jar");
165
+ return checkRuntimeArchive(result.stdout, options.runtimeArtifacts, options);
166
+ }
167
+ export async function verifyPlatformStartup(projectRoot, evidenceDir, manifest, environment, options = {}) {
168
+ const runtimeModule = `${manifest.project_name}-${manifest.architecture_family === "domain-driven" ? "bootstrap" : "server"}`;
169
+ const runtimeTarget = path.join(projectRoot, runtimeModule, "target");
170
+ try {
171
+ const packaged = (await readdir(runtimeTarget)).filter(name => name.endsWith(".jar") && !/-sources|-javadoc|-tests/.test(name));
172
+ if (packaged.length !== 1) fail("expected exactly one production runtime jar");
173
+ await inspectRuntimeJar(path.join(runtimeTarget, packaged[0]), evidenceDir, environment, options);
174
+ } catch (error) { return { status: "failed", error: error.message, commands: [] }; }
175
+ const packaging = await platformCommand(projectRoot, evidenceDir, "platform-local-package", ["-Pscaffold-local", "package", "-DskipTests"], environment, options);
176
+ const { stdout: ignoredOut, stderr: ignoredErr, ...command } = packaging;
177
+ if (packaging.exit_code !== 0) return { status: "failed", error: "local packaging failed", commands: [command] };
178
+ const module = `${manifest.project_name}-${manifest.architecture_family === "domain-driven" ? "bootstrap" : "server"}`;
179
+ const target = path.join(projectRoot, module, "target");
180
+ const jars = (await readdir(target)).filter(name => name.endsWith(".jar") && !/-sources|-javadoc|-tests/.test(name));
181
+ if (jars.length !== 1) return { status: "failed", error: "expected exactly one executable jar", commands: [command] };
182
+ const port = await freePort();
183
+ const java = environment.JAVA_HOME ? path.join(environment.JAVA_HOME, "bin/java") : "java";
184
+ const args = ["-jar", path.join(target, jars[0]), "--spring.profiles.active=scaffold-local", "--server.address=127.0.0.1", `--server.port=${port}`];
185
+ const processStartedAt = new Date().toISOString();
186
+ let output = "", errors = "", started = false, exited = false;
187
+ const child = spawn(java, args, { cwd: projectRoot, env: environment, stdio: ["ignore", "pipe", "pipe"] });
188
+ child.stdout.on("data", data => { output += data; if (/Started .* in /.test(output)) started = true; });
189
+ child.stderr.on("data", data => { errors += data; });
190
+ child.on("error", error => { errors += error.message; exited = true; });
191
+ const closed = new Promise(resolve => child.once("close", code => { exited = true; resolve(code); }));
192
+ let passed = false;
193
+ const deadline = Date.now() + Math.min(options.timeoutMs || 60000, 120000);
194
+ try {
195
+ while (!exited && Date.now() < deadline && !options.signal?.aborted) {
196
+ if (started) {
197
+ try { const response = await fetch(`http://127.0.0.1:${port}/`, { signal: AbortSignal.timeout(1000) }); if (response.status > 0 && response.status < 500) { passed = true; break; } } catch {}
198
+ }
199
+ await new Promise(resolve => setTimeout(resolve, 250));
200
+ }
201
+ } finally {
202
+ if (!exited) child.kill("SIGTERM");
203
+ const killTimer = setTimeout(() => { if (!exited) child.kill("SIGKILL"); }, 3000);
204
+ await closed; clearTimeout(killTimer);
205
+ }
206
+ const redact = text => [environment.MAVEN_REPO_USERNAME, environment.MAVEN_REPO_PASSWORD].filter(Boolean).reduce((value, secret) => value.replaceAll(secret, "[REDACTED]"), text);
207
+ await writeFile(path.join(evidenceDir, "startup.stdout.log"), redact(output));
208
+ await writeFile(path.join(evidenceDir, "startup.stderr.log"), redact(errors));
209
+ return { status: passed ? "passed" : "failed", commands: [command], executed_at: processStartedAt, artifact_digest: platformDigest(await readFile(path.join(target, jars[0]))), stdout_ref: path.join(evidenceDir, "startup.stdout.log"), stderr_ref: path.join(evidenceDir, "startup.stderr.log") };
210
+ }