create-yss-spec 3.2.0 → 3.3.1

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 (439) hide show
  1. package/README.md +2 -2
  2. package/package.json +3 -2
  3. package/src/command-runner.mjs +107 -0
  4. package/src/self-update.js +7 -4
  5. package/src/template/instance-runtime.js +1 -1
  6. package/template/.agents/skills/.strategic-design-skills-manifest.json +2 -2
  7. package/template/.agents/skills/code-review/SKILL.md +4 -0
  8. package/template/.agents/skills/code-review/agents/openai.yaml +1 -1
  9. package/template/.agents/skills/code-review/references/yss-review-standards.md +9 -2
  10. package/template/.agents/skills/competitive-intelligence/SKILL.md +4 -4
  11. package/template/.agents/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  12. package/template/.agents/skills/grill-with-docs/SKILL.md +1 -1
  13. package/template/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  14. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  15. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +7 -3
  16. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +8 -8
  17. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +9 -0
  18. package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +10 -7
  19. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +10 -0
  20. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +121 -0
  21. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +40 -0
  22. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +55 -0
  23. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +1 -1
  24. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +4 -0
  25. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +25 -0
  26. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  27. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +8 -5
  28. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +17 -2
  29. package/template/.agents/skills/yss-mybatis/SKILL.md +34 -58
  30. package/template/.agents/skills/yss-mybatis/agents/openai.yaml +2 -2
  31. package/template/.agents/skills/yss-mybatis/references/source-index.md +88 -68
  32. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +50 -42
  33. package/template/.agents/skills/yss-product-lifecycle/references/artifact-dependencies.md +5 -3
  34. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  35. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +124 -16
  36. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +16 -2
  37. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +5 -2
  38. package/template/.agents/skills/yss-repository/SKILL.md +28 -70
  39. package/template/.agents/skills/yss-repository/agents/openai.yaml +2 -2
  40. package/template/.agents/skills/yss-repository/references/profiles/layered-mvc-service.md +19 -1
  41. package/template/.agents/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +19 -1
  42. package/template/.agents/skills/yss-repository/references/profiles/target-domain-model.md +21 -1
  43. package/template/.agents/skills/yss-repository/tests/profile-routing.test.mjs +59 -0
  44. package/template/.agents/skills/yss-research/SKILL.md +1 -1
  45. package/template/.agents/skills/yss-skill-source-index-refresh/SKILL.md +8 -5
  46. package/template/.agents/skills/yss-skill-source-index-refresh/references/source-location.md +2 -1
  47. package/template/.agents/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +388 -18
  48. package/template/.agents/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.test.mjs +79 -1
  49. package/template/.agents/skills/yss-stage-decision/SKILL.md +5 -3
  50. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  51. package/template/.claude/skills/code-review/SKILL.md +4 -0
  52. package/template/.claude/skills/code-review/agents/openai.yaml +1 -1
  53. package/template/.claude/skills/code-review/references/yss-review-standards.md +9 -2
  54. package/template/.claude/skills/competitive-intelligence/SKILL.md +4 -4
  55. package/template/.claude/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  56. package/template/.claude/skills/grill-with-docs/SKILL.md +1 -1
  57. package/template/.claude/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  58. package/template/.claude/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  59. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +7 -3
  60. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +8 -8
  61. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +9 -0
  62. package/template/.claude/skills/yss-frontend-scaffold-generator/SKILL.md +10 -7
  63. package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +10 -0
  64. package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +121 -0
  65. package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +40 -0
  66. package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +55 -0
  67. package/template/.claude/skills/yss-implementation-contract-compiler/SKILL.md +1 -1
  68. package/template/.claude/skills/yss-implementation-contract-compiler/references/boundaries.md +4 -0
  69. package/template/.claude/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +25 -0
  70. package/template/.claude/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  71. package/template/.claude/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +8 -5
  72. package/template/.claude/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +17 -2
  73. package/template/.claude/skills/yss-mybatis/SKILL.md +34 -58
  74. package/template/.claude/skills/yss-mybatis/agents/openai.yaml +2 -2
  75. package/template/.claude/skills/yss-mybatis/references/source-index.md +88 -68
  76. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +50 -42
  77. package/template/.claude/skills/yss-product-lifecycle/references/artifact-dependencies.md +5 -3
  78. package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  79. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +124 -16
  80. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +16 -2
  81. package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +5 -2
  82. package/template/.claude/skills/yss-repository/SKILL.md +28 -70
  83. package/template/.claude/skills/yss-repository/agents/openai.yaml +2 -2
  84. package/template/.claude/skills/yss-repository/references/profiles/layered-mvc-service.md +19 -1
  85. package/template/.claude/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +19 -1
  86. package/template/.claude/skills/yss-repository/references/profiles/target-domain-model.md +21 -1
  87. package/template/.claude/skills/yss-repository/tests/profile-routing.test.mjs +59 -0
  88. package/template/.claude/skills/yss-research/SKILL.md +1 -1
  89. package/template/.claude/skills/yss-skill-source-index-refresh/SKILL.md +8 -5
  90. package/template/.claude/skills/yss-skill-source-index-refresh/references/source-location.md +2 -1
  91. package/template/.claude/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +388 -18
  92. package/template/.claude/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.test.mjs +79 -1
  93. package/template/.claude/skills/yss-stage-decision/SKILL.md +5 -3
  94. package/template/.claude/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  95. package/template/.codex/skills/code-review/SKILL.md +4 -0
  96. package/template/.codex/skills/code-review/agents/openai.yaml +1 -1
  97. package/template/.codex/skills/code-review/references/yss-review-standards.md +9 -2
  98. package/template/.codex/skills/competitive-intelligence/SKILL.md +4 -4
  99. package/template/.codex/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  100. package/template/.codex/skills/grill-with-docs/SKILL.md +1 -1
  101. package/template/.codex/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  102. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  103. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +7 -3
  104. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +8 -8
  105. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +9 -0
  106. package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +10 -7
  107. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +10 -0
  108. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +121 -0
  109. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +40 -0
  110. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +55 -0
  111. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +1 -1
  112. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +4 -0
  113. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +25 -0
  114. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  115. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +8 -5
  116. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +17 -2
  117. package/template/.codex/skills/yss-mybatis/SKILL.md +34 -58
  118. package/template/.codex/skills/yss-mybatis/agents/openai.yaml +2 -2
  119. package/template/.codex/skills/yss-mybatis/references/source-index.md +88 -68
  120. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +50 -42
  121. package/template/.codex/skills/yss-product-lifecycle/references/artifact-dependencies.md +5 -3
  122. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  123. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +124 -16
  124. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +16 -2
  125. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +5 -2
  126. package/template/.codex/skills/yss-repository/SKILL.md +28 -70
  127. package/template/.codex/skills/yss-repository/agents/openai.yaml +2 -2
  128. package/template/.codex/skills/yss-repository/references/profiles/layered-mvc-service.md +19 -1
  129. package/template/.codex/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +19 -1
  130. package/template/.codex/skills/yss-repository/references/profiles/target-domain-model.md +21 -1
  131. package/template/.codex/skills/yss-repository/tests/profile-routing.test.mjs +59 -0
  132. package/template/.codex/skills/yss-research/SKILL.md +1 -1
  133. package/template/.codex/skills/yss-skill-source-index-refresh/SKILL.md +8 -5
  134. package/template/.codex/skills/yss-skill-source-index-refresh/references/source-location.md +2 -1
  135. package/template/.codex/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +388 -18
  136. package/template/.codex/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.test.mjs +79 -1
  137. package/template/.codex/skills/yss-stage-decision/SKILL.md +5 -3
  138. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  139. package/template/.cursor/skills/code-review/SKILL.md +4 -0
  140. package/template/.cursor/skills/code-review/agents/openai.yaml +1 -1
  141. package/template/.cursor/skills/code-review/references/yss-review-standards.md +9 -2
  142. package/template/.cursor/skills/competitive-intelligence/SKILL.md +4 -4
  143. package/template/.cursor/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  144. package/template/.cursor/skills/grill-with-docs/SKILL.md +1 -1
  145. package/template/.cursor/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  146. package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  147. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +7 -3
  148. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +8 -8
  149. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +9 -0
  150. package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +10 -7
  151. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +10 -0
  152. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +121 -0
  153. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +40 -0
  154. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +55 -0
  155. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +1 -1
  156. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +4 -0
  157. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +25 -0
  158. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  159. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +8 -5
  160. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +17 -2
  161. package/template/.cursor/skills/yss-mybatis/SKILL.md +34 -58
  162. package/template/.cursor/skills/yss-mybatis/agents/openai.yaml +2 -2
  163. package/template/.cursor/skills/yss-mybatis/references/source-index.md +88 -68
  164. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +50 -42
  165. package/template/.cursor/skills/yss-product-lifecycle/references/artifact-dependencies.md +5 -3
  166. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  167. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +124 -16
  168. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +16 -2
  169. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +5 -2
  170. package/template/.cursor/skills/yss-repository/SKILL.md +28 -70
  171. package/template/.cursor/skills/yss-repository/agents/openai.yaml +2 -2
  172. package/template/.cursor/skills/yss-repository/references/profiles/layered-mvc-service.md +19 -1
  173. package/template/.cursor/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +19 -1
  174. package/template/.cursor/skills/yss-repository/references/profiles/target-domain-model.md +21 -1
  175. package/template/.cursor/skills/yss-repository/tests/profile-routing.test.mjs +59 -0
  176. package/template/.cursor/skills/yss-research/SKILL.md +1 -1
  177. package/template/.cursor/skills/yss-skill-source-index-refresh/SKILL.md +8 -5
  178. package/template/.cursor/skills/yss-skill-source-index-refresh/references/source-location.md +2 -1
  179. package/template/.cursor/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +388 -18
  180. package/template/.cursor/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.test.mjs +79 -1
  181. package/template/.cursor/skills/yss-stage-decision/SKILL.md +5 -3
  182. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  183. package/template/.pi/skills/code-review/SKILL.md +4 -0
  184. package/template/.pi/skills/code-review/agents/openai.yaml +1 -1
  185. package/template/.pi/skills/code-review/references/yss-review-standards.md +9 -2
  186. package/template/.pi/skills/competitive-intelligence/SKILL.md +4 -4
  187. package/template/.pi/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  188. package/template/.pi/skills/grill-with-docs/SKILL.md +1 -1
  189. package/template/.pi/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  190. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  191. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +7 -3
  192. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +8 -8
  193. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +9 -0
  194. package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +10 -7
  195. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +10 -0
  196. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +121 -0
  197. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +40 -0
  198. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +55 -0
  199. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +1 -1
  200. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +4 -0
  201. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +25 -0
  202. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  203. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +8 -5
  204. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +17 -2
  205. package/template/.pi/skills/yss-mybatis/SKILL.md +34 -58
  206. package/template/.pi/skills/yss-mybatis/agents/openai.yaml +2 -2
  207. package/template/.pi/skills/yss-mybatis/references/source-index.md +88 -68
  208. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +50 -42
  209. package/template/.pi/skills/yss-product-lifecycle/references/artifact-dependencies.md +5 -3
  210. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  211. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +124 -16
  212. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +16 -2
  213. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +5 -2
  214. package/template/.pi/skills/yss-repository/SKILL.md +28 -70
  215. package/template/.pi/skills/yss-repository/agents/openai.yaml +2 -2
  216. package/template/.pi/skills/yss-repository/references/profiles/layered-mvc-service.md +19 -1
  217. package/template/.pi/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +19 -1
  218. package/template/.pi/skills/yss-repository/references/profiles/target-domain-model.md +21 -1
  219. package/template/.pi/skills/yss-repository/tests/profile-routing.test.mjs +59 -0
  220. package/template/.pi/skills/yss-research/SKILL.md +1 -1
  221. package/template/.pi/skills/yss-skill-source-index-refresh/SKILL.md +8 -5
  222. package/template/.pi/skills/yss-skill-source-index-refresh/references/source-location.md +2 -1
  223. package/template/.pi/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +388 -18
  224. package/template/.pi/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.test.mjs +79 -1
  225. package/template/.pi/skills/yss-stage-decision/SKILL.md +5 -3
  226. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  227. package/template/.qoder/skills/code-review/SKILL.md +4 -0
  228. package/template/.qoder/skills/code-review/agents/openai.yaml +1 -1
  229. package/template/.qoder/skills/code-review/references/yss-review-standards.md +9 -2
  230. package/template/.qoder/skills/competitive-intelligence/SKILL.md +4 -4
  231. package/template/.qoder/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  232. package/template/.qoder/skills/grill-with-docs/SKILL.md +1 -1
  233. package/template/.qoder/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  234. package/template/.qoder/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  235. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +7 -3
  236. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +8 -8
  237. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +9 -0
  238. package/template/.qoder/skills/yss-frontend-scaffold-generator/SKILL.md +10 -7
  239. package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +10 -0
  240. package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +121 -0
  241. package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +40 -0
  242. package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +55 -0
  243. package/template/.qoder/skills/yss-implementation-contract-compiler/SKILL.md +1 -1
  244. package/template/.qoder/skills/yss-implementation-contract-compiler/references/boundaries.md +4 -0
  245. package/template/.qoder/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +25 -0
  246. package/template/.qoder/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  247. package/template/.qoder/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +8 -5
  248. package/template/.qoder/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +17 -2
  249. package/template/.qoder/skills/yss-mybatis/SKILL.md +34 -58
  250. package/template/.qoder/skills/yss-mybatis/agents/openai.yaml +2 -2
  251. package/template/.qoder/skills/yss-mybatis/references/source-index.md +88 -68
  252. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +50 -42
  253. package/template/.qoder/skills/yss-product-lifecycle/references/artifact-dependencies.md +5 -3
  254. package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  255. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +124 -16
  256. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +16 -2
  257. package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +5 -2
  258. package/template/.qoder/skills/yss-repository/SKILL.md +28 -70
  259. package/template/.qoder/skills/yss-repository/agents/openai.yaml +2 -2
  260. package/template/.qoder/skills/yss-repository/references/profiles/layered-mvc-service.md +19 -1
  261. package/template/.qoder/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +19 -1
  262. package/template/.qoder/skills/yss-repository/references/profiles/target-domain-model.md +21 -1
  263. package/template/.qoder/skills/yss-repository/tests/profile-routing.test.mjs +59 -0
  264. package/template/.qoder/skills/yss-research/SKILL.md +1 -1
  265. package/template/.qoder/skills/yss-skill-source-index-refresh/SKILL.md +8 -5
  266. package/template/.qoder/skills/yss-skill-source-index-refresh/references/source-location.md +2 -1
  267. package/template/.qoder/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +388 -18
  268. package/template/.qoder/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.test.mjs +79 -1
  269. package/template/.qoder/skills/yss-stage-decision/SKILL.md +5 -3
  270. package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  271. package/template/.trae/skills/code-review/SKILL.md +4 -0
  272. package/template/.trae/skills/code-review/agents/openai.yaml +1 -1
  273. package/template/.trae/skills/code-review/references/yss-review-standards.md +9 -2
  274. package/template/.trae/skills/competitive-intelligence/SKILL.md +4 -4
  275. package/template/.trae/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  276. package/template/.trae/skills/grill-with-docs/SKILL.md +1 -1
  277. package/template/.trae/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  278. package/template/.trae/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  279. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +7 -3
  280. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +8 -8
  281. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +9 -0
  282. package/template/.trae/skills/yss-frontend-scaffold-generator/SKILL.md +10 -7
  283. package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +10 -0
  284. package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +121 -0
  285. package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +40 -0
  286. package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +55 -0
  287. package/template/.trae/skills/yss-implementation-contract-compiler/SKILL.md +1 -1
  288. package/template/.trae/skills/yss-implementation-contract-compiler/references/boundaries.md +4 -0
  289. package/template/.trae/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +25 -0
  290. package/template/.trae/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  291. package/template/.trae/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +8 -5
  292. package/template/.trae/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +17 -2
  293. package/template/.trae/skills/yss-mybatis/SKILL.md +34 -58
  294. package/template/.trae/skills/yss-mybatis/agents/openai.yaml +2 -2
  295. package/template/.trae/skills/yss-mybatis/references/source-index.md +88 -68
  296. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +50 -42
  297. package/template/.trae/skills/yss-product-lifecycle/references/artifact-dependencies.md +5 -3
  298. package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  299. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +124 -16
  300. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +16 -2
  301. package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +5 -2
  302. package/template/.trae/skills/yss-repository/SKILL.md +28 -70
  303. package/template/.trae/skills/yss-repository/agents/openai.yaml +2 -2
  304. package/template/.trae/skills/yss-repository/references/profiles/layered-mvc-service.md +19 -1
  305. package/template/.trae/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +19 -1
  306. package/template/.trae/skills/yss-repository/references/profiles/target-domain-model.md +21 -1
  307. package/template/.trae/skills/yss-repository/tests/profile-routing.test.mjs +59 -0
  308. package/template/.trae/skills/yss-research/SKILL.md +1 -1
  309. package/template/.trae/skills/yss-skill-source-index-refresh/SKILL.md +8 -5
  310. package/template/.trae/skills/yss-skill-source-index-refresh/references/source-location.md +2 -1
  311. package/template/.trae/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +388 -18
  312. package/template/.trae/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.test.mjs +79 -1
  313. package/template/.trae/skills/yss-stage-decision/SKILL.md +5 -3
  314. package/template/.trae/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  315. package/template/AGENTS.md +2 -2
  316. package/template/CONTEXT.md +3 -2
  317. package/template/README.md +3 -3
  318. package/template/docs/agents/digital-human-roles.md +1 -1
  319. package/template/docs/agents/digital-human-roles.yaml +13 -11
  320. package/template/docs/agents/issue-tracker.md +1 -1
  321. package/template/docs/agents/strategic-design-skills-integration.md +2 -0
  322. package/template/docs/agents/yss-skill-registry.yaml +5 -0
  323. package/template/docs/architecture/README.md +1 -1
  324. package/template/docs/architecture/templates/business-architecture-template.md +1 -1
  325. package/template/docs/architecture/templates/functional-architecture-template.md +1 -1
  326. package/template/docs/design/README.md +1 -1
  327. package/template/docs/design/templates/interaction-spec-template.md +1 -1
  328. package/template/docs/design/templates/product-overview-design-template.md +1 -1
  329. package/template/docs/{discovery → plan}/IDEATION.md +5 -5
  330. package/template/docs/plan/README.md +9 -0
  331. package/template/docs/plan/entry-review.md +23 -0
  332. package/template/docs/plan/templates/plan-template.md +58 -0
  333. package/template/docs/process/harness-process-tailoring.md +1 -1
  334. package/template/docs/process/lifecycle-artifact-map.md +10 -6
  335. package/template/docs/process/lifecycle-registry-baseline.json +10 -1
  336. package/template/docs/process/lifecycle-registry.yaml +48 -20
  337. package/template/docs/process/plan-migration.md +13 -0
  338. package/template/docs/process/schemas/digital-human-task-package.schema.json +2 -0
  339. package/template/docs/process/schemas/implementation-repository-preparation-result.schema.json +38 -0
  340. package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +3 -0
  341. package/template/docs/process/schemas/lifecycle-registry.schema.json +1 -1
  342. package/template/docs/process/schemas/project-scaffold-contract.schema.json +113 -0
  343. package/template/docs/process/template-engineering-overview.md +2 -2
  344. package/template/docs/process/template-verification-profiles.yaml +25 -0
  345. package/template/docs/process/templates/context-reconciliation-template.yaml +2 -2
  346. package/template/docs/process/templates/lifecycle-checkpoint-template.yaml +1 -1
  347. package/template/docs/requirements/README.md +2 -2
  348. package/template/docs/templates/implementation-repo-registry-template.md +8 -0
  349. package/template/docs/templates/local-parent-ticket-template.md +1 -1
  350. package/template/docs/templates/review-report-template.md +7 -3
  351. package/template/docs/templates/subagent-task-package-template.md +1 -1
  352. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
  353. package/template/scripts/cache-template-commit +2 -2
  354. package/template/scripts/fixtures/strategic-handoff/source-roles.json +7 -7
  355. package/template/scripts/fixtures/user-decision/plan-fixture.mjs +30 -0
  356. package/template/scripts/lib/backend-delivery.mjs +5 -2
  357. package/template/scripts/lib/command-runner.mjs +107 -0
  358. package/template/scripts/lib/context-reconciliation.mjs +34 -0
  359. package/template/scripts/lib/json-schema.mjs +42 -13
  360. package/template/scripts/lib/lifecycle-context-query.mjs +141 -0
  361. package/template/scripts/lib/lifecycle-transition.mjs +118 -6
  362. package/template/scripts/lib/plan-spec-entry.mjs +87 -0
  363. package/template/scripts/lib/scenario-checks.mjs +44 -38
  364. package/template/scripts/lib/skill-registry.mjs +9 -0
  365. package/template/scripts/lib/skill-supply-chain.mjs +18 -2
  366. package/template/scripts/lib/strategic-handoff-io.mjs +5 -4
  367. package/template/scripts/lib/strategic-handoff.mjs +10 -9
  368. package/template/scripts/lib/task-package.mjs +2 -0
  369. package/template/scripts/lib/template-commit-cache.mjs +7 -5
  370. package/template/scripts/query-lifecycle-context +25 -0
  371. package/template/scripts/run-template-verification +9 -3
  372. package/template/scripts/sync-strategic-handoff-tools +9 -0
  373. package/template/scripts/verify-business-language-scenarios +0 -7
  374. package/template/scripts/verify-context-reconciliation +2 -48
  375. package/template/scripts/verify-context-reconciliation-scenarios +1 -1
  376. package/template/scripts/verify-frontend-scaffold-generator-scenarios +2 -0
  377. package/template/scripts/verify-lifecycle-checkpoint +2 -0
  378. package/template/scripts/verify-lifecycle-context-query-scenarios +88 -0
  379. package/template/scripts/verify-lifecycle-transition-scenarios +44 -4
  380. package/template/scripts/verify-plan-spec-entry +11 -0
  381. package/template/scripts/verify-plan-spec-entry-scenarios +79 -0
  382. package/template/scripts/verify-script-performance-scenarios +56 -0
  383. package/template/scripts/verify-subagent-task-package-scenarios +9 -8
  384. package/template/scripts/verify-template-cache-scenarios +11 -1
  385. package/template/scripts/verify-user-decision-scenarios +4 -4
  386. package/template/skills-lock.json +23 -23
  387. package/template.manifest.json +2 -1
  388. package/template.snapshot.json +5 -5
  389. package/template/.agents/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  390. package/template/.agents/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  391. package/template/.agents/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  392. package/template/.agents/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  393. package/template/.agents/skills/yss-mybatis/references/README.md +0 -57
  394. package/template/.agents/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  395. package/template/.claude/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  396. package/template/.claude/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  397. package/template/.claude/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  398. package/template/.claude/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  399. package/template/.claude/skills/yss-mybatis/references/README.md +0 -57
  400. package/template/.claude/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  401. package/template/.codex/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  402. package/template/.codex/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  403. package/template/.codex/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  404. package/template/.codex/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  405. package/template/.codex/skills/yss-mybatis/references/README.md +0 -57
  406. package/template/.codex/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  407. package/template/.cursor/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  408. package/template/.cursor/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  409. package/template/.cursor/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  410. package/template/.cursor/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  411. package/template/.cursor/skills/yss-mybatis/references/README.md +0 -57
  412. package/template/.cursor/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  413. package/template/.pi/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  414. package/template/.pi/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  415. package/template/.pi/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  416. package/template/.pi/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  417. package/template/.pi/skills/yss-mybatis/references/README.md +0 -57
  418. package/template/.pi/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  419. package/template/.qoder/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  420. package/template/.qoder/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  421. package/template/.qoder/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  422. package/template/.qoder/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  423. package/template/.qoder/skills/yss-mybatis/references/README.md +0 -57
  424. package/template/.qoder/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  425. package/template/.trae/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  426. package/template/.trae/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  427. package/template/.trae/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  428. package/template/.trae/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  429. package/template/.trae/skills/yss-mybatis/references/README.md +0 -57
  430. package/template/.trae/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  431. package/template/docs/discovery/README.md +0 -87
  432. package/template/docs/discovery/reports/.gitkeep +0 -0
  433. package/template/docs/discovery/reports/agent-governance-options.md +0 -71
  434. package/template/docs/discovery/reports/competitor-deep-dive/.gitkeep +0 -0
  435. package/template/docs/discovery/templates/discovery-template.md +0 -94
  436. /package/template/docs/{discovery → plan}/templates/competitive-analysis-template.md +0 -0
  437. /package/template/docs/{discovery → plan}/templates/competitive-matrix-template.md +0 -0
  438. /package/template/docs/{discovery → plan}/templates/market-analysis-template.md +0 -0
  439. /package/template/docs/{discovery → plan}/templates/user-pain-points-template.md +0 -0
