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,259 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { parseDocument } from "../vendor/yaml.mjs";
3
+
4
+ const IMPLEMENTATION_WORK_UNIT = "work-unit.slice-implementation";
5
+ const TICKET_DECOMPOSITION_WORK_UNIT = "work-unit.ticket-decomposition";
6
+
7
+ function deepFreeze(value) {
8
+ if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
9
+ Object.freeze(value);
10
+ for (const child of Object.values(value)) deepFreeze(child);
11
+ return value;
12
+ }
13
+
14
+ const NEXT_ROUTES = deepFreeze({
15
+ "work-unit.entry-triage": ["work-unit.discovery-opportunity", "work-unit.discovery-requirements"],
16
+ "work-unit.ssot-update": ["work-unit.skill-projection-sync", "work-unit.intensity-aware-verification"],
17
+ "work-unit.skill-projection-sync": ["work-unit.template-snapshot-build", "work-unit.intensity-aware-verification"],
18
+ "work-unit.template-snapshot-build": ["work-unit.attach-sync-integration", "work-unit.intensity-aware-verification"],
19
+ "work-unit.attach-sync-integration": ["work-unit.intensity-aware-verification"],
20
+ "work-unit.intensity-aware-verification": ["work-unit.intensity-aware-review"],
21
+ "work-unit.intensity-aware-review": ["work-unit.release-and-rollback"],
22
+ "work-unit.release-and-rollback": [],
23
+ "work-unit.discovery-opportunity": ["work-unit.discovery-requirements", "work-unit.domain-strategy-design", "work-unit.stage-decision", "work-unit.spec-synthesis"],
24
+ "work-unit.discovery-requirements": ["work-unit.domain-strategy-design", "work-unit.stage-decision", "work-unit.spec-synthesis"],
25
+ "work-unit.domain-strategy-design": ["work-unit.stage-decision", "work-unit.spec-synthesis"],
26
+ "work-unit.stage-decision": ["work-unit.spec-synthesis"],
27
+ "work-unit.spec-synthesis": ["work-unit.prototype-design", "work-unit.technical-analysis", TICKET_DECOMPOSITION_WORK_UNIT],
28
+ "work-unit.prototype-design": ["work-unit.technical-analysis", TICKET_DECOMPOSITION_WORK_UNIT],
29
+ "work-unit.technical-analysis": [TICKET_DECOMPOSITION_WORK_UNIT],
30
+ [TICKET_DECOMPOSITION_WORK_UNIT]: [IMPLEMENTATION_WORK_UNIT],
31
+ [IMPLEMENTATION_WORK_UNIT]: ["work-unit.frontend-implementation-verification", "work-unit.code-review"],
32
+ "work-unit.frontend-implementation-verification": ["work-unit.code-review"],
33
+ "work-unit.code-review": ["work-unit.release-and-retrospective", IMPLEMENTATION_WORK_UNIT],
34
+ "work-unit.release-and-retrospective": [],
35
+ });
36
+
37
+ const BLOCKING_SIGNALS = Object.freeze({
38
+ invalidRoute: "illegal-next-route",
39
+ implementationBeforeTickets: "ticket-formalization-required",
40
+ invalidPredecessor: "invalid-implementation-predecessor",
41
+ decompositionIncomplete: "ticket-decomposition-incomplete",
42
+ decompositionRefMissing: "ticket-decomposition-result-ref-missing",
43
+ missingSlice: "vertical-slice-ticket-required",
44
+ sliceRefMissing: "vertical-slice-ticket-ref-missing",
45
+ parentTicket: "parent-ticket-forbidden",
46
+ wrongKind: "vertical-slice-ticket-kind-invalid",
47
+ kindMissing: "vertical-slice-ticket-kind-missing",
48
+ unreadableSlice: "vertical-slice-ticket-unreadable",
49
+ wrongRole: "ticket-not-ready-for-agent",
50
+ roleMissing: "vertical-slice-ticket-role-missing",
51
+ contractMismatch: "slice-contract-ticket-mismatch",
52
+ contractNotApproved: "slice-contract-not-approved",
53
+ contractNotPersisted: "slice-contract-not-persisted",
54
+ contractNotCurrent: "slice-contract-not-current",
55
+ missingEvidence: "ticket-formalization-evidence-missing",
56
+ stale: "ticket-formalization-stale",
57
+ });
58
+
59
+ const allowedResult = (evidenceRefs = []) => ({
60
+ result: "allowed",
61
+ blocking_signals: [],
62
+ missing_requirements: [],
63
+ evidence_refs: evidenceRefs,
64
+ next_work_unit: null,
65
+ });
66
+
67
+ const blockedResult = (signals, missing = [], evidenceRefs = []) => ({
68
+ result: "blocked",
69
+ blocking_signals: [...new Set(signals)],
70
+ missing_requirements: [...new Set(missing)],
71
+ evidence_refs: evidenceRefs,
72
+ next_work_unit: null,
73
+ });
74
+
75
+ function hasText(value) {
76
+ return typeof value === "string" && value.trim().length > 0;
77
+ }
78
+
79
+ function isReadable(ref, exists) {
80
+ return hasText(ref) && exists(ref);
81
+ }
82
+
83
+ function readDecompositionResult(ref, read) {
84
+ if (!hasText(ref)) return null;
85
+ try {
86
+ const document = parseDocument(read(ref), { maxAliasCount: 0, uniqueKeys: true });
87
+ if (document.errors.length > 0) return null;
88
+ const value = document.toJS({ maxAliasCount: 0 });
89
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
94
+
95
+ function validateTicketPath(ref) {
96
+ if (!hasText(ref)) return false;
97
+ return /^docs\/\.scratch\/[^/]+\/issues\/[^/]+\.md$/.test(ref);
98
+ }
99
+
100
+ function validateTicketReference(ref, trackerKind) {
101
+ if (trackerKind === "github" || trackerKind === "gitlab") {
102
+ return /^(https?:\/\/|(?:github|gitlab):)[^\s]+$/.test(ref);
103
+ }
104
+ return validateTicketPath(ref);
105
+ }
106
+
107
+ /**
108
+ * Validate a lifecycle work-unit transition without mutating state.
109
+ * `nextRoute` is null only for a terminal work unit. A blocked or needs-human
110
+ * Workflow Execution Result may omit a route; completed results are checked
111
+ * by `validateWorkflowExecutionResult` before this function is called.
112
+ */
113
+ export function validateNextRoute(currentWorkUnit, nextRoute) {
114
+ const routes = NEXT_ROUTES[currentWorkUnit];
115
+ if (!routes) return blockedResult([BLOCKING_SIGNALS.invalidRoute], ["known_current_work_unit"]);
116
+ if (nextRoute === null && routes.length === 0) return allowedResult();
117
+ if (!hasText(nextRoute) || !routes.includes(nextRoute)) {
118
+ const signals = [BLOCKING_SIGNALS.invalidRoute];
119
+ if (nextRoute === IMPLEMENTATION_WORK_UNIT && currentWorkUnit !== TICKET_DECOMPOSITION_WORK_UNIT) {
120
+ signals.push(BLOCKING_SIGNALS.implementationBeforeTickets);
121
+ }
122
+ return blockedResult(signals, ["allowed_next_route"]);
123
+ }
124
+ return allowedResult();
125
+ }
126
+
127
+ /**
128
+ * Validate that Ticket formalization produced a real, implementable vertical slice.
129
+ * `exists` is injectable so external adapters can resolve their own tracker refs.
130
+ */
131
+ export function validateTicketFormalization(state, { exists = existsSync, read = (ref) => readFileSync(ref, "utf8") } = {}) {
132
+ const decomposition = state?.ticket_decomposition_result;
133
+ const ticket = state?.vertical_slice_ticket;
134
+ const contract = state?.slice_contract;
135
+ const decompositionRef = state?.ticket_decomposition_result_ref;
136
+ const sliceRef = state?.vertical_slice_ticket_ref;
137
+ const sliceRole = state?.vertical_slice_ticket_role;
138
+ const sliceKind = state?.vertical_slice_ticket_kind;
139
+ const trackerKind = state?.tracker_kind ?? "local-markdown";
140
+ const evidenceRefs = decomposition?.evidence_refs ?? [];
141
+ const persistedDecomposition = readDecompositionResult(decompositionRef, read);
142
+ const signals = [];
143
+ const missing = [];
144
+
145
+ if (decomposition?.result !== "completed") {
146
+ signals.push(BLOCKING_SIGNALS.decompositionIncomplete);
147
+ missing.push("ticket_decomposition_result.result=completed");
148
+ }
149
+ if (!persistedDecomposition || persistedDecomposition.result_schema !== "workflow-execution-result-v1" || persistedDecomposition.result !== "completed" || persistedDecomposition.work_unit !== TICKET_DECOMPOSITION_WORK_UNIT) {
150
+ signals.push(BLOCKING_SIGNALS.decompositionIncomplete);
151
+ missing.push("ticket_decomposition_result_ref must contain completed work-unit.ticket-decomposition result");
152
+ }
153
+ if (persistedDecomposition && Array.isArray(persistedDecomposition.evidence_refs)) {
154
+ if (persistedDecomposition.evidence_refs.some((ref) => !isReadable(ref, exists))) {
155
+ signals.push(BLOCKING_SIGNALS.missingEvidence);
156
+ missing.push("persisted decomposition result evidence_refs must be readable");
157
+ }
158
+ }
159
+ if (!isReadable(decompositionRef, exists) || !evidenceRefs.includes(decompositionRef)) {
160
+ signals.push(BLOCKING_SIGNALS.decompositionRefMissing);
161
+ missing.push("readable ticket_decomposition_result_ref included in evidence_refs");
162
+ }
163
+ if (!Array.isArray(evidenceRefs) || evidenceRefs.length === 0 || evidenceRefs.some((ref) => !isReadable(ref, exists))) {
164
+ signals.push(BLOCKING_SIGNALS.missingEvidence);
165
+ missing.push("readable ticket_decomposition_result.evidence_refs");
166
+ }
167
+ if (!hasText(sliceRef)) {
168
+ signals.push(BLOCKING_SIGNALS.sliceRefMissing);
169
+ missing.push("vertical_slice_ticket_ref");
170
+ }
171
+ if (!hasText(sliceKind)) {
172
+ signals.push(BLOCKING_SIGNALS.kindMissing);
173
+ missing.push("vertical_slice_ticket_kind");
174
+ }
175
+ if (!hasText(sliceRole)) {
176
+ signals.push(BLOCKING_SIGNALS.roleMissing);
177
+ missing.push("vertical_slice_ticket_role");
178
+ }
179
+ if (!ticket || !hasText(ticket.ref)) {
180
+ signals.push(BLOCKING_SIGNALS.missingSlice);
181
+ missing.push("vertical_slice_ticket.ref");
182
+ } else {
183
+ if (sliceRef !== ticket.ref) {
184
+ signals.push(BLOCKING_SIGNALS.contractMismatch);
185
+ missing.push("vertical_slice_ticket_ref=vertical_slice_ticket.ref");
186
+ }
187
+ if (sliceKind !== ticket.kind) {
188
+ signals.push(BLOCKING_SIGNALS.wrongKind);
189
+ missing.push("vertical_slice_ticket_kind=vertical_slice_ticket.kind");
190
+ }
191
+ if (sliceRole !== ticket.role) {
192
+ signals.push(BLOCKING_SIGNALS.wrongRole);
193
+ missing.push("vertical_slice_ticket_role=vertical_slice_ticket.role");
194
+ }
195
+ if (!validateTicketReference(ticket.ref, trackerKind)) {
196
+ signals.push(ticket.kind === "parent-ticket" || ticket.ref.endsWith("/parent-ticket.md") ? BLOCKING_SIGNALS.parentTicket : BLOCKING_SIGNALS.missingSlice);
197
+ missing.push(trackerKind === "local-markdown" ? "vertical_slice_ticket.ref under docs/.scratch/<feature>/issues/" : "remote tracker Ticket reference");
198
+ }
199
+ if (ticket.kind === "parent-ticket" || ticket.ref.endsWith("/parent-ticket.md")) {
200
+ signals.push(BLOCKING_SIGNALS.parentTicket);
201
+ missing.push("vertical_slice_ticket.kind=vertical-slice-ticket");
202
+ } else if (ticket.kind !== "vertical-slice-ticket") {
203
+ signals.push(BLOCKING_SIGNALS.wrongKind);
204
+ missing.push("vertical_slice_ticket.kind=vertical-slice-ticket");
205
+ }
206
+ if (ticket.role !== "ready-for-agent") {
207
+ signals.push(BLOCKING_SIGNALS.wrongRole);
208
+ missing.push("vertical_slice_ticket.role=ready-for-agent");
209
+ }
210
+ if (trackerKind === "local-markdown" && !isReadable(ticket.ref, exists)) {
211
+ signals.push(BLOCKING_SIGNALS.unreadableSlice);
212
+ missing.push("readable vertical_slice_ticket.ref");
213
+ }
214
+ }
215
+ if (!contract || !hasText(contract.ticket_ref) || contract.ticket_ref !== ticket?.ref) {
216
+ signals.push(BLOCKING_SIGNALS.contractMismatch);
217
+ missing.push("slice_contract.ticket_ref=vertical_slice_ticket.ref");
218
+ }
219
+ if (contract?.status !== "approved") {
220
+ signals.push(BLOCKING_SIGNALS.contractNotApproved);
221
+ missing.push("slice_contract.status=approved");
222
+ }
223
+ if (contract?.persisted !== true) {
224
+ signals.push(BLOCKING_SIGNALS.contractNotPersisted);
225
+ missing.push("slice_contract.persisted=true");
226
+ }
227
+ if (contract?.current_version !== true) {
228
+ signals.push(BLOCKING_SIGNALS.contractNotCurrent);
229
+ missing.push("slice_contract.current_version=true");
230
+ }
231
+ if (state?.stale === true || (Array.isArray(state?.stale_inputs) && state.stale_inputs.length > 0)) {
232
+ signals.push(BLOCKING_SIGNALS.stale);
233
+ missing.push("ticket formalization inputs are current");
234
+ }
235
+
236
+ return signals.length === 0 ? allowedResult(evidenceRefs) : blockedResult(signals, missing, evidenceRefs);
237
+ }
238
+
239
+ /**
240
+ * Validate the complete implementation entry seam after ready-for-agent promotion.
241
+ */
242
+ export function validateImplementationEntry(state, options = {}) {
243
+ const ticketResult = validateTicketFormalization(state, options);
244
+ if (ticketResult.result === "blocked") return ticketResult;
245
+ if (state?.predecessor_work_unit !== TICKET_DECOMPOSITION_WORK_UNIT) {
246
+ return blockedResult([BLOCKING_SIGNALS.invalidPredecessor], ["predecessor_work_unit=work-unit.ticket-decomposition"], ticketResult.evidence_refs);
247
+ }
248
+ if (state?.ready_for_agent !== true) {
249
+ return blockedResult([BLOCKING_SIGNALS.wrongRole], ["ready_for_agent=true"], ticketResult.evidence_refs);
250
+ }
251
+ return ticketResult;
252
+ }
253
+
254
+ export const lifecycleTransitionContract = Object.freeze({
255
+ implementation_work_unit: IMPLEMENTATION_WORK_UNIT,
256
+ ticket_decomposition_work_unit: TICKET_DECOMPOSITION_WORK_UNIT,
257
+ next_routes: NEXT_ROUTES,
258
+ blocking_signals: BLOCKING_SIGNALS,
259
+ });
@@ -3,12 +3,16 @@ import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { spawnSync } from "node:child_process";
5
5
  import { parseDocument } from "../vendor/yaml.mjs";
6
+ import { lifecycleTransitionContract, validateImplementationEntry, validateNextRoute } from "./lifecycle-transition.mjs";
6
7
 
7
8
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
8
9
  const read = (relative) => readFileSync(path.join(root, relative), "utf8");
9
10
  function ensure(condition, message) { if (!condition) throw new TypeError(message); }
10
11
  function exists(relative) { return existsSync(path.join(root, relative)); }
11
12
  function includesAll(actual, expected) { return Array.isArray(actual) && expected.every((item) => actual.includes(item)); }
13
+ function hasText(value) { return typeof value === "string" && value.trim().length > 0; }
14
+ const virtualTicketDecompositionRef = "docs/.scratch/demo/evidence/ticket-decomposition-result.yaml";
15
+ const virtualTicketDecomposition = "result_schema: workflow-execution-result-v1\nwork_unit: work-unit.ticket-decomposition\nresult: completed\nevidence_refs:\n - docs/.scratch/demo/evidence/ticket-decomposition-result.yaml\n";
12
16
 
13
17
  function validateMattContract(data) {
14
18
  const direct = data.entry_routing?.direct_matt_entry;
@@ -84,13 +88,52 @@ function validateWorkflowExecutionResult(payload, contract, workUnitRoutes) {
84
88
  for (const field of contract.workflow_reference.required) ensure(typeof payload.workflow_reference?.[field] === "string" && payload.workflow_reference[field].trim(), `workflow_reference.${field} 无效`);
85
89
  const workUnit = workUnitRoutes?.[payload.work_unit];
86
90
  ensure(workUnit, `未知 Workflow Execution Result work_unit: ${payload.work_unit}`);
91
+ if (payload.result === "completed") {
92
+ const routeResult = validateNextRoute(payload.work_unit, payload.next_route);
93
+ ensure(routeResult.result === "allowed", `Workflow Execution Result next_route 非法: ${routeResult.blocking_signals.join(", ")}`);
94
+ }
87
95
  const accepted = [workUnit.native, workUnit.compatibility].filter(Boolean);
88
96
  ensure(contract.workflow_reference.allowed_sources.includes(payload.workflow_reference.source) && accepted.some((route) => route.source === payload.workflow_reference.source && route.skill === payload.workflow_reference.skill && route.invocation_mode === payload.workflow_reference.invocation_mode), "Workflow Execution Result workflow_reference 与 work_unit route 不匹配");
97
+ const workUnitRequirements = contract.work_unit_requirements?.[payload.work_unit];
98
+ if (workUnitRequirements && payload.result === "completed") {
99
+ for (const field of [...(workUnitRequirements.required ?? []), ...(workUnitRequirements.completed_requires ?? [])]) {
100
+ ensure(hasText(payload[field]), `Workflow Execution Result ${payload.work_unit} 缺少 ${field}`);
101
+ }
102
+ for (const field of workUnitRequirements.boolean_required ?? []) {
103
+ ensure(typeof payload[field] === "boolean", `Workflow Execution Result ${payload.work_unit} 缺少布尔字段 ${field}`);
104
+ }
105
+ }
106
+ if (payload.work_unit === "work-unit.slice-implementation" && payload.result === "completed") {
107
+ const implementationState = {
108
+ tracker_kind: payload.tracker_kind,
109
+ predecessor_work_unit: payload.predecessor_work_unit,
110
+ ready_for_agent: payload.ready_for_agent,
111
+ ticket_decomposition_result_ref: payload.ticket_decomposition_result_ref,
112
+ vertical_slice_ticket_ref: payload.vertical_slice_ticket_ref,
113
+ vertical_slice_ticket_role: payload.vertical_slice_ticket_role,
114
+ vertical_slice_ticket_kind: payload.vertical_slice_ticket_kind,
115
+ ticket_decomposition_result: { result: payload.ticket_decomposition_result_status, evidence_refs: payload.evidence_refs },
116
+ vertical_slice_ticket: { ref: payload.vertical_slice_ticket_ref, role: payload.vertical_slice_ticket_role, kind: payload.vertical_slice_ticket_kind },
117
+ slice_contract: {
118
+ ticket_ref: payload.slice_contract_ticket_ref,
119
+ status: payload.slice_contract_status,
120
+ persisted: payload.slice_contract_persisted,
121
+ current_version: payload.slice_contract_current_version,
122
+ },
123
+ };
124
+ const semantic = validateImplementationEntry(implementationState, {
125
+ // The scenario uses one explicit virtual fixture; arbitrary local refs
126
+ // must still pass the real readability check and cannot use a fallback.
127
+ exists: (ref) => exists(ref) || ref === "docs/.scratch/demo/issues/01-valid-slice.md" || ref === virtualTicketDecompositionRef,
128
+ read: (ref) => ref === virtualTicketDecompositionRef ? virtualTicketDecomposition : readFileSync(path.join(root, ref), "utf8"),
129
+ });
130
+ ensure(semantic.result === "allowed", `Workflow Execution Result implementation Ticket 语义非法: ${semantic.blocking_signals.join(", ")}`);
131
+ }
89
132
  if (payload.result !== "completed") return;
90
133
  for (const field of contract.completed_requires_empty) ensure(Array.isArray(payload[field]) && payload[field].length === 0, `completed 的 ${field} 必须为空`);
91
134
  for (const field of contract.completed_requires_non_empty) ensure(Array.isArray(payload[field]) && payload[field].length > 0, `completed 的 ${field} 不能为空`);
92
135
  if (contract.completed_requires_readable_evidence_refs) {
93
- for (const reference of payload.evidence_refs) ensure(typeof reference === "string" && reference.trim() && exists(reference), `completed 证据不可读取: ${reference}`);
136
+ for (const reference of payload.evidence_refs) ensure(typeof reference === "string" && reference.trim() && (exists(reference) || reference === virtualTicketDecompositionRef), `completed 证据不可读取: ${reference}`);
94
137
  }
95
138
  if (contract.completed_requires_no_blocking_signals) ensure(Array.isArray(payload.blocking_signals) && payload.blocking_signals.length === 0, "completed 不得携带 blocking_signals");
96
139
  }
@@ -177,6 +220,16 @@ export function runScenario(name) {
177
220
  const releaseGate = registry.gates.find((gate) => gate.id === "gate.release-ready");
178
221
  ensure(releaseGate?.requires_gates?.includes("gate.frontend-implementation-verified"), "发布就绪未依赖前端实现还原门禁");
179
222
  const contract = parseDocument(read(".agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml"), { uniqueKeys: true }).toJS({ maxAliasCount: 0 });
223
+ ensure(contract.ready_for_agent?.requires_vertical_slice_ticket === true && contract.ready_for_agent?.parent_ticket_as_implementation_ref === "forbidden", "ready_for_agent 未强制垂直切片且禁止父 Ticket 实现引用");
224
+ ensure(includesAll(contract.ready_for_agent?.required, ["ticket_decomposition_result_ref", "vertical_slice_ticket_ref", "vertical_slice_ticket_role", "vertical_slice_ticket_kind"]), "ready_for_agent 缺少 Ticket 正式化必填字段");
225
+ ensure(contract.ticket_formalization?.implementation_predecessor === "work-unit.ticket-decomposition" && contract.ticket_formalization?.vertical_slice_ticket?.parent_ticket_ref_forbidden === true, "Ticket 正式化实现前置或父 Ticket 禁止规则缺失");
226
+ ensure(contract.transition_graph?.implementation_requires_predecessor === "work-unit.ticket-decomposition", "生命周期转换图未声明实现前置工作单元");
227
+ ensure(JSON.stringify(contract.transition_graph?.forbidden_shortcuts) === JSON.stringify([
228
+ { from: "work-unit.spec-synthesis", to: "work-unit.slice-implementation" },
229
+ { from: "work-unit.prototype-design", to: "work-unit.slice-implementation" },
230
+ { from: "work-unit.technical-analysis", to: "work-unit.slice-implementation" },
231
+ ]), "生命周期转换图缺少 Spec/原型/技术分析到实现的越级阻断");
232
+ ensure(lifecycleTransitionContract.next_routes["work-unit.ticket-decomposition"]?.includes("work-unit.slice-implementation"), "转换校验器未允许 Ticket 正式化后进入实现");
180
233
  ensure(contract.release_readiness?.conditional?.ui_impact?.includes("gate.frontend-implementation-verified") && contract.frontend_implementation_plan?.acceptance?.includes("no_template_placeholders"), "发布公式或前端计划实质校验不完整");
181
234
  const templateRejected = spawnSync("scripts/verify-frontend-implementation-evidence", ["docs/process/templates/frontend-implementation-plan-template.yaml"], { cwd: root, encoding: "utf8" });
182
235
  ensure(templateRejected.status !== 0 && templateRejected.stderr.includes("template: false"), "前端实现计划占位模板可冒充正式批准证据");
@@ -200,6 +253,46 @@ export function runScenario(name) {
200
253
  blocking_signals: []
201
254
  };
202
255
  validateWorkflowExecutionResult(validResult, data.workflow_execution_result, data.work_unit_routes);
256
+ const validTicketResult = {
257
+ ...validResult,
258
+ work_unit: "work-unit.ticket-decomposition",
259
+ next_route: "work-unit.slice-implementation",
260
+ ticket_decomposition_result_ref: "docs/.scratch/demo/evidence/ticket-decomposition-result.yaml",
261
+ vertical_slice_ticket_ref: "docs/.scratch/demo/issues/01-valid-slice.md",
262
+ };
263
+ validateWorkflowExecutionResult(validTicketResult, data.workflow_execution_result, data.work_unit_routes);
264
+ const validImplementationResult = {
265
+ ...validResult,
266
+ evidence_refs: [virtualTicketDecompositionRef, "docs/process/lifecycle-registry.yaml"],
267
+ work_unit: "work-unit.slice-implementation",
268
+ next_route: "work-unit.code-review",
269
+ predecessor_work_unit: "work-unit.ticket-decomposition",
270
+ ready_for_agent: true,
271
+ ticket_decomposition_result_ref: "docs/.scratch/demo/evidence/ticket-decomposition-result.yaml",
272
+ ticket_decomposition_result_status: "completed",
273
+ vertical_slice_ticket_ref: "docs/.scratch/demo/issues/01-valid-slice.md",
274
+ vertical_slice_ticket_role: "ready-for-agent",
275
+ vertical_slice_ticket_kind: "vertical-slice-ticket",
276
+ slice_contract_ticket_ref: "docs/.scratch/demo/issues/01-valid-slice.md",
277
+ slice_contract_status: "approved",
278
+ slice_contract_persisted: true,
279
+ slice_contract_current_version: true,
280
+ };
281
+ validateWorkflowExecutionResult(validImplementationResult, data.workflow_execution_result, data.work_unit_routes);
282
+ for (const mutate of [
283
+ (item) => { item.vertical_slice_ticket_role = "ready-for-human"; },
284
+ (item) => { item.vertical_slice_ticket_kind = "parent-ticket"; },
285
+ (item) => { item.vertical_slice_ticket_ref = "docs/.scratch/demo/parent-ticket.md"; },
286
+ (item) => { item.slice_contract_ticket_ref = "docs/.scratch/demo/issues/02-other.md"; },
287
+ (item) => { item.predecessor_work_unit = "work-unit.spec-synthesis"; },
288
+ (item) => { item.ticket_decomposition_result_status = "blocked"; },
289
+ (item) => { item.ticket_decomposition_result_status = "needs-human"; },
290
+ ]) {
291
+ const invalid = structuredClone(validImplementationResult); mutate(invalid);
292
+ let rejected = false;
293
+ try { validateWorkflowExecutionResult(invalid, data.workflow_execution_result, data.work_unit_routes); } catch { rejected = true; }
294
+ ensure(rejected, "实现 Workflow Execution Result 的 Ticket 语义变异未被拒绝");
295
+ }
203
296
  const unavailableResult = structuredClone(validResult);
204
297
  unavailableResult.result = "blocked";
205
298
  unavailableResult.unavailable_skill = { skill: "yss-ui", provider: "codex", fallback: "manual-review", resolution: "needs-human" };
@@ -221,7 +314,8 @@ export function runScenario(name) {
221
314
  (item) => { item.new_impacts = ["new-api"]; },
222
315
  (item) => { item.workflow_reference.source = "untrusted/source"; },
223
316
  (item) => { item.workflow_reference.skill = "implement"; },
224
- (item) => { item.workflow_reference.invocation_mode = "reference"; }
317
+ (item) => { item.workflow_reference.invocation_mode = "reference"; },
318
+ (item) => { item.next_route = "work-unit.slice-implementation"; }
225
319
  ]) {
226
320
  const invalid = structuredClone(validResult); mutate(invalid);
227
321
  let rejected = false;
@@ -0,0 +1,214 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { spawnSync } from "node:child_process";
3
+ import path from "node:path";
4
+ import { parseDocument } from "../vendor/yaml.mjs";
5
+ import { loadDigitalHumanRoles, taskPackageDefaults } from "./digital-human-roles.mjs";
6
+ import { loadRegistry, ROOT } from "./lifecycle-registry.mjs";
7
+ import { loadMaintenanceCheckpoint, validateMaintenanceCheckpoint } from "./maintenance-intensity.mjs";
8
+ import { validateNextRoute } from "./lifecycle-transition.mjs";
9
+
10
+ export const TASK_PACKAGE_SCHEMA = path.join(ROOT, "docs/process/schemas/digital-human-task-package.schema.json");
11
+ export const LEGACY_TASK_PACKAGE_SCHEMA = path.join(ROOT, "docs/process/schemas/subagent-task-package.schema.json");
12
+ export const TASK_PACKAGE_REGISTRY_REF = "docs/agents/digital-human-roles.yaml";
13
+ export const CONTRACT_KINDS = new Set(["lifecycle-work-unit", "slice-implementation", "template-maintenance"]);
14
+ export const EXECUTION_STATES = new Set(["Explorer", "Drafter", "Worker", "Reviewer", "Verifier"]);
15
+ export const WORKFLOW_STATUSES = new Set(["not-started", "active", "paused", "resolved", "failed"]);
16
+
17
+ function fail(message) { throw new TypeError(message); }
18
+ function requireString(value, field) { if (typeof value !== "string" || !value.trim()) fail(`${field} 不能为空`); }
19
+ function equalArrays(actual, expected) { return Array.isArray(actual) && actual.length === expected.length && actual.every((value, index) => value === expected[index]); }
20
+
21
+ function parseYaml(source, label) {
22
+ const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
23
+ if (document.errors.length > 0) fail(`无法解析${label}: ${document.errors[0].message}`);
24
+ return document.toJS({ maxAliasCount: 0 });
25
+ }
26
+
27
+ function assertSafeRelativePath(value, field) {
28
+ requireString(value, field);
29
+ if (path.isAbsolute(value)) fail(`${field} 必须是仓库相对路径`);
30
+ const resolved = path.resolve(ROOT, value);
31
+ const relative = path.relative(ROOT, resolved);
32
+ if (relative === ".." || relative.startsWith(`..${path.sep}`)) fail(`${field} 不得越出仓库根目录`);
33
+ return resolved;
34
+ }
35
+
36
+ function assertReadableEvidenceRef(value, field) {
37
+ requireString(value, field);
38
+ if (/^https?:\/\//.test(value)) return;
39
+ const evidencePath = assertSafeRelativePath(value, field);
40
+ if (!existsSync(evidencePath)) fail(`${field} 不可读: ${value}`);
41
+ }
42
+
43
+ export function loadTaskPackage(filePath) {
44
+ const sourcePath = path.resolve(filePath);
45
+ if (!existsSync(sourcePath)) fail(`任务包不存在: ${filePath}`);
46
+ const value = parseYaml(readFileSync(sourcePath, "utf8"), "任务包");
47
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail("任务包必须是对象");
48
+ return { value, sourcePath };
49
+ }
50
+
51
+ export function validateTaskPackageSchema(value, schemaPath = TASK_PACKAGE_SCHEMA) {
52
+ const effectiveSchema = path.resolve(schemaPath) === path.resolve(LEGACY_TASK_PACKAGE_SCHEMA) ? TASK_PACKAGE_SCHEMA : schemaPath;
53
+ if (!existsSync(effectiveSchema)) fail(`缺少任务包 schema: ${effectiveSchema}`);
54
+ const validator = String.raw`
55
+ import json
56
+ import sys
57
+ from jsonschema import Draft202012Validator
58
+ schema = json.load(open(sys.argv[1], encoding="utf-8"))
59
+ value = json.load(sys.stdin)
60
+ errors = sorted(Draft202012Validator(schema).iter_errors(value), key=lambda error: list(error.absolute_path))
61
+ for error in errors:
62
+ location = ".".join(str(part) for part in error.absolute_path) or "<root>"
63
+ print(f"{location}: {error.message}", file=sys.stderr)
64
+ sys.exit(1 if errors else 0)
65
+ `;
66
+ const result = spawnSync("python3", ["-c", validator, effectiveSchema], { cwd: ROOT, encoding: "utf8", input: JSON.stringify(value) });
67
+ if (result.status !== 0) fail(`${result.stdout}${result.stderr}`.trim());
68
+ }
69
+
70
+ function validateSkillSource(value, registry) {
71
+ const defaults = taskPackageDefaults(value.role_id, registry);
72
+ if (value.skill_source.registry_ref !== TASK_PACKAGE_REGISTRY_REF) fail(`skill_source.registry_ref 必须为 ${TASK_PACKAGE_REGISTRY_REF}`);
73
+ if (value.skill_source.defaults_ref !== `taskPackageDefaults(${value.role_id})`) fail(`skill_source.defaults_ref 必须为 taskPackageDefaults(${value.role_id})`);
74
+ if (!equalArrays(value.skill_source.core_skills, defaults.core_skills)) fail("skill_source.core_skills 必须与角色注册表完全一致");
75
+ if (!equalArrays(value.skill_source.forbidden_skills, defaults.forbidden_skills)) fail("skill_source.forbidden_skills 必须与角色注册表完全一致");
76
+ }
77
+
78
+ function validateCommon(value, registry, lifecycle) {
79
+ const workUnit = lifecycle.work_units.find((item) => item.id === value.work_unit_id);
80
+ if (!workUnit && value.contract.kind !== "slice-implementation") fail(`未知 work_unit_id: ${value.work_unit_id}`);
81
+ const roleDefaults = taskPackageDefaults(value.role_id, registry);
82
+ const runtimeIds = new Set((registry.runtimes || []).map((runtime) => runtime.id));
83
+ if (!runtimeIds.has(value.runtime_id)) fail(`未知 runtime_id: ${value.runtime_id}`);
84
+ if (!EXECUTION_STATES.has(value.execution_state)) fail(`execution_state 无效: ${value.execution_state}`);
85
+ if (!WORKFLOW_STATUSES.has(value.workflow_status)) fail(`workflow_status 无效: ${value.workflow_status}`);
86
+ if (value.stage_id) {
87
+ if (!lifecycle.stages.some((stage) => stage.id === value.stage_id)) fail(`未知 stage_id: ${value.stage_id}`);
88
+ if (!roleDefaults.stages.includes(value.stage_id)) fail(`role_id 未覆盖 stage_id: ${value.role_id} -> ${value.stage_id}`);
89
+ }
90
+ validateSkillSource(value, registry);
91
+ for (const allowed of value.allowed_write_paths) assertSafeRelativePath(allowed, "allowed_write_paths 条目");
92
+ if (value.execution_state === "Reviewer" && value.review_context.implementation_actor_id === value.actor_id) fail("Reviewer 必须与实现者使用不同 actor_id");
93
+ const commands = new Set(value.verification_commands);
94
+ for (const result of value.verification_results) {
95
+ if (!commands.has(result.command)) fail(`verification_results 命令未声明: ${result.command}`);
96
+ assertReadableEvidenceRef(result.evidence_ref, "verification_results.evidence_ref");
97
+ }
98
+ const completed = value.workflow_status === "resolved" || value.result?.result === "completed";
99
+ if (completed) {
100
+ if (!value.result || value.result.result !== "completed") fail("resolved/完成任务必须返回 result=completed 的 Workflow Execution Result");
101
+ if (value.workflow_status !== "resolved") fail("result=completed 的任务包必须处于 resolved 状态");
102
+ for (const field of ["result_schema", "work_unit", "workflow_reference", "skill", "changed_files", "evidence_refs", "deferred_seams", "drift", "violation", "new_impacts", "stale_candidates", "next_route", "blocking_signals"]) {
103
+ if (value.result[field] === undefined) fail(`result=completed 时 ${field} 不能为空`);
104
+ }
105
+ if (value.result.result_schema !== "workflow-execution-result-v1") fail("result_schema 必须为 workflow-execution-result-v1");
106
+ if (value.result.work_unit !== value.work_unit_id) fail("Workflow Execution Result.work_unit 必须与任务包 work_unit_id 一致");
107
+ const routeResult = validateNextRoute(value.result.work_unit, value.result.next_route);
108
+ if (routeResult.result !== "allowed") fail(`Workflow Execution Result next_route 非法: ${routeResult.blocking_signals.join(", ")}`);
109
+ value.expected_evidence_files.forEach((ref) => assertReadableEvidenceRef(ref, "expected_evidence_files"));
110
+ if (value.verification_results.length === 0) fail("已完成任务必须包含 verification_results");
111
+ if (value.verification_results.some((result) => result.exit_code !== 0)) fail("已完成任务的验证命令必须全部成功");
112
+ const resultCommands = new Set(value.verification_results.map((result) => result.command));
113
+ if (value.verification_commands.some((command) => !resultCommands.has(command))) fail("已完成任务必须覆盖全部 verification_commands");
114
+ }
115
+ if (value.result) {
116
+ for (const field of ["new_impacts", "stale_candidates", "blocking_signals", "drift", "violation"]) {
117
+ if (value.result.result === "completed" && Array.isArray(value.result[field]) && value.result[field].length > 0) fail(`result=completed 时 ${field} 必须为空`);
118
+ }
119
+ if (value.result.result === "completed") {
120
+ if (!Array.isArray(value.result.evidence_refs) || value.result.evidence_refs.length === 0) fail("result=completed 时 evidence_refs 不能为空");
121
+ value.result.evidence_refs.forEach((ref) => assertReadableEvidenceRef(ref, "result.evidence_refs"));
122
+ }
123
+ if (Array.isArray(value.result.changed_files)) {
124
+ for (const changed of value.result.changed_files) {
125
+ const changedPath = assertSafeRelativePath(changed, "changed_files 条目");
126
+ if (!value.allowed_write_paths.some((allowed) => {
127
+ const allowedPath = assertSafeRelativePath(allowed, "allowed_write_paths 条目");
128
+ const relative = path.relative(allowedPath, changedPath);
129
+ return relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`));
130
+ })) fail(`changed_files 超出 allowed_write_paths: ${changed}`);
131
+ }
132
+ }
133
+ }
134
+ return workUnit;
135
+ }
136
+
137
+ function validateContract(value, registry, lifecycle) {
138
+ const contract = value.contract;
139
+ const implementationWorkUnit = "work-unit.slice-implementation";
140
+ const workUnit = lifecycle.work_units.find((item) => item.id === value.work_unit_id);
141
+ if (!CONTRACT_KINDS.has(contract.kind)) fail(`未知 contract.kind: ${contract.kind}`);
142
+ if (!new Set(["issued", "stale", "blocked"]).has(contract.status)) fail(`contract.status 无效: ${contract.status}`);
143
+ if (contract.status === "stale" && value.workflow_status !== "paused") fail("stale 任务包必须暂停,待主控重新路由");
144
+ assertReadableEvidenceRef(contract.contract_ref, "contract.contract_ref");
145
+ if (contract.kind === "lifecycle-work-unit") {
146
+ if (value.work_unit_id === implementationWorkUnit || value.convergence.parent_work_unit === implementationWorkUnit) {
147
+ fail("lifecycle-work-unit 不得冒充垂直切片实现任务;实现任务必须使用 slice-implementation");
148
+ }
149
+ if (workUnit?.scope === "template-source") fail("template-source 工作单元必须使用 template-maintenance");
150
+ if (contract.slice_contract_ref || contract.maintenance_ref) fail("lifecycle-work-unit 不得携带专项合同引用");
151
+ assertReadableEvidenceRef(contract.lifecycle_ref, "contract.lifecycle_ref");
152
+ return;
153
+ }
154
+ if (contract.kind === "template-maintenance") {
155
+ if (contract.slice_contract_ref || contract.lifecycle_ref) fail("template-maintenance 不得携带其他合同引用");
156
+ const checkpointPath = assertSafeRelativePath(contract.maintenance_ref, "contract.maintenance_ref");
157
+ if (!existsSync(checkpointPath)) fail(`维护 checkpoint 不存在: ${contract.maintenance_ref}`);
158
+ validateMaintenanceCheckpoint(loadMaintenanceCheckpoint(contract.maintenance_ref));
159
+ if (lifecycle.work_units.find((item) => item.id === value.work_unit_id)?.scope !== "template-source") fail("template-maintenance 必须绑定 template-source work unit");
160
+ return;
161
+ }
162
+ if (contract.lifecycle_ref || contract.maintenance_ref) fail("slice-implementation 不得携带其他合同引用");
163
+ if (!/^stage\.vertical-slice-implementation$/.test(value.stage_id)) fail("slice-implementation 必须绑定垂直切片实现阶段");
164
+ if (value.convergence.parent_work_unit !== implementationWorkUnit) fail("slice-implementation 必须汇合到 work-unit.slice-implementation");
165
+ if (/^https?:\/\//.test(contract.slice_contract_ref)) fail("slice_contract_ref 必须引用本地已持久化的 Slice Implementation Contract");
166
+ const contractPath = assertSafeRelativePath(contract.slice_contract_ref, "contract.slice_contract_ref");
167
+ if (!existsSync(contractPath)) fail(`Slice Implementation Contract 不存在: ${contract.slice_contract_ref}`);
168
+ const document = parseYaml(readFileSync(contractPath, "utf8"), "Slice Implementation Contract");
169
+ const slice = document.slice_contract || document;
170
+ if (slice.schema_version !== 1 || typeof slice.slice_id !== "string" || !slice.lifecycle_refs || !slice.readiness || !Array.isArray(slice.work_units)) fail("Slice Implementation Contract 缺少 schema_version/slice_id/lifecycle_refs/readiness/work_units");
171
+ if (slice.contract_id !== contract.contract_id || slice.contract_version !== contract.contract_version || slice.status !== "approved") fail("Slice Contract 必须是当前 approved 版本");
172
+ const sliceUnit = slice.work_units.find((item) => item && item.id === value.work_unit_id);
173
+ if (!sliceUnit) fail(`Slice Implementation Contract 缺少 work_unit_id: ${value.work_unit_id}`);
174
+ for (const field of ["role_id", "runtime_id", "task_package_ref", "contract_id", "contract_version", "allowed_write_paths"]) if (sliceUnit[field] === undefined) fail(`Slice Contract work_unit 缺少 ${field}`);
175
+ if (sliceUnit.role_id !== value.role_id || sliceUnit.runtime_id !== value.runtime_id || sliceUnit.contract_id !== contract.contract_id || sliceUnit.contract_version !== contract.contract_version || sliceUnit.task_package_ref !== value.work_unit_id) fail("任务包与 Slice Contract work_unit 不一致");
176
+ for (const allowed of value.allowed_write_paths) if (!sliceUnit.allowed_write_paths.includes(allowed)) fail(`allowed_write_paths 未获 Slice Contract 授权: ${allowed}`);
177
+ }
178
+
179
+ export function validateTaskPackage(value, { rolesDoc, lifecycleDoc } = {}) {
180
+ validateTaskPackageSchema(value);
181
+ const registry = rolesDoc || loadDigitalHumanRoles();
182
+ const lifecycle = lifecycleDoc || loadRegistry();
183
+ validateCommon(value, registry, lifecycle);
184
+ validateContract(value, registry, lifecycle);
185
+ return value;
186
+ }
187
+
188
+ export function generateTaskPackageDefaults(roleId, overrides = {}, { rolesDoc } = {}) {
189
+ const defaults = taskPackageDefaults(roleId, rolesDoc || loadDigitalHumanRoles());
190
+ return {
191
+ schema_version: 1,
192
+ role_id: defaults.role_id,
193
+ skill_source: {
194
+ registry_ref: TASK_PACKAGE_REGISTRY_REF,
195
+ defaults_ref: `taskPackageDefaults(${roleId})`,
196
+ core_skills: defaults.core_skills,
197
+ forbidden_skills: defaults.forbidden_skills
198
+ },
199
+ ...overrides
200
+ };
201
+ }
202
+
203
+ export function validateTaskPackageSet(packages, options = {}) {
204
+ if (!Array.isArray(packages) || packages.length === 0) fail("任务包集合不能为空");
205
+ packages.forEach((pkg) => validateTaskPackage(pkg, options));
206
+ const slicePackages = packages.filter((pkg) => pkg.contract.kind === "slice-implementation");
207
+ if (slicePackages.length > 0) {
208
+ const contracts = new Set(slicePackages.map((pkg) => `${pkg.contract.contract_id}@${pkg.contract.contract_version}`));
209
+ if (contracts.size !== 1) fail("同一切片的任务包必须消费同一 contract_id/contract_version");
210
+ const workers = new Set(slicePackages.filter((pkg) => pkg.execution_state === "Worker").map((pkg) => pkg.actor_id));
211
+ slicePackages.filter((pkg) => pkg.execution_state === "Reviewer").forEach((pkg) => { if (workers.has(pkg.actor_id)) fail("Reviewer 不得与实现者使用同一 actor_id"); });
212
+ }
213
+ return packages;
214
+ }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from "node:util";
3
+ import { loadTaskPackage, validateTaskPackage } from "./lib/task-package.mjs";
4
+
5
+ try {
6
+ const { positionals } = parseArgs({ allowPositionals: true, strict: true });
7
+ if (positionals.length !== 1) throw new TypeError("用法: scripts/verify-digital-human-task-package <task-package.yaml|task-package.json>");
8
+ const { value } = loadTaskPackage(positionals[0]);
9
+ validateTaskPackage(value);
10
+ process.stdout.write("数字人任务包验证通过\n");
11
+ } catch (error) {
12
+ process.stderr.write(`数字人任务包验证失败: ${error.message}\n`);
13
+ process.exitCode = 1;
14
+ }
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
7
+ const result = spawnSync(process.execPath, [path.join(root, "scripts/verify-subagent-task-package-scenarios"), ...process.argv.slice(2)], { stdio: "inherit" });
8
+ process.exitCode = result.status ?? 1;