create-yss-spec 2.2.3 → 2.2.5

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 (341) hide show
  1. package/README.md +17 -1
  2. package/package.json +1 -1
  3. package/src/cli.js +15 -1
  4. package/src/self-update.js +277 -0
  5. package/template/.agents/skills/yss-domain/SKILL.md +5 -4
  6. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +15 -14
  7. package/template/.agents/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
  8. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  9. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
  10. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +6 -3
  11. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +7 -2
  12. package/template/.agents/skills/yss-router/SKILL.md +4 -2
  13. package/template/.agents/skills/yss-router/references/router-contract.yaml +18 -3
  14. package/template/.agents/skills/yss-router/references/slice-implementation-contract.md +58 -1
  15. package/template/.agents/skills/yss-stage-decision/SKILL.md +50 -0
  16. package/template/.agents/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
  17. package/template/.agents/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
  18. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
  19. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
  20. package/template/.agents/skills/yss-stage-decision/references/validation-rules.md +18 -0
  21. package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
  22. package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
  23. package/template/.agents/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
  24. package/template/.agents/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
  25. package/template/.agents/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
  26. package/template/.agents/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
  27. package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
  28. package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
  29. package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
  30. package/template/.agents/skills/yss-tactical-design/SKILL.md +33 -0
  31. package/template/.agents/skills/yss-tactical-design/agents/openai.yaml +4 -0
  32. package/template/.agents/skills/yss-tactical-design/references/output-template.md +28 -0
  33. package/template/.agents/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
  34. package/template/.agents/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
  35. package/template/.agents/skills/yss-tactical-design/references/validation-rules.md +13 -0
  36. package/template/.agents/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
  37. package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
  38. package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
  39. package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
  40. package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
  41. package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
  42. package/template/.agents/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
  43. package/template/.agents/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
  44. package/template/.claude/skills/yss-domain/SKILL.md +5 -4
  45. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +15 -14
  46. package/template/.claude/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
  47. package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  48. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
  49. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +6 -3
  50. package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +7 -2
  51. package/template/.claude/skills/yss-router/SKILL.md +4 -2
  52. package/template/.claude/skills/yss-router/references/router-contract.yaml +18 -3
  53. package/template/.claude/skills/yss-router/references/slice-implementation-contract.md +58 -1
  54. package/template/.claude/skills/yss-stage-decision/SKILL.md +50 -0
  55. package/template/.claude/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
  56. package/template/.claude/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
  57. package/template/.claude/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
  58. package/template/.claude/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
  59. package/template/.claude/skills/yss-stage-decision/references/validation-rules.md +18 -0
  60. package/template/.claude/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
  61. package/template/.claude/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
  62. package/template/.claude/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
  63. package/template/.claude/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
  64. package/template/.claude/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
  65. package/template/.claude/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
  66. package/template/.claude/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
  67. package/template/.claude/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
  68. package/template/.claude/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
  69. package/template/.claude/skills/yss-tactical-design/SKILL.md +33 -0
  70. package/template/.claude/skills/yss-tactical-design/agents/openai.yaml +4 -0
  71. package/template/.claude/skills/yss-tactical-design/references/output-template.md +28 -0
  72. package/template/.claude/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
  73. package/template/.claude/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
  74. package/template/.claude/skills/yss-tactical-design/references/validation-rules.md +13 -0
  75. package/template/.claude/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
  76. package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
  77. package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
  78. package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
  79. package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
  80. package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
  81. package/template/.claude/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
  82. package/template/.claude/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
  83. package/template/.codex/skills/yss-domain/SKILL.md +5 -4
  84. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +15 -14
  85. package/template/.codex/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
  86. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  87. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
  88. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +6 -3
  89. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +7 -2
  90. package/template/.codex/skills/yss-router/SKILL.md +4 -2
  91. package/template/.codex/skills/yss-router/references/router-contract.yaml +18 -3
  92. package/template/.codex/skills/yss-router/references/slice-implementation-contract.md +58 -1
  93. package/template/.codex/skills/yss-stage-decision/SKILL.md +50 -0
  94. package/template/.codex/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
  95. package/template/.codex/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
  96. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
  97. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
  98. package/template/.codex/skills/yss-stage-decision/references/validation-rules.md +18 -0
  99. package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
  100. package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
  101. package/template/.codex/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
  102. package/template/.codex/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
  103. package/template/.codex/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
  104. package/template/.codex/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
  105. package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
  106. package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
  107. package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
  108. package/template/.codex/skills/yss-tactical-design/SKILL.md +33 -0
  109. package/template/.codex/skills/yss-tactical-design/agents/openai.yaml +4 -0
  110. package/template/.codex/skills/yss-tactical-design/references/output-template.md +28 -0
  111. package/template/.codex/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
  112. package/template/.codex/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
  113. package/template/.codex/skills/yss-tactical-design/references/validation-rules.md +13 -0
  114. package/template/.codex/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
  115. package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
  116. package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
  117. package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
  118. package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
  119. package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
  120. package/template/.codex/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
  121. package/template/.codex/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
  122. package/template/.cursor/skills/yss-domain/SKILL.md +5 -4
  123. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +15 -14
  124. package/template/.cursor/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
  125. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  126. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
  127. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +6 -3
  128. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +7 -2
  129. package/template/.cursor/skills/yss-router/SKILL.md +4 -2
  130. package/template/.cursor/skills/yss-router/references/router-contract.yaml +18 -3
  131. package/template/.cursor/skills/yss-router/references/slice-implementation-contract.md +58 -1
  132. package/template/.cursor/skills/yss-stage-decision/SKILL.md +50 -0
  133. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
  134. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
  135. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
  136. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
  137. package/template/.cursor/skills/yss-stage-decision/references/validation-rules.md +18 -0
  138. package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
  139. package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
  140. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
  141. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
  142. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
  143. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
  144. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
  145. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
  146. package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
  147. package/template/.cursor/skills/yss-tactical-design/SKILL.md +33 -0
  148. package/template/.cursor/skills/yss-tactical-design/agents/openai.yaml +4 -0
  149. package/template/.cursor/skills/yss-tactical-design/references/output-template.md +28 -0
  150. package/template/.cursor/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
  151. package/template/.cursor/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
  152. package/template/.cursor/skills/yss-tactical-design/references/validation-rules.md +13 -0
  153. package/template/.cursor/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
  154. package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
  155. package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
  156. package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
  157. package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
  158. package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
  159. package/template/.cursor/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
  160. package/template/.cursor/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
  161. package/template/.hermes/skills/yss-domain/SKILL.md +5 -4
  162. package/template/.hermes/skills/yss-product-lifecycle/SKILL.md +15 -14
  163. package/template/.hermes/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
  164. package/template/.hermes/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  165. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
  166. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +6 -3
  167. package/template/.hermes/skills/yss-product-lifecycle/references/state-model.md +7 -2
  168. package/template/.hermes/skills/yss-router/SKILL.md +4 -2
  169. package/template/.hermes/skills/yss-router/references/router-contract.yaml +18 -3
  170. package/template/.hermes/skills/yss-router/references/slice-implementation-contract.md +58 -1
  171. package/template/.hermes/skills/yss-stage-decision/SKILL.md +50 -0
  172. package/template/.hermes/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
  173. package/template/.hermes/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
  174. package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
  175. package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
  176. package/template/.hermes/skills/yss-stage-decision/references/validation-rules.md +18 -0
  177. package/template/.hermes/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
  178. package/template/.hermes/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
  179. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
  180. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
  181. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
  182. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
  183. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
  184. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
  185. package/template/.hermes/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
  186. package/template/.hermes/skills/yss-tactical-design/SKILL.md +33 -0
  187. package/template/.hermes/skills/yss-tactical-design/agents/openai.yaml +4 -0
  188. package/template/.hermes/skills/yss-tactical-design/references/output-template.md +28 -0
  189. package/template/.hermes/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
  190. package/template/.hermes/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
  191. package/template/.hermes/skills/yss-tactical-design/references/validation-rules.md +13 -0
  192. package/template/.hermes/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
  193. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
  194. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
  195. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
  196. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
  197. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
  198. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
  199. package/template/.hermes/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
  200. package/template/.pi/skills/yss-domain/SKILL.md +5 -4
  201. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +15 -14
  202. package/template/.pi/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
  203. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  204. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
  205. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +6 -3
  206. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +7 -2
  207. package/template/.pi/skills/yss-router/SKILL.md +4 -2
  208. package/template/.pi/skills/yss-router/references/router-contract.yaml +18 -3
  209. package/template/.pi/skills/yss-router/references/slice-implementation-contract.md +58 -1
  210. package/template/.pi/skills/yss-stage-decision/SKILL.md +50 -0
  211. package/template/.pi/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
  212. package/template/.pi/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
  213. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
  214. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
  215. package/template/.pi/skills/yss-stage-decision/references/validation-rules.md +18 -0
  216. package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
  217. package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
  218. package/template/.pi/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
  219. package/template/.pi/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
  220. package/template/.pi/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
  221. package/template/.pi/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
  222. package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
  223. package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
  224. package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
  225. package/template/.pi/skills/yss-tactical-design/SKILL.md +33 -0
  226. package/template/.pi/skills/yss-tactical-design/agents/openai.yaml +4 -0
  227. package/template/.pi/skills/yss-tactical-design/references/output-template.md +28 -0
  228. package/template/.pi/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
  229. package/template/.pi/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
  230. package/template/.pi/skills/yss-tactical-design/references/validation-rules.md +13 -0
  231. package/template/.pi/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
  232. package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
  233. package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
  234. package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
  235. package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
  236. package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
  237. package/template/.pi/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
  238. package/template/.pi/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
  239. package/template/.qoder/skills/yss-domain/SKILL.md +5 -4
  240. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +15 -14
  241. package/template/.qoder/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
  242. package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  243. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
  244. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +6 -3
  245. package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +7 -2
  246. package/template/.qoder/skills/yss-router/SKILL.md +4 -2
  247. package/template/.qoder/skills/yss-router/references/router-contract.yaml +18 -3
  248. package/template/.qoder/skills/yss-router/references/slice-implementation-contract.md +58 -1
  249. package/template/.qoder/skills/yss-stage-decision/SKILL.md +50 -0
  250. package/template/.qoder/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
  251. package/template/.qoder/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
  252. package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
  253. package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
  254. package/template/.qoder/skills/yss-stage-decision/references/validation-rules.md +18 -0
  255. package/template/.qoder/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
  256. package/template/.qoder/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
  257. package/template/.qoder/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
  258. package/template/.qoder/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
  259. package/template/.qoder/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
  260. package/template/.qoder/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
  261. package/template/.qoder/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
  262. package/template/.qoder/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
  263. package/template/.qoder/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
  264. package/template/.qoder/skills/yss-tactical-design/SKILL.md +33 -0
  265. package/template/.qoder/skills/yss-tactical-design/agents/openai.yaml +4 -0
  266. package/template/.qoder/skills/yss-tactical-design/references/output-template.md +28 -0
  267. package/template/.qoder/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
  268. package/template/.qoder/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
  269. package/template/.qoder/skills/yss-tactical-design/references/validation-rules.md +13 -0
  270. package/template/.qoder/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
  271. package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
  272. package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
  273. package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
  274. package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
  275. package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
  276. package/template/.qoder/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
  277. package/template/.qoder/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
  278. package/template/.trae/skills/yss-domain/SKILL.md +5 -4
  279. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +15 -14
  280. package/template/.trae/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
  281. package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
  282. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
  283. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +6 -3
  284. package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +7 -2
  285. package/template/.trae/skills/yss-router/SKILL.md +4 -2
  286. package/template/.trae/skills/yss-router/references/router-contract.yaml +18 -3
  287. package/template/.trae/skills/yss-router/references/slice-implementation-contract.md +58 -1
  288. package/template/.trae/skills/yss-stage-decision/SKILL.md +50 -0
  289. package/template/.trae/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
  290. package/template/.trae/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
  291. package/template/.trae/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
  292. package/template/.trae/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
  293. package/template/.trae/skills/yss-stage-decision/references/validation-rules.md +18 -0
  294. package/template/.trae/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
  295. package/template/.trae/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
  296. package/template/.trae/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
  297. package/template/.trae/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
  298. package/template/.trae/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
  299. package/template/.trae/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
  300. package/template/.trae/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
  301. package/template/.trae/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
  302. package/template/.trae/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
  303. package/template/.trae/skills/yss-tactical-design/SKILL.md +33 -0
  304. package/template/.trae/skills/yss-tactical-design/agents/openai.yaml +4 -0
  305. package/template/.trae/skills/yss-tactical-design/references/output-template.md +28 -0
  306. package/template/.trae/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
  307. package/template/.trae/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
  308. package/template/.trae/skills/yss-tactical-design/references/validation-rules.md +13 -0
  309. package/template/.trae/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
  310. package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
  311. package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
  312. package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
  313. package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
  314. package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
  315. package/template/.trae/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
  316. package/template/.trae/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
  317. package/template/AGENTS.md +22 -5
  318. package/template/CONTEXT.md +1 -1
  319. package/template/docs/agents/README.md +1 -1
  320. package/template/docs/agents/digital-human-roles.md +2 -2
  321. package/template/docs/agents/digital-human-roles.yaml +7 -1
  322. package/template/docs/agents/yss-skill-registry.yaml +12 -0
  323. package/template/docs/architecture/templates/system-overview-design-template.md +1 -0
  324. package/template/docs/process/lifecycle-artifact-map.md +8 -0
  325. package/template/docs/process/lifecycle-registry-baseline.json +11 -1
  326. package/template/docs/process/lifecycle-registry.yaml +46 -2
  327. package/template/docs/process/schemas/digital-human-task-package.schema.json +107 -0
  328. package/template/docs/process/schemas/subagent-task-package.schema.json +6 -0
  329. package/template/docs/process/subagent-collaboration.md +2 -2
  330. package/template/docs/templates/subagent-task-package-template.md +39 -1
  331. package/template/scripts/lib/lifecycle-transition.mjs +259 -0
  332. package/template/scripts/lib/scenario-checks.mjs +96 -2
  333. package/template/scripts/lib/task-package.mjs +214 -0
  334. package/template/scripts/verify-digital-human-task-package +14 -0
  335. package/template/scripts/verify-digital-human-task-package-scenarios +8 -0
  336. package/template/scripts/verify-lifecycle-transition-scenarios +105 -0
  337. package/template/scripts/verify-subagent-task-package +8 -0
  338. package/template/scripts/verify-subagent-task-package-scenarios +196 -0
  339. package/template/scripts/verify-template +13 -2
  340. package/template/skills-lock.json +36 -4
  341. package/template.snapshot.json +4 -4
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: yss-tactical-design
3
+ description: 在 YSS 技术分析阶段将批准的战略领域输入细化为可审查的聚合、行为、一致性和 Gateway 设计;不生成生产代码。
4
+ ---
5
+
6
+ # yss-tactical-design
7
+
8
+ 本 skill 负责 DDD 战术设计,不新增生命周期主阶段,也不替代 `yss-stage-decision` 或 `yss-domain`。
9
+
10
+ ## 适用边界
11
+
12
+ - 由 `yss-product-lifecycle` 在 `work-unit.technical-analysis` 中按领域影响条件调度。
13
+ - 输入必须是版本当前的 Spec、功能架构、战略 DDD、状态矩阵、OpenAPI Draft / Freeze、ADR 和工程约束。
14
+ - 默认将结果写入系统概要设计 / 数据架构中的 Tactical DDD Check;聚合、状态机、一致性或持久化映射复杂到无法清楚表达时,才升级为独立战术设计文档。
15
+ - 没有聚合、状态、不变量、一致性或领域边界影响时返回 `not-applicable` 及原因,不生成空设计。
16
+
17
+ ## 设计内容
18
+
19
+ 必须明确 Aggregate Root、Entity、Value Object、领域行为、状态转换、不变量、一致性 / 事务、幂等 / 并发、Domain Event、Gateway、持久化映射和 Domain / Application 测试 seam。
20
+
21
+ 数据库表、HTTP 调用链或菜单结构不能单独决定聚合边界;OpenAPI 不得暴露内部聚合、Repository 或持久化表结构。
22
+
23
+ ## 输出与状态
24
+
25
+ 输出结构化 tactical-design contract、验证结果、评审引用和影响标记。状态使用 `draft`、`ready-for-human`、`approved`、`blocked`、`stale`、`drift`、`new_impacts`。
26
+
27
+ 本 skill 不能自行批准资产、设置 `ready-for-agent`、创建 Ticket 或修改生命周期状态。批准由 `yss-product-lifecycle` 维护;独立架构评审使用 `evidence.tactical-design-review`。
28
+
29
+ ## 与实现交接
30
+
31
+ 批准且版本当前的战术设计合同由 `Slice Implementation Contract` 引用,再由 `yss-domain` 使用 `behavior-tdd` 实现。`yss-domain` 不得静默重新定义聚合或不变量;发现新的 API、状态、数据或架构影响时必须返回 `new_impacts` / `drift` 并重新路由。
32
+
33
+ 合同、Schema、校验规则和示例见 `references/`;使用 `scripts/validate-tactical-design.mjs` 做只读验证。该 skill 不生成 Java、Repository、Controller、DTO、OpenAPI Freeze、实现 Ticket 或生产代码。
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "YSS Tactical DDD Design"
3
+ short_description: "聚合、不变量、状态机、一致性与 Gateway 的战术设计"
4
+ default_prompt: "基于批准且版本当前的上游资产,完成可审查的 YSS DDD 战术设计并返回结构化验证结果。"
@@ -0,0 +1,28 @@
1
+ pipeline: <feature-id>
2
+ stage: system-data-engineering
3
+ status: draft
4
+ owner: ai
5
+
6
+ # <功能名称> Tactical DDD Design
7
+
8
+ ## 上游引用
9
+
10
+ | 资产 | ref | version / digest | 状态 |
11
+ |---|---|---|---|
12
+ | Spec | | | |
13
+ | Strategic DDD | | | |
14
+ | 状态矩阵 | | | |
15
+ | OpenAPI / 无 API 影响记录 | | | |
16
+
17
+ ## Tactical DDD Check
18
+
19
+ | 限界上下文 | 聚合根 | Entity / Value Object | 行为与不变量 | 一致性边界 | Gateway | 测试 seam |
20
+ |---|---|---|---|---|---|---|
21
+ | | | | | | | |
22
+
23
+ ## 结论
24
+
25
+ - 复杂度升级:是 / 否
26
+ - API / 数据 / 状态新增影响:是 / 否
27
+ - 评审结论:Approved / Blocked
28
+ - 下一步:
@@ -0,0 +1,41 @@
1
+ # Tactical DDD Design Contract
2
+
3
+ ## 输入
4
+
5
+ 必须引用批准且版本当前的 Spec、功能架构、战略 DDD、原型状态矩阵、OpenAPI Draft / Freeze(或无 API 影响记录)、ADR 和工程约束。所有引用都必须带版本或 digest;不可读或过期引用返回 `blocked` / `stale`。
6
+
7
+ ## 输出
8
+
9
+ ```yaml
10
+ schema_version: 1
11
+ tactical_design_id: tactical-design.<feature>
12
+ tactical_version: v1
13
+ status: draft | ready-for-human | approved | blocked | stale | drift | new_impacts | not-applicable
14
+ context_ref: <限界上下文引用>
15
+ aggregate_catalog: []
16
+ entity_catalog: []
17
+ value_object_catalog: []
18
+ behavior_catalog: []
19
+ invariant_catalog: []
20
+ state_transition_catalog: []
21
+ consistency_policy: {}
22
+ domain_event_catalog: []
23
+ gateway_catalog: []
24
+ persistence_mapping: []
25
+ test_seams: []
26
+ adr_candidates: []
27
+ upstream_impact: {}
28
+ version: v1
29
+ digest: sha256:<digest>
30
+ evidence_refs: []
31
+ ```
32
+
33
+ ## 设计规则
34
+
35
+ 1. 聚合边界由业务行为和不变量决定,不由数据库表或外键自动推导。
36
+ 2. 每个聚合根必须列出行为、不变量和一致性边界。
37
+ 3. 状态转换必须绑定领域行为,禁止只有状态字段而没有行为语义。
38
+ 4. Gateway 接口属于 Domain,Repository / Mapper / GatewayImpl 属于 Infrastructure。
39
+ 5. OpenAPI schema 使用上下文公开语言,不泄漏内部聚合或持久化结构。
40
+ 6. 复杂跨聚合一致性、并发、幂等、事件补偿或持久化错位必须写明策略,必要时升级独立文档。
41
+ 7. 实现发现新影响时必须阻断并回到生命周期重新路由。
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://yss.dev/schemas/tactical-design.schema.json",
4
+ "title": "YSS Tactical DDD Design Contract",
5
+ "type": "object",
6
+ "required": ["schema_version", "tactical_design_id", "tactical_version", "status", "context_ref", "aggregate_catalog", "entity_catalog", "value_object_catalog", "behavior_catalog", "invariant_catalog", "state_transition_catalog", "consistency_policy", "domain_event_catalog", "gateway_catalog", "persistence_mapping", "test_seams", "adr_candidates", "upstream_impact", "version", "digest", "evidence_refs"],
7
+ "properties": {
8
+ "schema_version": {"const": 1},
9
+ "tactical_design_id": {"type": "string", "pattern": "^tactical-design\\.[a-z0-9][a-z0-9-]*$"},
10
+ "tactical_version": {"type": "string", "pattern": "^v[0-9]+$"},
11
+ "status": {"enum": ["draft", "ready-for-human", "approved", "blocked", "stale", "drift", "new_impacts", "not-applicable"]},
12
+ "context_ref": {"type": "string", "minLength": 1},
13
+ "aggregate_catalog": {"type": "array", "minItems": 1},
14
+ "entity_catalog": {"type": "array", "minItems": 1},
15
+ "value_object_catalog": {"type": "array"},
16
+ "behavior_catalog": {"type": "array", "minItems": 1},
17
+ "invariant_catalog": {"type": "array", "minItems": 1},
18
+ "state_transition_catalog": {"type": "array", "minItems": 1},
19
+ "consistency_policy": {"type": "object", "minProperties": 1},
20
+ "domain_event_catalog": {"type": "array"},
21
+ "gateway_catalog": {"type": "array", "minItems": 1},
22
+ "persistence_mapping": {"type": "array", "minItems": 1},
23
+ "test_seams": {"type": "array", "minItems": 1},
24
+ "adr_candidates": {"type": "array"},
25
+ "upstream_impact": {"type": "object", "minProperties": 1},
26
+ "version": {"type": "string", "pattern": "^v[0-9]+$"},
27
+ "digest": {"type": "string", "pattern": "^sha256:[A-Fa-f0-9]{64}$"},
28
+ "evidence_refs": {"type": "array", "items": {"type": "string"}}
29
+ },
30
+ "additionalProperties": true
31
+ }
@@ -0,0 +1,13 @@
1
+ # Tactical DDD Validation Rules
2
+
3
+ - 根对象必须包含合同要求的字段,状态只能使用生命周期状态集合。
4
+ - `aggregate_catalog`、`behavior_catalog`、`invariant_catalog`、`state_transition_catalog`、`consistency_policy` 和 `test_seams` 不能为空。
5
+ - 聚合、Entity、Value Object、行为、不变量、状态转换、事件和 Gateway ID 必须唯一且引用已声明对象。
6
+ - 聚合根必须是 Entity;Entity 和 Value Object 不得跨聚合隐式共享身份。
7
+ - 每个行为至少引用一个聚合和一个不变量;每个状态转换必须引用已声明行为。
8
+ - `api_exposure: internal-only` 是内部聚合的唯一允许值;其他值需要回到 API 影响分析。
9
+ - Gateway 的 `layer` 必须为 `Domain`,持久化实现不得出现在 Domain 目录。
10
+ - `consistency_policy` 必须说明事务边界、并发、幂等和跨聚合策略。
11
+ - 上游引用发生版本变化时,设计标记为 `stale`,不得进入 `approved` 或 `ready-for-agent`。
12
+ - 若合同标记 `model_origin: strategic-concept`,必须阻断并要求完成战术细化。
13
+ - 当 `complexity.escalate_to_standalone` 为真时,必须提供独立文档引用;否则仅允许作为嵌入式 Tactical DDD Check 消费。
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from "node:fs/promises";
3
+ import process from "node:process";
4
+ import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
5
+
6
+ const required = [
7
+ "schema_version", "tactical_design_id", "tactical_version", "status", "context_ref",
8
+ "aggregate_catalog", "entity_catalog", "value_object_catalog", "behavior_catalog",
9
+ "invariant_catalog", "state_transition_catalog", "consistency_policy", "domain_event_catalog",
10
+ "gateway_catalog", "persistence_mapping", "test_seams", "adr_candidates", "upstream_impact",
11
+ "version", "digest", "evidence_refs"
12
+ ];
13
+ const statuses = new Set(["draft", "ready-for-human", "approved", "blocked", "stale", "drift", "new_impacts", "not-applicable"]);
14
+ const idPatterns = {
15
+ tactical_design_id: /^tactical-design\.[a-z0-9][a-z0-9-]*$/,
16
+ aggregate_id: /^aggregate\.[a-z0-9][a-z0-9-]*$/,
17
+ entity_id: /^entity\.[a-z0-9][a-z0-9-]*$/,
18
+ value_object_id: /^value-object\.[a-z0-9][a-z0-9-]*$/,
19
+ behavior_id: /^behavior\.[a-z0-9][a-z0-9-]*$/,
20
+ invariant_id: /^invariant\.[a-z0-9][a-z0-9-]*$/,
21
+ transition_id: /^transition\.[a-z0-9][a-z0-9-]*$/,
22
+ event_id: /^domain-event\.[a-z0-9][a-z0-9-]*$/,
23
+ gateway_id: /^gateway\.[a-z0-9][a-z0-9-]*$/,
24
+ seam_id: /^test-seam\.[a-z0-9][a-z0-9-]*$/,
25
+ version: /^v[0-9]+$/
26
+ };
27
+
28
+ const isObject = (value) => value && typeof value === "object" && !Array.isArray(value);
29
+ const nonEmpty = (value) => typeof value === "string" && value.trim().length > 0;
30
+ const list = (value) => Array.isArray(value);
31
+
32
+ function unique(items, label, errors) {
33
+ const seen = new Set();
34
+ for (const item of items) {
35
+ if (seen.has(item)) errors.push(`${label} 重复: ${item}`);
36
+ seen.add(item);
37
+ }
38
+ }
39
+
40
+ function requireField(object, field, path, errors) {
41
+ if (!Object.prototype.hasOwnProperty.call(object ?? {}, field) || object[field] === null || object[field] === undefined) {
42
+ errors.push(`${path}.${field} 缺失`);
43
+ }
44
+ }
45
+
46
+ function requireString(object, field, path, errors) {
47
+ requireField(object, field, path, errors);
48
+ if (Object.prototype.hasOwnProperty.call(object ?? {}, field) && !nonEmpty(object[field])) errors.push(`${path}.${field} 必须是非空字符串`);
49
+ }
50
+
51
+ function requireArray(object, field, path, errors, minimum = 0) {
52
+ requireField(object, field, path, errors);
53
+ if (Object.prototype.hasOwnProperty.call(object ?? {}, field) && (!list(object[field]) || object[field].length < minimum)) {
54
+ errors.push(`${path}.${field} 必须是至少 ${minimum} 项的数组`);
55
+ }
56
+ }
57
+
58
+ function requireId(value, kind, path, errors) {
59
+ if (value !== undefined && value !== null && !idPatterns[kind].test(String(value))) errors.push(`${path} 格式非法`);
60
+ }
61
+
62
+ function refsExist(values, known, path, errors) {
63
+ for (const [index, value] of (values ?? []).entries()) {
64
+ if (!known.has(value)) errors.push(`${path}[${index}] 引用了未声明对象: ${value}`);
65
+ }
66
+ }
67
+
68
+ function validate(data) {
69
+ const errors = [];
70
+ if (!isObject(data)) return ["合同必须是对象"];
71
+ for (const field of required) requireField(data, field, "root", errors);
72
+ if (data.schema_version !== 1) errors.push("schema_version 必须为 1");
73
+ requireId(data.tactical_design_id, "tactical_design_id", "tactical_design_id", errors);
74
+ for (const field of ["tactical_version", "version"]) if (data[field] !== undefined && !idPatterns.version.test(String(data[field]))) errors.push(`${field} 必须形如 v1`);
75
+ if (data.status !== undefined && !statuses.has(data.status)) errors.push(`status 非法: ${data.status}`);
76
+ if (data.digest !== undefined && !/^sha256:[A-Fa-f0-9]{64}$/.test(String(data.digest))) errors.push("digest 必须为 sha256 加 64 位十六进制");
77
+
78
+ for (const field of ["aggregate_catalog", "entity_catalog", "behavior_catalog", "invariant_catalog", "state_transition_catalog", "gateway_catalog", "persistence_mapping", "test_seams"]) {
79
+ requireArray(data, field, "root", errors, 1);
80
+ }
81
+ for (const field of ["value_object_catalog", "domain_event_catalog", "adr_candidates", "evidence_refs"]) requireArray(data, field, "root", errors);
82
+ if (!isObject(data.consistency_policy)) errors.push("consistency_policy 必须是对象");
83
+ if (!isObject(data.upstream_impact)) errors.push("upstream_impact 必须是对象");
84
+ if (isObject(data.consistency_policy)) {
85
+ for (const field of ["transaction_boundary", "concurrency", "idempotency", "cross_aggregate_strategy"]) requireString(data.consistency_policy, field, "consistency_policy", errors);
86
+ }
87
+ if (isObject(data.upstream_impact)) {
88
+ for (const field of ["spec_ref", "strategic_ref", "api_ref", "data_ref"]) requireString(data.upstream_impact, field, "upstream_impact", errors);
89
+ if (data.upstream_impact.upstream_current === false && data.status !== "stale") errors.push("上游已过期时 status 必须为 stale");
90
+ }
91
+
92
+ const aggregates = list(data.aggregate_catalog) ? data.aggregate_catalog : [];
93
+ const entities = list(data.entity_catalog) ? data.entity_catalog : [];
94
+ const values = list(data.value_object_catalog) ? data.value_object_catalog : [];
95
+ const behaviors = list(data.behavior_catalog) ? data.behavior_catalog : [];
96
+ const invariants = list(data.invariant_catalog) ? data.invariant_catalog : [];
97
+ const transitions = list(data.state_transition_catalog) ? data.state_transition_catalog : [];
98
+ const events = list(data.domain_event_catalog) ? data.domain_event_catalog : [];
99
+ const gateways = list(data.gateway_catalog) ? data.gateway_catalog : [];
100
+ const mappings = list(data.persistence_mapping) ? data.persistence_mapping : [];
101
+ const seams = list(data.test_seams) ? data.test_seams : [];
102
+ const ids = (items, field, kind, label) => {
103
+ const result = new Set();
104
+ unique(items.map((item) => item?.[field]).filter(Boolean), label, errors);
105
+ for (const [index, item] of items.entries()) {
106
+ if (item?.[field]) { requireId(item[field], kind, `${label}[${index}].${field}`, errors); result.add(item[field]); }
107
+ }
108
+ return result;
109
+ };
110
+ const aggregateIds = ids(aggregates, "aggregate_id", "aggregate_id", "aggregate_id");
111
+ const entityIds = ids(entities, "entity_id", "entity_id", "entity_id");
112
+ const valueIds = ids(values, "value_object_id", "value_object_id", "value_object_id");
113
+ const behaviorIds = ids(behaviors, "behavior_id", "behavior_id", "behavior_id");
114
+ const invariantIds = ids(invariants, "invariant_id", "invariant_id", "invariant_id");
115
+ const transitionIds = ids(transitions, "transition_id", "transition_id", "transition_id");
116
+ const eventIds = ids(events, "event_id", "event_id", "event_id");
117
+ const gatewayIds = ids(gateways, "gateway_id", "gateway_id", "gateway_id");
118
+ ids(seams, "seam_id", "seam_id", "seam_id");
119
+
120
+ for (const [index, aggregate] of aggregates.entries()) {
121
+ const path = `aggregate_catalog[${index}]`;
122
+ if (!isObject(aggregate)) { errors.push(`${path} 必须是对象`); continue; }
123
+ for (const field of ["aggregate_id", "name", "context_ref", "root_entity", "consistency_boundary"]) requireString(aggregate, field, path, errors);
124
+ requireArray(aggregate, "invariant_refs", path, errors, 1); requireArray(aggregate, "behavior_refs", path, errors, 1);
125
+ if (aggregate.model_origin === "strategic-concept") errors.push(`${path}.model_origin 不得直接把战略概念当作 Aggregate`);
126
+ if (aggregate.api_exposure !== "internal-only") errors.push(`${path}.api_exposure 必须为 internal-only`);
127
+ if (aggregate.root_entity && !entityIds.has(aggregate.root_entity)) errors.push(`${path}.root_entity 未引用 Entity`);
128
+ const rootEntity = entities.find((entity) => entity?.entity_id === aggregate.root_entity);
129
+ if (rootEntity && rootEntity.aggregate_id !== aggregate.aggregate_id) errors.push(`${path}.root_entity 的 aggregate_id 必须与当前 Aggregate 一致`);
130
+ refsExist(aggregate.invariant_refs, invariantIds, `${path}.invariant_refs`, errors);
131
+ refsExist(aggregate.behavior_refs, behaviorIds, `${path}.behavior_refs`, errors);
132
+ }
133
+ for (const [index, entity] of entities.entries()) {
134
+ const path = `entity_catalog[${index}]`;
135
+ if (!isObject(entity)) { errors.push(`${path} 必须是对象`); continue; }
136
+ for (const field of ["entity_id", "name", "aggregate_id", "identity", "lifecycle"]) requireString(entity, field, path, errors);
137
+ if (entity.aggregate_id && !aggregateIds.has(entity.aggregate_id)) errors.push(`${path}.aggregate_id 未引用 Aggregate`);
138
+ }
139
+ for (const [index, value] of values.entries()) {
140
+ const path = `value_object_catalog[${index}]`;
141
+ if (!isObject(value)) { errors.push(`${path} 必须是对象`); continue; }
142
+ for (const field of ["value_object_id", "name", "aggregate_id", "immutable"]) requireField(value, field, path, errors);
143
+ if (typeof value.immutable !== "boolean" || value.immutable !== true) errors.push(`${path}.immutable 必须为 true`);
144
+ if (value.aggregate_id && !aggregateIds.has(value.aggregate_id)) errors.push(`${path}.aggregate_id 未引用 Aggregate`);
145
+ }
146
+ for (const [index, behavior] of behaviors.entries()) {
147
+ const path = `behavior_catalog[${index}]`;
148
+ if (!isObject(behavior)) { errors.push(`${path} 必须是对象`); continue; }
149
+ for (const field of ["behavior_id", "name", "aggregate_id", "command"]) requireString(behavior, field, path, errors);
150
+ for (const field of ["preconditions", "invariant_refs", "postconditions", "event_refs"]) requireArray(behavior, field, path, errors, field === "invariant_refs" ? 1 : 0);
151
+ if (behavior.aggregate_id && !aggregateIds.has(behavior.aggregate_id)) errors.push(`${path}.aggregate_id 未引用 Aggregate`);
152
+ refsExist(behavior.invariant_refs, invariantIds, `${path}.invariant_refs`, errors);
153
+ refsExist(behavior.event_refs, eventIds, `${path}.event_refs`, errors);
154
+ }
155
+ for (const [index, invariant] of invariants.entries()) {
156
+ const path = `invariant_catalog[${index}]`;
157
+ if (!isObject(invariant)) { errors.push(`${path} 必须是对象`); continue; }
158
+ for (const field of ["invariant_id", "aggregate_id", "statement"]) requireString(invariant, field, path, errors);
159
+ if (invariant.aggregate_id && !aggregateIds.has(invariant.aggregate_id)) errors.push(`${path}.aggregate_id 未引用 Aggregate`);
160
+ }
161
+ for (const [index, transition] of transitions.entries()) {
162
+ const path = `state_transition_catalog[${index}]`;
163
+ if (!isObject(transition)) { errors.push(`${path} 必须是对象`); continue; }
164
+ for (const field of ["transition_id", "aggregate_id", "from", "to", "behavior_id"]) requireString(transition, field, path, errors);
165
+ if (transition.aggregate_id && !aggregateIds.has(transition.aggregate_id)) errors.push(`${path}.aggregate_id 未引用 Aggregate`);
166
+ if (transition.behavior_id && !behaviorIds.has(transition.behavior_id)) errors.push(`${path}.behavior_id 未引用行为`);
167
+ }
168
+ for (const [index, event] of events.entries()) {
169
+ const path = `domain_event_catalog[${index}]`;
170
+ if (!isObject(event)) { errors.push(`${path} 必须是对象`); continue; }
171
+ for (const field of ["event_id", "name", "aggregate_id", "delivery"]) requireString(event, field, path, errors);
172
+ requireArray(event, "consumers", path, errors, 0);
173
+ if (event.aggregate_id && !aggregateIds.has(event.aggregate_id)) errors.push(`${path}.aggregate_id 未引用 Aggregate`);
174
+ }
175
+ for (const [index, gateway] of gateways.entries()) {
176
+ const path = `gateway_catalog[${index}]`;
177
+ if (!isObject(gateway)) { errors.push(`${path} 必须是对象`); continue; }
178
+ for (const field of ["gateway_id", "name", "context_ref", "layer"]) requireString(gateway, field, path, errors);
179
+ requireArray(gateway, "capabilities", path, errors, 1);
180
+ if (gateway.layer !== "Domain") errors.push(`${path}.layer 必须为 Domain,Infrastructure 只实现 Gateway`);
181
+ }
182
+ for (const [index, mapping] of mappings.entries()) {
183
+ const path = `persistence_mapping[${index}]`;
184
+ if (!isObject(mapping)) { errors.push(`${path} 必须是对象`); continue; }
185
+ for (const field of ["aggregate_id", "storage_model", "notes"]) requireString(mapping, field, path, errors);
186
+ if (mapping.aggregate_id && !aggregateIds.has(mapping.aggregate_id)) errors.push(`${path}.aggregate_id 未引用 Aggregate`);
187
+ }
188
+ for (const [index, seam] of seams.entries()) {
189
+ const path = `test_seams[${index}]`;
190
+ if (!isObject(seam)) { errors.push(`${path} 必须是对象`); continue; }
191
+ for (const field of ["seam_id", "kind", "subject_ref", "assertion"]) requireString(seam, field, path, errors);
192
+ if (seam.subject_ref && !new Set([...aggregateIds, ...entityIds, ...valueIds, ...behaviorIds, ...invariantIds, ...transitionIds, ...gatewayIds]).has(seam.subject_ref)) errors.push(`${path}.subject_ref 未引用已声明领域对象`);
193
+ }
194
+ if (isObject(data.complexity) && data.complexity.escalate_to_standalone === true && !nonEmpty(data.complexity.standalone_ref)) errors.push("complexity.escalate_to_standalone 为 true 时必须提供 standalone_ref");
195
+ return errors;
196
+ }
197
+
198
+ async function main() {
199
+ const file = process.argv[2];
200
+ if (!file) throw new Error("用法: validate-tactical-design.mjs <contract.yaml>");
201
+ const source = await readFile(file, "utf8");
202
+ const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
203
+ if (document.errors.length) throw new Error(`无法解析合同: ${document.errors[0].message}`);
204
+ const errors = validate(document.toJS({ maxAliasCount: 0 }));
205
+ if (errors.length) {
206
+ process.stderr.write(`${JSON.stringify({ result: "blocked", errors }, null, 2)}\n`);
207
+ process.exitCode = 1;
208
+ return;
209
+ }
210
+ process.stdout.write(`${JSON.stringify({ result: "ready-for-lifecycle-review", tactical_design_id: document.get("tactical_design_id"), status: document.get("status") }, null, 2)}\n`);
211
+ }
212
+
213
+ main().catch((error) => { process.stderr.write(`${error.message}\n`); process.exitCode = 1; });
@@ -0,0 +1,21 @@
1
+ schema_version: 1
2
+ tactical_design_id: tactical-design.invalid-api
3
+ tactical_version: v1
4
+ status: ready-for-human
5
+ context_ref: context.example
6
+ aggregate_catalog: [{aggregate_id: aggregate.example, name: Example, context_ref: context.example, root_entity: entity.example-root, invariant_refs: [invariant.example], behavior_refs: [behavior.example], consistency_boundary: single, api_exposure: public-aggregate}]
7
+ entity_catalog: [{entity_id: entity.example-root, name: Root, aggregate_id: aggregate.example, identity: id, lifecycle: active}]
8
+ value_object_catalog: []
9
+ behavior_catalog: [{behavior_id: behavior.example, name: Do, aggregate_id: aggregate.example, command: do, preconditions: [ready], invariant_refs: [invariant.example], postconditions: [done], event_refs: []}]
10
+ invariant_catalog: [{invariant_id: invariant.example, aggregate_id: aggregate.example, statement: required}]
11
+ state_transition_catalog: [{transition_id: transition.example, aggregate_id: aggregate.example, from: draft, to: done, behavior_id: behavior.example}]
12
+ consistency_policy: {transaction_boundary: single, concurrency: optimistic, idempotency: guarded, cross_aggregate_strategy: none}
13
+ domain_event_catalog: []
14
+ gateway_catalog: [{gateway_id: gateway.example, name: Gateway, context_ref: context.example, layer: Domain, capabilities: [load]}]
15
+ persistence_mapping: [{aggregate_id: aggregate.example, storage_model: example, notes: mapping}]
16
+ test_seams: [{seam_id: test-seam.example, kind: domain, subject_ref: invariant.example, assertion: behavior}]
17
+ adr_candidates: []
18
+ upstream_impact: {spec_ref: spec@v1, strategic_ref: strategy@v1, api_ref: api@draft, data_ref: data@v1, upstream_current: true}
19
+ version: v1
20
+ digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
21
+ evidence_refs: []
@@ -0,0 +1,21 @@
1
+ schema_version: 1
2
+ tactical_design_id: tactical-design.invalid-consistency
3
+ tactical_version: v1
4
+ status: ready-for-human
5
+ context_ref: context.example
6
+ aggregate_catalog: [{aggregate_id: aggregate.example, name: Example, context_ref: context.example, root_entity: entity.example-root, invariant_refs: [invariant.example], behavior_refs: [behavior.example], consistency_boundary: single, api_exposure: internal-only}]
7
+ entity_catalog: [{entity_id: entity.example-root, name: Root, aggregate_id: aggregate.example, identity: id, lifecycle: active}]
8
+ value_object_catalog: []
9
+ behavior_catalog: [{behavior_id: behavior.example, name: Do, aggregate_id: aggregate.example, command: do, preconditions: [ready], invariant_refs: [invariant.example], postconditions: [done], event_refs: []}]
10
+ invariant_catalog: [{invariant_id: invariant.example, aggregate_id: aggregate.example, statement: required}]
11
+ state_transition_catalog: [{transition_id: transition.example, aggregate_id: aggregate.example, from: draft, to: done, behavior_id: behavior.example}]
12
+ consistency_policy: {}
13
+ domain_event_catalog: []
14
+ gateway_catalog: [{gateway_id: gateway.example, name: Gateway, context_ref: context.example, layer: Domain, capabilities: [load]}]
15
+ persistence_mapping: [{aggregate_id: aggregate.example, storage_model: example, notes: mapping}]
16
+ test_seams: [{seam_id: test-seam.example, kind: domain, subject_ref: invariant.example, assertion: behavior}]
17
+ adr_candidates: []
18
+ upstream_impact: {spec_ref: spec@v1, strategic_ref: strategy@v1, api_ref: api@draft, data_ref: data@v1, upstream_current: true}
19
+ version: v1
20
+ digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
21
+ evidence_refs: []
@@ -0,0 +1,21 @@
1
+ schema_version: 1
2
+ tactical_design_id: tactical-design.invalid-missing-invariant
3
+ tactical_version: v1
4
+ status: ready-for-human
5
+ context_ref: context.example
6
+ aggregate_catalog: [{aggregate_id: aggregate.example, name: Example, context_ref: context.example, root_entity: entity.example-root, invariant_refs: [], behavior_refs: [behavior.example], consistency_boundary: single, api_exposure: internal-only}]
7
+ entity_catalog: [{entity_id: entity.example-root, name: Root, aggregate_id: aggregate.example, identity: id, lifecycle: active}]
8
+ value_object_catalog: []
9
+ behavior_catalog: [{behavior_id: behavior.example, name: Do, aggregate_id: aggregate.example, command: do, preconditions: [], invariant_refs: [], postconditions: [], event_refs: []}]
10
+ invariant_catalog: []
11
+ state_transition_catalog: [{transition_id: transition.example, aggregate_id: aggregate.example, from: draft, to: done, behavior_id: behavior.example}]
12
+ consistency_policy: {transaction_boundary: single, concurrency: optimistic, idempotency: guarded, cross_aggregate_strategy: none}
13
+ domain_event_catalog: []
14
+ gateway_catalog: [{gateway_id: gateway.example, name: Gateway, context_ref: context.example, layer: Domain, capabilities: [load]}]
15
+ persistence_mapping: [{aggregate_id: aggregate.example, storage_model: example, notes: mapping}]
16
+ test_seams: [{seam_id: test-seam.example, kind: domain, subject_ref: aggregate.example, assertion: behavior}]
17
+ adr_candidates: []
18
+ upstream_impact: {spec_ref: spec@v1, strategic_ref: strategy@v1, api_ref: api@draft, data_ref: data@v1, upstream_current: true}
19
+ version: v1
20
+ digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
21
+ evidence_refs: []
@@ -0,0 +1,21 @@
1
+ schema_version: 1
2
+ tactical_design_id: tactical-design.invalid-stale
3
+ tactical_version: v1
4
+ status: ready-for-human
5
+ context_ref: context.example
6
+ aggregate_catalog: [{aggregate_id: aggregate.example, name: Example, context_ref: context.example, root_entity: entity.example-root, invariant_refs: [invariant.example], behavior_refs: [behavior.example], consistency_boundary: single, api_exposure: internal-only}]
7
+ entity_catalog: [{entity_id: entity.example-root, name: Root, aggregate_id: aggregate.example, identity: id, lifecycle: active}]
8
+ value_object_catalog: []
9
+ behavior_catalog: [{behavior_id: behavior.example, name: Do, aggregate_id: aggregate.example, command: do, preconditions: [ready], invariant_refs: [invariant.example], postconditions: [done], event_refs: []}]
10
+ invariant_catalog: [{invariant_id: invariant.example, aggregate_id: aggregate.example, statement: required}]
11
+ state_transition_catalog: [{transition_id: transition.example, aggregate_id: aggregate.example, from: draft, to: done, behavior_id: behavior.example}]
12
+ consistency_policy: {transaction_boundary: single, concurrency: optimistic, idempotency: guarded, cross_aggregate_strategy: none}
13
+ domain_event_catalog: []
14
+ gateway_catalog: [{gateway_id: gateway.example, name: Gateway, context_ref: context.example, layer: Domain, capabilities: [load]}]
15
+ persistence_mapping: [{aggregate_id: aggregate.example, storage_model: example, notes: mapping}]
16
+ test_seams: [{seam_id: test-seam.example, kind: domain, subject_ref: invariant.example, assertion: behavior}]
17
+ adr_candidates: []
18
+ upstream_impact: {spec_ref: spec@v1, strategic_ref: strategy@v1, api_ref: api@draft, data_ref: data@v1, upstream_current: false}
19
+ version: v1
20
+ digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
21
+ evidence_refs: []
@@ -0,0 +1,21 @@
1
+ schema_version: 1
2
+ tactical_design_id: tactical-design.invalid-strategic
3
+ tactical_version: v1
4
+ status: ready-for-human
5
+ context_ref: context.example
6
+ aggregate_catalog: [{aggregate_id: aggregate.example, name: Example, context_ref: context.example, root_entity: entity.example-root, model_origin: strategic-concept, invariant_refs: [invariant.example], behavior_refs: [behavior.example], consistency_boundary: single, api_exposure: internal-only}]
7
+ entity_catalog: [{entity_id: entity.example-root, name: Root, aggregate_id: aggregate.example, identity: id, lifecycle: active}]
8
+ value_object_catalog: []
9
+ behavior_catalog: [{behavior_id: behavior.example, name: Do, aggregate_id: aggregate.example, command: do, preconditions: [ready], invariant_refs: [invariant.example], postconditions: [done], event_refs: []}]
10
+ invariant_catalog: [{invariant_id: invariant.example, aggregate_id: aggregate.example, statement: required}]
11
+ state_transition_catalog: [{transition_id: transition.example, aggregate_id: aggregate.example, from: draft, to: done, behavior_id: behavior.example}]
12
+ consistency_policy: {transaction_boundary: single, concurrency: optimistic, idempotency: guarded, cross_aggregate_strategy: none}
13
+ domain_event_catalog: []
14
+ gateway_catalog: [{gateway_id: gateway.example, name: Gateway, context_ref: context.example, layer: Domain, capabilities: [load]}]
15
+ persistence_mapping: [{aggregate_id: aggregate.example, storage_model: example, notes: mapping}]
16
+ test_seams: [{seam_id: test-seam.example, kind: domain, subject_ref: invariant.example, assertion: behavior}]
17
+ adr_candidates: []
18
+ upstream_impact: {spec_ref: spec@v1, strategic_ref: strategy@v1, api_ref: api@draft, data_ref: data@v1, upstream_current: true}
19
+ version: v1
20
+ digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
21
+ evidence_refs: []
@@ -0,0 +1,80 @@
1
+ schema_version: 1
2
+ tactical_design_id: tactical-design.example-feature
3
+ tactical_version: v1
4
+ status: ready-for-human
5
+ context_ref: context.example
6
+ aggregate_catalog:
7
+ - aggregate_id: aggregate.example
8
+ name: ExampleAggregate
9
+ context_ref: context.example
10
+ root_entity: entity.example-root
11
+ invariant_refs: [invariant.example-required]
12
+ behavior_refs: [behavior.example-publish]
13
+ consistency_boundary: single-aggregate-transaction
14
+ api_exposure: internal-only
15
+ entity_catalog:
16
+ - entity_id: entity.example-root
17
+ name: ExampleRoot
18
+ aggregate_id: aggregate.example
19
+ identity: stable-id
20
+ lifecycle: draft-published
21
+ value_object_catalog:
22
+ - value_object_id: value-object.example-code
23
+ name: ExampleCode
24
+ aggregate_id: aggregate.example
25
+ immutable: true
26
+ behavior_catalog:
27
+ - behavior_id: behavior.example-publish
28
+ name: Publish
29
+ aggregate_id: aggregate.example
30
+ command: publish
31
+ preconditions: ["状态为 draft"]
32
+ invariant_refs: [invariant.example-required]
33
+ postconditions: ["状态变为 published"]
34
+ event_refs: [domain-event.example-published]
35
+ invariant_catalog:
36
+ - invariant_id: invariant.example-required
37
+ aggregate_id: aggregate.example
38
+ statement: "发布前必须满足业务前置条件"
39
+ state_transition_catalog:
40
+ - transition_id: transition.example-publish
41
+ aggregate_id: aggregate.example
42
+ from: draft
43
+ to: published
44
+ behavior_id: behavior.example-publish
45
+ consistency_policy:
46
+ transaction_boundary: single aggregate
47
+ concurrency: optimistic version check
48
+ idempotency: repeated publish is rejected by state guard
49
+ cross_aggregate_strategy: domain event with retry
50
+ domain_event_catalog:
51
+ - event_id: domain-event.example-published
52
+ name: ExamplePublished
53
+ aggregate_id: aggregate.example
54
+ consumers: [downstream-context]
55
+ delivery: outbox-at-least-once
56
+ gateway_catalog:
57
+ - gateway_id: gateway.example
58
+ name: ExampleGateway
59
+ context_ref: context.example
60
+ layer: Domain
61
+ capabilities: [save aggregate, load aggregate]
62
+ persistence_mapping:
63
+ - aggregate_id: aggregate.example
64
+ storage_model: example_aggregate_snapshot
65
+ notes: "持久化结构不泄漏到领域模型"
66
+ test_seams:
67
+ - seam_id: test-seam.example-invariant
68
+ kind: domain-behavior
69
+ subject_ref: invariant.example-required
70
+ assertion: "不满足前置条件时 publish 失败"
71
+ adr_candidates: []
72
+ upstream_impact:
73
+ spec_ref: docs/.scratch/example/spec.yaml@v1
74
+ strategic_ref: docs/.scratch/example/domain-strategy.yaml@v1
75
+ api_ref: docs/.scratch/example/openapi.yaml@draft
76
+ data_ref: docs/.scratch/example/data-architecture.md@v1
77
+ upstream_current: true
78
+ version: v1
79
+ digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
80
+ evidence_refs: [docs/.scratch/example/tactical-review.yaml]
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { spawnSync } from "node:child_process";
6
+ import { tmpdir } from "node:os";
7
+
8
+ const root = dirname(fileURLToPath(import.meta.url));
9
+ const validator = join(root, "..", "scripts", "validate-tactical-design.mjs");
10
+ const fixture = (name) => join(root, "fixtures", name);
11
+ const run = (file) => spawnSync(process.execPath, [validator, file], { encoding: "utf8" });
12
+ const pass = run(fixture("valid-tactical-design.yaml"));
13
+ if (pass.status !== 0) throw new Error(`valid tactical design should pass: ${pass.stderr}`);
14
+
15
+ const blockedCases = [
16
+ ["invalid-missing-invariant.yaml", "invariant_refs 必须是至少 1 项的数组"],
17
+ ["invalid-api-leaks-aggregate.yaml", "api_exposure 必须为 internal-only"],
18
+ ["invalid-stale-upstream-ref.yaml", "status 必须为 stale"],
19
+ ["invalid-strategic-as-tactical.yaml", "不得直接把战略概念当作 Aggregate"],
20
+ ["invalid-missing-consistency-policy.yaml", "consistency_policy.transaction_boundary 缺失"]
21
+ ];
22
+ for (const [name, message] of blockedCases) {
23
+ const result = run(fixture(name));
24
+ if (result.status === 0 || !result.stderr.includes(message)) throw new Error(`${name} should be blocked: ${result.stderr}`);
25
+ }
26
+
27
+ const temporaryRoot = await mkdtemp(join(tmpdir(), "yss-tactical-design-pressure-"));
28
+ try {
29
+ const source = await readFile(fixture("valid-tactical-design.yaml"), "utf8");
30
+ const escalated = join(temporaryRoot, "missing-standalone-ref.yaml");
31
+ await writeFile(escalated, `${source}\ncomplexity:\n escalate_to_standalone: true\n`);
32
+ const result = run(escalated);
33
+ if (result.status === 0 || !result.stderr.includes("standalone_ref")) throw new Error("complexity escalation without standalone_ref should be blocked");
34
+
35
+ const wrongGateway = join(temporaryRoot, "wrong-gateway-layer.yaml");
36
+ await writeFile(wrongGateway, source.replace("layer: Domain", "layer: Infrastructure"));
37
+ const gatewayResult = run(wrongGateway);
38
+ if (gatewayResult.status === 0 || !gatewayResult.stderr.includes("layer 必须为 Domain")) throw new Error("Infrastructure gateway should be blocked");
39
+
40
+ const crossAggregateRoot = join(temporaryRoot, "cross-aggregate-root.yaml");
41
+ await writeFile(crossAggregateRoot, source.replace("aggregate_id: aggregate.example\n identity", "aggregate_id: aggregate.other\n identity"));
42
+ const crossAggregateResult = run(crossAggregateRoot);
43
+ if (crossAggregateResult.status === 0 || !crossAggregateResult.stderr.includes("root_entity 的 aggregate_id 必须与当前 Aggregate 一致")) throw new Error("cross-aggregate root should be blocked");
44
+ } finally {
45
+ await rm(temporaryRoot, { recursive: true, force: true });
46
+ }
47
+ process.stdout.write("DDD tactical design scenarios passed\n");