@@ -0,0 +1,107 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { appendFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { StringDecoder } from "node:string_decoder";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ function killTree(child, signal) {
7
+ if (!child.pid) return;
8
+ try {
9
+ if (process.platform === "win32") spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" });
10
+ else process.kill(-child.pid, signal);
11
+ } catch (error) { if (error.code !== "ESRCH") throw error; }
12
+ }
13
+
14
+ /** Bounded lifetime for external tools; stdout is never mixed with progress. */
15
+ export function runCommand(command, args, { cwd, env = process.env, timeoutMs = 0, signal, stdoutFile, stderrFile, secrets = [], progress = false } = {}) {
16
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) throw new TypeError("timeoutMs 必须是非负毫秒数");
17
+ if (signal?.aborted) return Promise.resolve({ status: 1, stdout: "", stderr: "command cancelled", termination: "cancelled", duration_ms: 0 });
18
+ return new Promise(resolve => {
19
+ const started = performance.now();
20
+ const argumentSecrets = args.flatMap(value => { try { const url = new URL(value); return url.password ? [url.password, decodeURIComponent(url.password)] : []; } catch { return []; } });
21
+ const secretNames = new Map(Object.entries(env).filter(([key]) => /(?:PASSWORD|TOKEN|SECRET)$/.test(key) || key === "MAVEN_REPO_USERNAME").map(([key,value]) => [value,key]));
22
+ const replacement = secret => secretNames.has(secret) ? `[REDACTED:${secretNames.get(secret)}]` : "[REDACTED]";
23
+ const sensitive = [...secrets, ...argumentSecrets, ...secretNames.keys()].filter(value => typeof value === "string" && value.length > 0).sort((a, b) => b.length - a.length);
24
+ const redact = text => sensitive.reduce((value, secret) => value.replaceAll(secret, replacement(secret)), text);
25
+ if (progress) process.stderr.write(`[开始] ${redact(command + " " + args.join(" "))}\n`);
26
+ for (const file of [stdoutFile, stderrFile]) if (file) writeFileSync(file, "");
27
+ const child = spawn(command, args, { cwd, env, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
28
+ let termination = null, escalation, timer, errorMessage;
29
+ const makeStream = file => {
30
+ const decoder = new StringDecoder("utf8");
31
+ let pending = "", captured = "";
32
+ const emit = text => { captured += text; if (file) appendFileSync(file, text); };
33
+ function consume(final = false) {
34
+ let output = "", cursor = 0;
35
+ while (cursor < pending.length) {
36
+ const match = sensitive.find(secret => pending.startsWith(secret, cursor));
37
+ if (match) { output += replacement(match); cursor += match.length; continue; }
38
+ if (!final && sensitive.some(secret => secret.startsWith(pending.slice(cursor)))) break;
39
+ output += pending[cursor++];
40
+ }
41
+ pending = pending.slice(cursor);
42
+ emit(output);
43
+ }
44
+ return { write(bytes) { pending += decoder.write(bytes); consume(); }, end() { pending += decoder.end(); consume(true); return captured; } };
45
+ };
46
+ const stdout = makeStream(stdoutFile), stderr = makeStream(stderrFile);
47
+ function cancel(reason) {
48
+ if (termination) return;
49
+ termination = reason;
50
+ killTree(child, "SIGTERM");
51
+ escalation = setTimeout(() => killTree(child, "SIGKILL"), 1000);
52
+ }
53
+ const abort = () => cancel("cancelled");
54
+ const onInterrupt = () => cancel("cancelled");
55
+ signal?.addEventListener("abort", abort, { once: true });
56
+ process.on("SIGINT", onInterrupt); process.on("SIGTERM", onInterrupt);
57
+ if (timeoutMs) timer = setTimeout(() => cancel("timeout"), timeoutMs);
58
+ child.stdout.on("data", bytes => stdout.write(bytes));
59
+ child.stderr.on("data", bytes => stderr.write(bytes));
60
+ child.on("error", error => { errorMessage = redact(error.message); });
61
+ child.on("close", (code, exitSignal) => {
62
+ clearTimeout(timer); clearTimeout(escalation);
63
+ // A group leader can exit before its descendants close redirected streams.
64
+ if (termination) killTree(child, "SIGKILL");
65
+ signal?.removeEventListener("abort", abort);
66
+ process.off("SIGINT", onInterrupt); process.off("SIGTERM", onInterrupt);
67
+ const duration_ms = performance.now() - started;
68
+ const status = termination === "timeout" ? 124 : termination ? 1 : code ?? 1;
69
+ if (progress) process.stderr.write(`[${status === 0 ? "完成" : "失败"}] ${command} (${Math.round(duration_ms)}ms, exit=${status}${termination ? `, ${termination}` : ""})\n`);
70
+ resolve({ status, stdout: stdout.end(), stderr: stderr.end() + (errorMessage || ""), signal: exitSignal, termination, duration_ms });
71
+ });
72
+ });
73
+ }
74
+
75
+ // Synchronous API compatibility for existing generators/cache consumers. The worker
76
+ // owns the process group and streams logs; it never updates project metadata.
77
+ export function runCommandSync(command, args, options = {}) {
78
+ const result = spawnSync(process.execPath, [fileURLToPath(import.meta.url), "--worker"], {
79
+ input: JSON.stringify({ command, args, options }), encoding: "utf8", maxBuffer: 64 * 1024 * 1024,
80
+ stdio: ["pipe", "pipe", "inherit"], env: options.env ?? process.env,
81
+ });
82
+ if (result.error || result.status !== 0) throw new TypeError(result.error?.message || "command worker failed");
83
+ return JSON.parse(result.stdout);
84
+ }
85
+
86
+ if (process.argv[1] === fileURLToPath(import.meta.url) && process.argv[2] === "--worker") {
87
+ const parent = process.ppid;
88
+ let input = "";
89
+ for await (const bytes of process.stdin) input += bytes;
90
+ const { command, args, options } = JSON.parse(input);
91
+ const controller = new AbortController();
92
+ const watchParent = setInterval(() => {
93
+ if (process.ppid !== parent) controller.abort();
94
+ else {
95
+ try { process.kill(parent, 0); } catch (error) { if (error.code === "ESRCH") controller.abort(); }
96
+ }
97
+ }, 100);
98
+ try {
99
+ const result = await runCommand(command, args, { ...options, signal: controller.signal });
100
+ if (result.status !== 0) {
101
+ // Only caller-created temporary resources are supplied here. The worker
102
+ // also owns this cleanup when a terminated synchronous caller cannot run finally.
103
+ for (const directory of options.cleanupPathsOnFailure || []) rmSync(directory, { recursive: true, force: true });
104
+ }
105
+ if (process.ppid === parent) process.stdout.write(JSON.stringify(result));
106
+ } finally { clearInterval(watchParent); }
107
+ }
@@ -0,0 +1,34 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { parseDocument } from "../vendor/yaml.mjs";
4
+ import { resolveContextTermRefs, verifyContextSnapshot } from "./context-contract.mjs";
5
+ import { validateJsonSchema } from "./json-schema.mjs";
6
+ const schemaPath = path.resolve(import.meta.dirname, "../../docs/process/schemas/context-reconciliation.schema.json");
7
+ export function parseReconciliationFile(file) {
8
+ const document = parseDocument(readFileSync(file, "utf8"), { maxAliasCount: 0, uniqueKeys: true });
9
+ if (document.errors.length) throw new TypeError(document.errors[0].message);
10
+ return document.toJS({ maxAliasCount: 0 });
11
+ }
12
+
13
+ export function verifyContextReconciliation(file, { root = process.cwd() } = {}) {
14
+ root = path.resolve(root);
15
+ const value = parseReconciliationFile(path.resolve(file));
16
+ validateJsonSchema(value, schemaPath, { formatChecker: false });
17
+ const manifest = parseReconciliationFile(path.join(root, "yss-project.yaml"));
18
+ if (value.repository_mode !== manifest.repository_mode) throw new TypeError("repository_mode 必须与根目录 yss-project.yaml 一致");
19
+ const contract = verifyContextSnapshot(value.context_snapshot, { root });
20
+ const changedRefs = [...value.changes.added, ...value.changes.updated, ...value.changes.deprecated];
21
+ resolveContextTermRefs(contract, changedRefs);
22
+ if (new Set(changedRefs).size !== changedRefs.length) throw new TypeError("changes 中的术语引用不得跨分类重复");
23
+ if (value.repository_mode === "project-instance" && value.status !== "reconciled") throw new TypeError("project-instance 工作单元只有 status=reconciled 才允许批准或进入下一步");
24
+ if (value.status === "reconciled" && value.unresolved_terms.length) throw new TypeError("reconciled 不得包含 unresolved_terms");
25
+ if (value.status === "blocked" && value.unresolved_terms.length === 0) throw new TypeError("blocked 必须说明 unresolved_terms");
26
+ if (value.status === "not-applicable" && (value.repository_mode !== "template-source" || !value.reason)) throw new TypeError("not-applicable 只适用于 template-source 且必须说明 reason");
27
+ for (const ref of value.evidence_refs) {
28
+ if (/^https?:\/\//.test(ref)) continue;
29
+ const resolved = path.resolve(root, ref);
30
+ if (!resolved.startsWith(`${root}${path.sep}`) && resolved !== root) throw new TypeError(`evidence_ref 越出仓库: ${ref}`);
31
+ if (!existsSync(resolved)) throw new TypeError(`evidence_ref 不可读: ${ref}`);
32
+ }
33
+ return contract;
34
+ }
@@ -2,21 +2,50 @@ import { existsSync } from "node:fs";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import path from "node:path";
4
4
 
5
- export function validateJsonSchema(value, schemaPath, { cwd = process.cwd(), label = "JSON Schema" } = {}) {
6
- const effectiveSchema = path.resolve(schemaPath);
7
- if (!existsSync(effectiveSchema)) throw new TypeError(`缺少 ${label}: ${effectiveSchema}`);
8
- const validator = String.raw`
5
+ const validator = String.raw`
9
6
  import json
10
7
  import sys
8
+ import traceback
11
9
  from jsonschema import Draft202012Validator, FormatChecker
12
- schema = json.load(open(sys.argv[1], encoding="utf-8"))
13
- value = json.load(sys.stdin)
14
- errors = sorted(Draft202012Validator(schema, format_checker=FormatChecker()).iter_errors(value), key=lambda error: list(error.absolute_path))
15
- for error in errors:
16
- location = ".".join(str(part) for part in error.absolute_path) or "<root>"
17
- print(f"{location}: {error.message}", file=sys.stderr)
18
- sys.exit(1 if errors else 0)
10
+ validators = {}
11
+ results = []
12
+ for item in json.load(sys.stdin):
13
+ try:
14
+ key = (item['schemaPath'], item['formatChecker'])
15
+ if key not in validators:
16
+ with open(item['schemaPath'], encoding='utf-8') as source:
17
+ schema = json.load(source)
18
+ validators[key] = Draft202012Validator(schema, format_checker=FormatChecker() if key[1] else None)
19
+ errors = list(validators[key].iter_errors(item['value']))
20
+ if item['errorStyle'] == 'verbose':
21
+ message = '\n'.join(str(error) for error in errors)
22
+ else:
23
+ errors.sort(key=lambda error: list(error.absolute_path))
24
+ message = '\n'.join(f"{'.'.join(str(part) for part in error.absolute_path) or '<root>'}: {error.message}" for error in errors)
25
+ results.append({'valid': not errors, 'error': message})
26
+ except Exception:
27
+ results.append({'valid': False, 'error': traceback.format_exc().strip()})
28
+ json.dump(results, sys.stdout)
19
29
  `;
20
- const result = spawnSync("python3", ["-c", validator, effectiveSchema], { cwd, encoding: "utf8", input: JSON.stringify(value) });
21
- if (result.status !== 0) throw new TypeError(`${result.stdout}${result.stderr}`.trim());
30
+
31
+ /** One immutable validation phase, never a cache of successful work units. */
32
+ export function validateJsonSchemas(items, { cwd = process.cwd() } = {}) {
33
+ if (!Array.isArray(items)) throw new TypeError("schema batch 必须是数组");
34
+ if (!items.length) return [];
35
+ const jobs = items.map(({ value, schemaPath, formatChecker = true, errorStyle = "compact", label = "JSON Schema" }) => {
36
+ const effectiveSchema = path.resolve(schemaPath);
37
+ if (!existsSync(effectiveSchema)) throw new TypeError(`缺少 ${label}: ${effectiveSchema}`);
38
+ if (!["compact", "verbose"].includes(errorStyle)) throw new TypeError(`未知 schema errorStyle: ${errorStyle}`);
39
+ return { value, schemaPath: effectiveSchema, formatChecker, errorStyle };
40
+ });
41
+ const result = spawnSync("python3", ["-c", validator], { cwd, encoding: "utf8", input: JSON.stringify(jobs), maxBuffer: 16 * 1024 * 1024 });
42
+ if (result.error || result.status !== 0) throw new TypeError(result.error?.message || `${result.stdout}${result.stderr}`.trim());
43
+ const results = JSON.parse(result.stdout);
44
+ if (results.length !== jobs.length) throw new TypeError("schema batch 返回数量不匹配");
45
+ return results;
46
+ }
47
+
48
+ export function validateJsonSchema(value, schemaPath, options = {}) {
49
+ const [result] = validateJsonSchemas([{ value, schemaPath, ...options }], options);
50
+ if (!result.valid) throw new TypeError(result.error);
22
51
  }
@@ -0,0 +1,141 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { parseDocument } from "../vendor/yaml.mjs";
5
+ import { ROOT, loadRegistry, semanticDigest, validateRegistry } from "./lifecycle-registry.mjs";
6
+ import { loadSkillRegistry } from "./skill-registry.mjs";
7
+
8
+ const LIFECYCLE_REGISTRY_REF = "docs/process/lifecycle-registry.yaml";
9
+ const ORCHESTRATION_CONTRACT_REF = ".agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml";
10
+ const SKILL_REGISTRY_REF = "docs/agents/yss-skill-registry.yaml";
11
+
12
+ function read(relativePath) {
13
+ return readFileSync(path.join(ROOT, relativePath), "utf8");
14
+ }
15
+
16
+ function sha256(source) {
17
+ return createHash("sha256").update(source).digest("hex");
18
+ }
19
+
20
+ function loadYaml(relativePath, label) {
21
+ const document = parseDocument(read(relativePath), { maxAliasCount: 0, uniqueKeys: true });
22
+ if (document.errors.length) throw new TypeError(`无法解析${label}: ${document.errors[0].message}`);
23
+ const value = document.toJS({ maxAliasCount: 0 });
24
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${label}必须是对象`);
25
+ return value;
26
+ }
27
+
28
+ function canonicalize(value) {
29
+ if (Array.isArray(value)) return value.map(canonicalize);
30
+ if (value && typeof value === "object") {
31
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalize(value[key])]));
32
+ }
33
+ return value;
34
+ }
35
+
36
+ function selectById(records, id, label) {
37
+ if (!id) return null;
38
+ const selected = records.find((record) => record.id === id);
39
+ if (!selected) throw new TypeError(`未知${label}: ${id}`);
40
+ return selected;
41
+ }
42
+
43
+ function collectPathReferences(value, references = new Set()) {
44
+ if (Array.isArray(value)) {
45
+ for (const item of value) collectPathReferences(item, references);
46
+ } else if (value && typeof value === "object") {
47
+ for (const item of Object.values(value)) collectPathReferences(item, references);
48
+ } else if (typeof value === "string" && /(?:^|\/)[^\s]+\.(?:md|ya?ml|json|mjs)(?:#.*)?$/.test(value)) {
49
+ references.add(value);
50
+ }
51
+ return references;
52
+ }
53
+
54
+ function selectedSkills(route, skillRegistry) {
55
+ if (!route) return [];
56
+ const ids = new Set([
57
+ route.primary_skill,
58
+ route.native?.skill,
59
+ ...(route.supporting_skills ?? []),
60
+ ...(route.skills ?? []),
61
+ ].filter(Boolean));
62
+ return skillRegistry.skills.filter((skill) => ids.has(skill.id));
63
+ }
64
+
65
+ export function queryLifecycleContext({ mode, stageId, workUnitId, include = [] } = {}) {
66
+ if (!mode && !stageId && !workUnitId && include.length === 0) {
67
+ throw new TypeError("至少提供 --mode、--stage、--work-unit 或 --include 之一");
68
+ }
69
+
70
+ const lifecycle = validateRegistry(loadRegistry());
71
+ const orchestration = loadYaml(ORCHESTRATION_CONTRACT_REF, "生命周期编排合同");
72
+ const skillRegistry = loadSkillRegistry();
73
+ const planUnit = ['work-unit.plan-opportunity', 'work-unit.plan-requirements', 'work-unit.domain-strategy-design', 'work-unit.stage-decision', 'work-unit.spec-synthesis'].includes(workUnitId);
74
+ const loadPlan = ['stage.plan', 'stage.spec-architecture'].includes(stageId) || planUnit;
75
+ stageId ??= planUnit ? 'stage.plan' : undefined;
76
+ if (loadPlan) include = [...include, 'planning', 'grill_exit'];
77
+ const stage = selectById(lifecycle.stages, stageId, "阶段");
78
+ const workUnit = selectById(lifecycle.work_units, workUnitId, "工作单元");
79
+
80
+ if (mode && !(mode in orchestration.modes)) throw new TypeError(`未知模式: ${mode}`);
81
+ const normalizedIncludes = [...new Set(include.flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean))].sort();
82
+ for (const key of normalizedIncludes) {
83
+ if (!(key in orchestration)) throw new TypeError(`未知编排合同子树: ${key}`);
84
+ }
85
+
86
+ const route = workUnitId ? orchestration.work_unit_routes?.[workUnitId] ?? null : null;
87
+ const gates = lifecycle.gates.filter((gate) => gate.stage === stageId || (loadPlan && gate.stage === 'stage.plan'));
88
+ const artifacts = lifecycle.artifacts.filter((artifact) => artifact.stage === stageId || (loadPlan && artifact.stage === 'stage.plan'));
89
+ const evidenceIds = new Set(gates.flatMap((gate) => gate.evidence ?? []));
90
+ const selected = Object.fromEntries(normalizedIncludes.map((key) => [key, orchestration[key]]));
91
+ const transition = workUnitId ? {
92
+ next: orchestration.transition_graph?.routes?.[workUnitId] ?? [],
93
+ forbidden_shortcuts: (orchestration.transition_graph?.forbidden_shortcuts ?? [])
94
+ .filter((edge) => edge.from === workUnitId || edge.to === workUnitId),
95
+ } : null;
96
+
97
+ const result = {
98
+ schema_version: 1,
99
+ query: {
100
+ include: normalizedIncludes,
101
+ mode: mode ?? null,
102
+ stage: stageId ?? null,
103
+ work_unit: workUnitId ?? null,
104
+ },
105
+ sources: {
106
+ lifecycle_registry: {
107
+ ref: LIFECYCLE_REGISTRY_REF,
108
+ semantic_sha256: semanticDigest(lifecycle),
109
+ status: lifecycle.status,
110
+ },
111
+ orchestration_contract: {
112
+ ref: ORCHESTRATION_CONTRACT_REF,
113
+ sha256: sha256(read(ORCHESTRATION_CONTRACT_REF)),
114
+ },
115
+ skill_registry: {
116
+ ref: SKILL_REGISTRY_REF,
117
+ sha256: sha256(read(SKILL_REGISTRY_REF)),
118
+ status: skillRegistry.status,
119
+ },
120
+ },
121
+ lifecycle: {
122
+ artifacts,
123
+ evidence: lifecycle.evidence.filter((item) => evidenceIds.has(item.id)),
124
+ gates,
125
+ stage,
126
+ work_unit: workUnit,
127
+ },
128
+ execution: {
129
+ mode: mode ? orchestration.modes[mode] : null,
130
+ selected,
131
+ ...(loadPlan ? { plan_checks: lifecycle.stages.find(stage => stage.id === 'stage.plan').spec_entry.required_checks.map(id => ({ id, status: 'pending', evidence_refs: [] })) } : {}),
132
+ transition,
133
+ work_unit_route: route,
134
+ },
135
+ skills: selectedSkills(route, skillRegistry),
136
+ };
137
+ result.references = [...collectPathReferences(result), LIFECYCLE_REGISTRY_REF, ORCHESTRATION_CONTRACT_REF, SKILL_REGISTRY_REF]
138
+ .filter((value, index, all) => all.indexOf(value) === index)
139
+ .sort();
140
+ return canonicalize(result);
141
+ }
@@ -2,9 +2,11 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { parseDocument } from "../vendor/yaml.mjs";
3
3
  import { assertWorkUnitUserDecision, assertImplementationDecision } from "./user-decision.mjs";
4
4
  import { enforceFrontendDelivery } from "./frontend-delivery-boundary.mjs";
5
+ import { validatePlanSpecEntry } from "./plan-spec-entry.mjs";
5
6
 
6
7
  const IMPLEMENTATION_WORK_UNIT = "work-unit.slice-implementation";
7
8
  const TICKET_DECOMPOSITION_WORK_UNIT = "work-unit.ticket-decomposition";
9
+ const REPOSITORY_PREPARATION_WORK_UNIT = "work-unit.implementation-repository-preparation";
8
10
 
9
11
  function deepFreeze(value) {
10
12
  if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
@@ -14,7 +16,7 @@ function deepFreeze(value) {
14
16
  }
15
17
 
16
18
  const NEXT_ROUTES = deepFreeze({
17
- "work-unit.entry-triage": ["work-unit.discovery-opportunity", "work-unit.discovery-requirements"],
19
+ "work-unit.entry-triage": ["work-unit.plan-opportunity", "work-unit.plan-requirements"],
18
20
  "work-unit.ssot-update": ["work-unit.skill-projection-sync", "work-unit.intensity-aware-verification"],
19
21
  "work-unit.skill-projection-sync": ["work-unit.template-snapshot-build", "work-unit.intensity-aware-verification"],
20
22
  "work-unit.template-snapshot-build": ["work-unit.attach-sync-integration", "work-unit.intensity-aware-verification"],
@@ -22,13 +24,14 @@ const NEXT_ROUTES = deepFreeze({
22
24
  "work-unit.intensity-aware-verification": ["work-unit.intensity-aware-review"],
23
25
  "work-unit.intensity-aware-review": ["work-unit.release-and-rollback"],
24
26
  "work-unit.release-and-rollback": [],
25
- "work-unit.discovery-opportunity": ["work-unit.discovery-requirements", "work-unit.domain-strategy-design", "work-unit.stage-decision", "work-unit.spec-synthesis"],
26
- "work-unit.discovery-requirements": ["work-unit.domain-strategy-design", "work-unit.stage-decision", "work-unit.spec-synthesis"],
27
+ "work-unit.plan-opportunity": ["work-unit.plan-requirements", "work-unit.domain-strategy-design", "work-unit.stage-decision", "work-unit.spec-synthesis"],
28
+ "work-unit.plan-requirements": ["work-unit.domain-strategy-design", "work-unit.stage-decision", "work-unit.spec-synthesis"],
27
29
  "work-unit.domain-strategy-design": ["work-unit.stage-decision", "work-unit.spec-synthesis"],
28
30
  "work-unit.stage-decision": ["work-unit.spec-synthesis"],
29
- "work-unit.spec-synthesis": ["work-unit.prototype-design", "work-unit.technical-analysis", TICKET_DECOMPOSITION_WORK_UNIT],
30
- "work-unit.prototype-design": ["work-unit.technical-analysis", TICKET_DECOMPOSITION_WORK_UNIT],
31
- "work-unit.technical-analysis": [TICKET_DECOMPOSITION_WORK_UNIT],
31
+ "work-unit.spec-synthesis": ["work-unit.prototype-design", "work-unit.technical-analysis", REPOSITORY_PREPARATION_WORK_UNIT],
32
+ "work-unit.prototype-design": ["work-unit.technical-analysis", REPOSITORY_PREPARATION_WORK_UNIT],
33
+ "work-unit.technical-analysis": [REPOSITORY_PREPARATION_WORK_UNIT],
34
+ [REPOSITORY_PREPARATION_WORK_UNIT]: [TICKET_DECOMPOSITION_WORK_UNIT],
32
35
  [TICKET_DECOMPOSITION_WORK_UNIT]: [IMPLEMENTATION_WORK_UNIT],
33
36
  [IMPLEMENTATION_WORK_UNIT]: ["work-unit.frontend-implementation-verification", "work-unit.code-review"],
34
37
  "work-unit.frontend-implementation-verification": ["work-unit.code-review"],
@@ -56,6 +59,15 @@ const BLOCKING_SIGNALS = Object.freeze({
56
59
  contractNotCurrent: "slice-contract-not-current",
57
60
  missingEvidence: "ticket-formalization-evidence-missing",
58
61
  stale: "ticket-formalization-stale",
62
+ repositoryDecisionRequired: "implementation-repository-decision-required",
63
+ repositoryLocationRequired: "implementation-repository-location-required",
64
+ repositoryOnboardingIncomplete: "implementation-repository-onboarding-incomplete",
65
+ scaffoldChoiceRequired: "scaffold-choice-required",
66
+ scaffoldContractUnapproved: "scaffold-contract-unapproved",
67
+ scaffoldGenerationIncomplete: "scaffold-generation-incomplete",
68
+ scaffoldVerificationFailed: "scaffold-verification-failed",
69
+ frontendTemplateUnavailable: "frontend-template-unavailable",
70
+ implementationRepositoriesStale: "implementation-repositories-stale",
59
71
  });
60
72
 
61
73
  const allowedResult = (evidenceRefs = []) => ({
@@ -113,6 +125,7 @@ function validateTicketReference(ref, trackerKind) {
113
125
  * by `validateWorkflowExecutionResult` before this function is called.
114
126
  */
115
127
  export function validateNextRoute(currentWorkUnit, nextRoute, decisionState, options = {}) {
128
+
116
129
  if (["work-unit.technical-analysis", TICKET_DECOMPOSITION_WORK_UNIT, IMPLEMENTATION_WORK_UNIT, "work-unit.frontend-implementation-verification"].includes(nextRoute)) {
117
130
  try { enforceFrontendDelivery(decisionState, { root: options.root, phase: nextRoute === IMPLEMENTATION_WORK_UNIT ? "implementation" : "inputs" }); }
118
131
  catch (error) { return blockedResult(["frontend-delivery-blocked"], [error.message]); }
@@ -130,9 +143,105 @@ export function validateNextRoute(currentWorkUnit, nextRoute, decisionState, opt
130
143
  }
131
144
  return blockedResult(signals, ["allowed_next_route"]);
132
145
  }
146
+ if (nextRoute === 'work-unit.spec-synthesis' || currentWorkUnit === 'work-unit.spec-synthesis') {
147
+ const entry = validatePlanSpecEntry(decisionState, options);
148
+ if (entry.result === 'blocked' || nextRoute === 'work-unit.spec-synthesis') return entry;
149
+ }
150
+ if (nextRoute === TICKET_DECOMPOSITION_WORK_UNIT) {
151
+ const readiness = validateImplementationRepositoriesReady(decisionState, options);
152
+ if (readiness.result === "blocked") return readiness;
153
+ }
133
154
  return decisionState ? validateDecisionBoundary(currentWorkUnit, decisionState, options) : allowedResult();
134
155
  }
135
156
 
157
+ /**
158
+ * Validate the aggregate stage-5 repository-preparation result. Every affected
159
+ * delivery role must resolve to an onboarded existing repository or a freshly
160
+ * generated and verified scaffold. Unaffected roles need an explicit reason.
161
+ */
162
+ export function validateImplementationRepositoriesReady(state, { exists = existsSync } = {}) {
163
+ const preparation = state?.implementation_repository_preparation;
164
+ const impacts = state?.delivery_impacts;
165
+ const signals = [];
166
+ const missing = [];
167
+ const evidenceRefs = Array.isArray(preparation?.evidence_refs) ? preparation.evidence_refs : [];
168
+
169
+ if (!preparation || preparation.result !== "completed" || !Array.isArray(preparation.projects)) {
170
+ return blockedResult([BLOCKING_SIGNALS.repositoryDecisionRequired], ["completed implementation_repository_preparation with projects"]);
171
+ }
172
+ if (preparation.current_version !== true || state?.implementation_repositories_stale === true) {
173
+ signals.push(BLOCKING_SIGNALS.implementationRepositoriesStale);
174
+ missing.push("implementation repository preparation is current");
175
+ }
176
+ if (evidenceRefs.length === 0 || evidenceRefs.some((ref) => !isReadable(ref, exists))) {
177
+ signals.push(BLOCKING_SIGNALS.repositoryOnboardingIncomplete);
178
+ missing.push("readable implementation_repository_preparation.evidence_refs");
179
+ }
180
+
181
+ for (const role of ["backend", "frontend"]) {
182
+ const affected = impacts?.[role] === true;
183
+ const projects = preparation.projects.filter((project) => project?.delivery_role === role);
184
+ if (affected && projects.length === 0) {
185
+ signals.push(BLOCKING_SIGNALS.repositoryDecisionRequired);
186
+ missing.push(`${role} repository decision`);
187
+ }
188
+ if (!affected && projects.length === 0) {
189
+ signals.push(BLOCKING_SIGNALS.repositoryDecisionRequired);
190
+ missing.push(`${role} not-applicable decision with reason`);
191
+ }
192
+ for (const project of projects) {
193
+ if (!hasText(project.project_id) || !hasText(project.status)) {
194
+ signals.push(BLOCKING_SIGNALS.repositoryDecisionRequired);
195
+ missing.push(`${role} project_id and status`);
196
+ continue;
197
+ }
198
+ if (project.status === "not-applicable") {
199
+ if (affected || !hasText(project.reason)) {
200
+ signals.push(BLOCKING_SIGNALS.repositoryDecisionRequired);
201
+ missing.push(`${project.project_id} not-applicable reason for an unaffected role`);
202
+ }
203
+ continue;
204
+ }
205
+ if (!hasText(project.repository_ref) || !hasText(project.project_root) || !hasText(project.repository_scope)) {
206
+ signals.push(BLOCKING_SIGNALS.repositoryLocationRequired);
207
+ missing.push(`${project.project_id} repository_ref, project_root and repository_scope`);
208
+ }
209
+ if (project.status === "existing-and-onboarded") {
210
+ if (project.onboarding_result?.status !== "completed" || !isReadable(project.onboarding_result?.ref, exists)) {
211
+ signals.push(BLOCKING_SIGNALS.repositoryOnboardingIncomplete);
212
+ missing.push(`${project.project_id} readable completed onboarding result`);
213
+ }
214
+ } else if (project.status === "initialized-and-verified") {
215
+ if (role === "backend" && !hasText(project.architecture_family)) {
216
+ signals.push(BLOCKING_SIGNALS.scaffoldChoiceRequired);
217
+ missing.push(`${project.project_id} confirmed backend architecture_family`);
218
+ }
219
+ const contract = project.scaffold_contract;
220
+ if (![3, 4].includes(contract?.schema_version) || contract?.status !== "approved" || contract?.persisted !== true || contract?.current_version !== true || (role === "frontend" && contract?.schema_version !== 4)) {
221
+ signals.push(BLOCKING_SIGNALS.scaffoldContractUnapproved);
222
+ missing.push(`${project.project_id} approved persisted current scaffold contract`);
223
+ }
224
+ if (!isReadable(project.scaffold_manifest_ref, exists)) {
225
+ signals.push(BLOCKING_SIGNALS.scaffoldGenerationIncomplete);
226
+ missing.push(`${project.project_id} readable scaffold manifest`);
227
+ }
228
+ if (project.scaffold_verification?.status !== "passed" || !isReadable(project.scaffold_verification?.ref, exists)) {
229
+ signals.push(BLOCKING_SIGNALS.scaffoldVerificationFailed);
230
+ missing.push(`${project.project_id} readable passed scaffold verification`);
231
+ }
232
+ if (role === "frontend" && project.template_available !== true) {
233
+ signals.push(BLOCKING_SIGNALS.frontendTemplateUnavailable);
234
+ missing.push(`${project.project_id} verified frontend template source`);
235
+ }
236
+ } else {
237
+ signals.push(BLOCKING_SIGNALS.repositoryDecisionRequired);
238
+ missing.push(`${project.project_id} supported repository preparation status`);
239
+ }
240
+ }
241
+ }
242
+ return signals.length === 0 ? allowedResult(evidenceRefs) : blockedResult(signals, missing, evidenceRefs);
243
+ }
244
+
136
245
  function validateDecisionBoundary(workUnit, state, options) {
137
246
  try { assertWorkUnitUserDecision(workUnit, state, options); return allowedResult(); }
138
247
  catch (error) { return blockedResult([error.code || "user-decision-invalid"], [error.message]); }
@@ -145,6 +254,8 @@ function validateDecisionBoundary(workUnit, state, options) {
145
254
  export function validateTicketFormalization(state, { exists = existsSync, read = (ref) => readFileSync(ref, "utf8"), ...decisionOptions } = {}) {
146
255
  try { enforceFrontendDelivery(state, { root: decisionOptions.root, phase: "implementation" }); }
147
256
  catch (error) { return blockedResult(["frontend-delivery-blocked"], [error.message]); }
257
+ const repositoryResult = validateImplementationRepositoriesReady(state, { exists });
258
+ if (repositoryResult.result === "blocked") return repositoryResult;
148
259
  const decomposition = state?.ticket_decomposition_result;
149
260
  const ticket = state?.vertical_slice_ticket;
150
261
  const contract = state?.slice_contract;
@@ -276,6 +387,7 @@ export function validateImplementationEntry(state, options = {}) {
276
387
  export const lifecycleTransitionContract = Object.freeze({
277
388
  implementation_work_unit: IMPLEMENTATION_WORK_UNIT,
278
389
  ticket_decomposition_work_unit: TICKET_DECOMPOSITION_WORK_UNIT,
390
+ repository_preparation_work_unit: REPOSITORY_PREPARATION_WORK_UNIT,
279
391
  next_routes: NEXT_ROUTES,
280
392
  blocking_signals: BLOCKING_SIGNALS,
281
393
  });
@@ -0,0 +1,87 @@
1
+ import path from 'node:path';
2
+ import { verifyContextReconciliation } from './context-reconciliation.mjs';
3
+ import { decisionIO, decisionDigest, assertUserDecisionRequirement } from './user-decision.mjs';
4
+ import { validateApprovalRecord } from './approval-record.mjs';
5
+
6
+ const fail = message => { throw new TypeError(`plan-spec-entry-blocked: ${message}`); };
7
+ const text = value => typeof value === 'string' && value.trim().length > 0;
8
+
9
+ export function planEntryPolicy(options = {}) {
10
+ const registry = decisionIO(options).document('docs/process/lifecycle-registry.yaml');
11
+ const policy = registry.stages.find(stage => stage.id === 'stage.plan')?.spec_entry;
12
+ if (!policy?.required_checks?.length || !policy?.gate_impacts) fail('缺少 Plan 入口策略');
13
+ const gateIds = registry.gates.filter(gate => gate.stage === 'stage.plan').map(gate => gate.id).sort();
14
+ if (JSON.stringify(gateIds) !== JSON.stringify(Object.keys(policy.gate_impacts).sort())) fail('Plan 门禁触发策略覆盖不完整');
15
+ return policy;
16
+ }
17
+
18
+ // 自动加载只产生 pending;持久化审阅包、证据新鲜度和真实回复共同决定放行。
19
+ export function assertPlanSpecEntry(state, options = {}) {
20
+ if (!text(state?.plan_review_ref) || !text(state?.feature_id)) fail('缺少 plan_review_ref / feature_id');
21
+ const io = decisionIO(options);
22
+ const policy = planEntryPolicy(options);
23
+ const review = io.document(state.plan_review_ref);
24
+ if (review.schema_version !== 1 || review.kind !== 'plan-entry-review' || review.feature_id !== state.feature_id) fail('审阅包身份或范围不匹配');
25
+ if (!Array.isArray(review.basis) || !review.basis.length) fail('缺少带摘要依据');
26
+ const basis = new Map();
27
+ for (const asset of review.basis) {
28
+ if (!text(asset.ref) || basis.has(asset.ref) || decisionDigest(io.bytes(asset.ref)) !== asset.digest) fail(`依据缺失、重复或已变更: ${asset.ref}`);
29
+ basis.set(asset.ref, asset);
30
+ }
31
+ const evidence = refs => {
32
+ if (!Array.isArray(refs) || !refs.length || refs.some(ref => !basis.has(ref))) fail('检查项缺少当前依据');
33
+ };
34
+ for (const ref of [review.plan_ref, 'CONTEXT.md', 'docs/process/lifecycle-registry.yaml', review.context_reconciliation_ref]) {
35
+ if (!basis.has(ref)) fail(`必需依据未绑定: ${ref}`);
36
+ }
37
+ if (Object.keys(review.checks || {}).length !== policy.required_checks.length) fail('检查项缺失或存在未知项');
38
+ for (const id of policy.required_checks) {
39
+ const check = review.checks[id];
40
+ if (check?.status !== 'passed') fail(`检查未通过: ${id}`);
41
+ evidence(check.evidence_refs);
42
+ }
43
+ if (!Array.isArray(review.open_items)) fail('必须显式列出未决项');
44
+ for (const item of review.open_items) {
45
+ if (!text(item.id) || typeof item.critical !== 'boolean' || typeof item.runnable_blocker !== 'boolean') fail('未决项分类不完整');
46
+ if (item.status === 'resolved') { evidence(item.evidence_refs); continue; }
47
+ if (item.status !== 'deferred' || item.critical || item.runnable_blocker) fail(`关键问题或可执行阻塞未解决: ${item.id}`);
48
+ for (const field of ['noncritical_reason', 'owner', 'resolution_point', 'downstream_recipient']) if (!text(item[field])) fail(`延期项缺少 ${field}`);
49
+ evidence(item.evidence_refs);
50
+ }
51
+ for (const [gateId, impact] of Object.entries(policy.gate_impacts)) {
52
+ if (typeof review.impacts?.[impact] !== 'boolean') fail(`未评估影响面: ${impact}`);
53
+ const gate = review.gates?.[gateId];
54
+ evidence(gate?.evidence_refs);
55
+ if (review.impacts[impact]) {
56
+ if (gate.status !== 'approved' || !basis.has(gate.approval_ref) || !basis.has(gate.subject_ref)) fail(`命中门禁未批准或未绑定依据: ${gateId}`);
57
+ const record = io.document(gate.approval_ref);
58
+ if (record.gate_id !== gateId || record.subject_ref !== gate.subject_ref || !gate.approval_scope?.includes(state.feature_id) || JSON.stringify([...(gate.approval_scope || [])].sort()) !== JSON.stringify([...(record.approval_scope || [])].sort())) fail(`会签资产或范围不匹配: ${gateId}`);
59
+ validateApprovalRecord(record, { ...options, requireApproved: true });
60
+ assertUserDecisionRequirement({ boundary: gateId, subject_ref: gate.subject_ref, scope: gate.approval_scope, user_decision_ref: record.user_decision_ref }, options);
61
+ } else if (gate.status !== 'not-applicable' || !text(gate.reason)) fail(`未命中门禁须有原因和依据: ${gateId}`);
62
+ }
63
+ // 门禁依赖不能通过将上游标成 N/A 来跳过。
64
+ const registry = io.document('docs/process/lifecycle-registry.yaml');
65
+ for (const gate of registry.gates.filter(gate => gate.stage === 'stage.plan')) {
66
+ if (review.gates[gate.id]?.status === 'approved') {
67
+ for (const dependency of gate.requires_gates || []) if (review.gates[dependency]?.status !== 'approved') fail(`门禁依赖未批准: ${dependency}`);
68
+ }
69
+ }
70
+ const reconciliation = io.document(review.context_reconciliation_ref);
71
+ if (reconciliation.status !== 'reconciled' || reconciliation.repository_mode !== 'project-instance') fail('Context 未调和');
72
+ try { verifyContextReconciliation(path.resolve(io.root, review.context_reconciliation_ref), { root: io.root }); }
73
+ catch (error) { fail(`Context reconciliation 验证失败: ${error.message}\n`); }
74
+ assertUserDecisionRequirement({ boundary: 'plan-conclusion', subject_ref: state.plan_review_ref, scope: [state.feature_id], user_decision_ref: state.plan_user_decision_ref }, options);
75
+ return { result: 'allowed', blocking_signals: [], missing_requirements: [], evidence_refs: [state.plan_review_ref, ...basis.keys()], next_work_unit: null };
76
+ }
77
+
78
+ export function validatePlanSpecEntry(state, options = {}) {
79
+ try { return assertPlanSpecEntry(state, options); }
80
+ catch (error) { return { result: 'blocked', blocking_signals: ['plan-spec-entry-blocked'], missing_requirements: [error.message], evidence_refs: [], next_work_unit: null }; }
81
+ }
82
+
83
+ export function assertPlanCheckpoint(checkpoint, options = {}) {
84
+ if (checkpoint.repository_mode !== 'project-instance') return;
85
+ const entering = checkpoint.next_work_unit === 'work-unit.spec-synthesis' || checkpoint.stage === 'stage.spec-architecture' || checkpoint.stage_trace?.completed_work_unit === 'work-unit.spec-synthesis';
86
+ if (entering) assertPlanSpecEntry(checkpoint, options);
87
+ }