create-yss-spec 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. package/README.md +119 -89
  2. package/bin/create-yss-spec.js +11 -3
  3. package/package.json +9 -2
  4. package/src/api/_sync-service.js +191 -0
  5. package/src/api/index.js +22 -0
  6. package/src/api/project-diff.js +9 -0
  7. package/src/api/project-doctor.js +10 -0
  8. package/src/api/template-apply.js +10 -0
  9. package/src/api/template-plan.js +9 -0
  10. package/src/cli/args.js +60 -0
  11. package/src/cli/error-output.js +52 -0
  12. package/src/cli/flags.js +9 -0
  13. package/src/cli/help.js +95 -0
  14. package/src/cli/index.js +82 -0
  15. package/src/cli/plan-output.js +61 -0
  16. package/src/cli/prompts.js +91 -0
  17. package/src/cli/router.js +41 -0
  18. package/src/cli.js +5 -2253
  19. package/src/commands/attach.js +280 -0
  20. package/src/commands/diff.js +23 -0
  21. package/src/commands/doctor.js +417 -0
  22. package/src/commands/init.js +193 -0
  23. package/src/commands/sync.js +146 -0
  24. package/src/commands/update.js +14 -0
  25. package/src/contracts/apply-result-v1.json +45 -0
  26. package/src/contracts/customization-policy-v1.json +28 -0
  27. package/src/contracts/doctor-report-v1.json +29 -0
  28. package/src/contracts/error-envelope-v1.json +24 -0
  29. package/src/contracts/generator-policy-v1.json +24 -0
  30. package/src/contracts/ownership-policy-v1.json +40 -0
  31. package/src/contracts/plan-schema-v1.json +115 -0
  32. package/src/family-identity.js +365 -0
  33. package/src/family-runtime.js +23 -0
  34. package/src/filesystem/apply-plan.js +44 -0
  35. package/src/filesystem/copy-path.js +28 -0
  36. package/src/filesystem/path-utils.js +67 -0
  37. package/src/filesystem/transaction-runner.js +41 -0
  38. package/src/filesystem/transaction.js +153 -0
  39. package/src/git/submodule.js +115 -0
  40. package/src/git/worktree.js +69 -0
  41. package/src/template/attach-planner-runtime.js +51 -0
  42. package/src/template/attach-planner.js +160 -0
  43. package/src/template/instance-runtime.js +463 -0
  44. package/src/template/lifecycle-metadata.js +99 -0
  45. package/src/template/lifecycle-policy.js +129 -0
  46. package/src/template/lifecycle-runtime.js +90 -0
  47. package/src/template/migration-planner.js +95 -0
  48. package/src/template/migration-runtime.js +247 -0
  49. package/src/template/ownership-metadata.js +79 -0
  50. package/src/template/ownership-policy.js +149 -0
  51. package/src/template/ownership-runtime.js +109 -0
  52. package/src/template/plan-schema.js +36 -0
  53. package/src/template/sync-planner-runtime.js +57 -0
  54. package/src/template/sync-planner.js +206 -0
  55. package/src/template/verification-runtime.js +154 -0
  56. package/src/validation/identity.js +41 -0
  57. package/src/validation/metadata.js +105 -0
  58. package/src/validation/security.js +67 -0
  59. package/src/validation/snapshot.js +47 -0
  60. package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
  61. package/template/.agents/skills/yss-domain/SKILL.md +3 -3
  62. package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  63. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +4 -2
  64. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
  65. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -2
  66. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +10 -1
  67. package/template/.agents/skills/yss-mvc-design/SKILL.md +16 -0
  68. package/template/.agents/skills/yss-mvc-design/agents/openai.yaml +4 -0
  69. package/template/.agents/skills/yss-mvc-design/references/mvc-design.schema.json +315 -0
  70. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +4 -2
  71. package/template/.agents/skills/yss-product-lifecycle/references/artifact-dependencies.md +2 -2
  72. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +14 -9
  73. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +1 -1
  74. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +1 -1
  75. package/template/.agents/skills/yss-tactical-design/SKILL.md +2 -2
  76. package/template/.agents/skills/yss-tactical-design/references/output-template.md +2 -0
  77. package/template/.agents/skills/yss-tactical-design/references/tactical-design-contract.md +3 -1
  78. package/template/.agents/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +3 -2
  79. package/template/.agents/skills/yss-technical-design/SKILL.md +31 -0
  80. package/template/.agents/skills/yss-technical-design/agents/openai.yaml +4 -0
  81. package/template/.agents/skills/yss-technical-design/references/technical-design-common.schema.json +290 -0
  82. package/template/.agents/skills/yss-technical-design/references/technical-design-contract.md +31 -0
  83. package/template/.agents/skills/yss-technical-design/references/technical-design.schema.json +759 -0
  84. package/template/.agents/skills/yss-technical-design/scripts/generate-schema.mjs +20 -0
  85. package/template/.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs +134 -0
  86. package/template/.agents/skills/yss-technical-design/tests/fixtures.mjs +32 -0
  87. package/template/.agents/skills/yss-technical-design/tests/run-scenarios.mjs +82 -0
  88. package/template/.agents/skills/yss-ui/SKILL.md +2 -0
  89. package/template/.claude/skills/yss-domain/SKILL.md +3 -3
  90. package/template/.claude/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  91. package/template/.claude/skills/yss-implementation-contract-compiler/SKILL.md +4 -2
  92. package/template/.claude/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
  93. package/template/.claude/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -2
  94. package/template/.claude/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +10 -1
  95. package/template/.claude/skills/yss-mvc-design/SKILL.md +16 -0
  96. package/template/.claude/skills/yss-mvc-design/agents/openai.yaml +4 -0
  97. package/template/.claude/skills/yss-mvc-design/references/mvc-design.schema.json +315 -0
  98. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +4 -2
  99. package/template/.claude/skills/yss-product-lifecycle/references/artifact-dependencies.md +2 -2
  100. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +14 -9
  101. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +1 -1
  102. package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +1 -1
  103. package/template/.claude/skills/yss-tactical-design/SKILL.md +2 -2
  104. package/template/.claude/skills/yss-tactical-design/references/output-template.md +2 -0
  105. package/template/.claude/skills/yss-tactical-design/references/tactical-design-contract.md +3 -1
  106. package/template/.claude/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +3 -2
  107. package/template/.claude/skills/yss-technical-design/SKILL.md +31 -0
  108. package/template/.claude/skills/yss-technical-design/agents/openai.yaml +4 -0
  109. package/template/.claude/skills/yss-technical-design/references/technical-design-common.schema.json +290 -0
  110. package/template/.claude/skills/yss-technical-design/references/technical-design-contract.md +31 -0
  111. package/template/.claude/skills/yss-technical-design/references/technical-design.schema.json +759 -0
  112. package/template/.claude/skills/yss-technical-design/scripts/generate-schema.mjs +20 -0
  113. package/template/.claude/skills/yss-technical-design/scripts/validate-technical-design.mjs +134 -0
  114. package/template/.claude/skills/yss-technical-design/tests/fixtures.mjs +32 -0
  115. package/template/.claude/skills/yss-technical-design/tests/run-scenarios.mjs +82 -0
  116. package/template/.claude/skills/yss-ui/SKILL.md +2 -0
  117. package/template/.codex/skills/yss-domain/SKILL.md +3 -3
  118. package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  119. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +4 -2
  120. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
  121. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -2
  122. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +10 -1
  123. package/template/.codex/skills/yss-mvc-design/SKILL.md +16 -0
  124. package/template/.codex/skills/yss-mvc-design/agents/openai.yaml +4 -0
  125. package/template/.codex/skills/yss-mvc-design/references/mvc-design.schema.json +315 -0
  126. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +4 -2
  127. package/template/.codex/skills/yss-product-lifecycle/references/artifact-dependencies.md +2 -2
  128. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +14 -9
  129. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +1 -1
  130. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +1 -1
  131. package/template/.codex/skills/yss-tactical-design/SKILL.md +2 -2
  132. package/template/.codex/skills/yss-tactical-design/references/output-template.md +2 -0
  133. package/template/.codex/skills/yss-tactical-design/references/tactical-design-contract.md +3 -1
  134. package/template/.codex/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +3 -2
  135. package/template/.codex/skills/yss-technical-design/SKILL.md +31 -0
  136. package/template/.codex/skills/yss-technical-design/agents/openai.yaml +4 -0
  137. package/template/.codex/skills/yss-technical-design/references/technical-design-common.schema.json +290 -0
  138. package/template/.codex/skills/yss-technical-design/references/technical-design-contract.md +31 -0
  139. package/template/.codex/skills/yss-technical-design/references/technical-design.schema.json +759 -0
  140. package/template/.codex/skills/yss-technical-design/scripts/generate-schema.mjs +20 -0
  141. package/template/.codex/skills/yss-technical-design/scripts/validate-technical-design.mjs +134 -0
  142. package/template/.codex/skills/yss-technical-design/tests/fixtures.mjs +32 -0
  143. package/template/.codex/skills/yss-technical-design/tests/run-scenarios.mjs +82 -0
  144. package/template/.codex/skills/yss-ui/SKILL.md +2 -0
  145. package/template/.cursor/skills/yss-domain/SKILL.md +3 -3
  146. package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  147. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +4 -2
  148. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
  149. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -2
  150. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +10 -1
  151. package/template/.cursor/skills/yss-mvc-design/SKILL.md +16 -0
  152. package/template/.cursor/skills/yss-mvc-design/agents/openai.yaml +4 -0
  153. package/template/.cursor/skills/yss-mvc-design/references/mvc-design.schema.json +315 -0
  154. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +4 -2
  155. package/template/.cursor/skills/yss-product-lifecycle/references/artifact-dependencies.md +2 -2
  156. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +14 -9
  157. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +1 -1
  158. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +1 -1
  159. package/template/.cursor/skills/yss-tactical-design/SKILL.md +2 -2
  160. package/template/.cursor/skills/yss-tactical-design/references/output-template.md +2 -0
  161. package/template/.cursor/skills/yss-tactical-design/references/tactical-design-contract.md +3 -1
  162. package/template/.cursor/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +3 -2
  163. package/template/.cursor/skills/yss-technical-design/SKILL.md +31 -0
  164. package/template/.cursor/skills/yss-technical-design/agents/openai.yaml +4 -0
  165. package/template/.cursor/skills/yss-technical-design/references/technical-design-common.schema.json +290 -0
  166. package/template/.cursor/skills/yss-technical-design/references/technical-design-contract.md +31 -0
  167. package/template/.cursor/skills/yss-technical-design/references/technical-design.schema.json +759 -0
  168. package/template/.cursor/skills/yss-technical-design/scripts/generate-schema.mjs +20 -0
  169. package/template/.cursor/skills/yss-technical-design/scripts/validate-technical-design.mjs +134 -0
  170. package/template/.cursor/skills/yss-technical-design/tests/fixtures.mjs +32 -0
  171. package/template/.cursor/skills/yss-technical-design/tests/run-scenarios.mjs +82 -0
  172. package/template/.cursor/skills/yss-ui/SKILL.md +2 -0
  173. package/template/.pi/skills/yss-domain/SKILL.md +3 -3
  174. package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  175. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +4 -2
  176. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
  177. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -2
  178. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +10 -1
  179. package/template/.pi/skills/yss-mvc-design/SKILL.md +16 -0
  180. package/template/.pi/skills/yss-mvc-design/agents/openai.yaml +4 -0
  181. package/template/.pi/skills/yss-mvc-design/references/mvc-design.schema.json +315 -0
  182. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +4 -2
  183. package/template/.pi/skills/yss-product-lifecycle/references/artifact-dependencies.md +2 -2
  184. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +14 -9
  185. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +1 -1
  186. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +1 -1
  187. package/template/.pi/skills/yss-tactical-design/SKILL.md +2 -2
  188. package/template/.pi/skills/yss-tactical-design/references/output-template.md +2 -0
  189. package/template/.pi/skills/yss-tactical-design/references/tactical-design-contract.md +3 -1
  190. package/template/.pi/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +3 -2
  191. package/template/.pi/skills/yss-technical-design/SKILL.md +31 -0
  192. package/template/.pi/skills/yss-technical-design/agents/openai.yaml +4 -0
  193. package/template/.pi/skills/yss-technical-design/references/technical-design-common.schema.json +290 -0
  194. package/template/.pi/skills/yss-technical-design/references/technical-design-contract.md +31 -0
  195. package/template/.pi/skills/yss-technical-design/references/technical-design.schema.json +759 -0
  196. package/template/.pi/skills/yss-technical-design/scripts/generate-schema.mjs +20 -0
  197. package/template/.pi/skills/yss-technical-design/scripts/validate-technical-design.mjs +134 -0
  198. package/template/.pi/skills/yss-technical-design/tests/fixtures.mjs +32 -0
  199. package/template/.pi/skills/yss-technical-design/tests/run-scenarios.mjs +82 -0
  200. package/template/.pi/skills/yss-ui/SKILL.md +2 -0
  201. package/template/.qoder/skills/yss-domain/SKILL.md +3 -3
  202. package/template/.qoder/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  203. package/template/.qoder/skills/yss-implementation-contract-compiler/SKILL.md +4 -2
  204. package/template/.qoder/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
  205. package/template/.qoder/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -2
  206. package/template/.qoder/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +10 -1
  207. package/template/.qoder/skills/yss-mvc-design/SKILL.md +16 -0
  208. package/template/.qoder/skills/yss-mvc-design/agents/openai.yaml +4 -0
  209. package/template/.qoder/skills/yss-mvc-design/references/mvc-design.schema.json +315 -0
  210. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +4 -2
  211. package/template/.qoder/skills/yss-product-lifecycle/references/artifact-dependencies.md +2 -2
  212. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +14 -9
  213. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +1 -1
  214. package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +1 -1
  215. package/template/.qoder/skills/yss-tactical-design/SKILL.md +2 -2
  216. package/template/.qoder/skills/yss-tactical-design/references/output-template.md +2 -0
  217. package/template/.qoder/skills/yss-tactical-design/references/tactical-design-contract.md +3 -1
  218. package/template/.qoder/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +3 -2
  219. package/template/.qoder/skills/yss-technical-design/SKILL.md +31 -0
  220. package/template/.qoder/skills/yss-technical-design/agents/openai.yaml +4 -0
  221. package/template/.qoder/skills/yss-technical-design/references/technical-design-common.schema.json +290 -0
  222. package/template/.qoder/skills/yss-technical-design/references/technical-design-contract.md +31 -0
  223. package/template/.qoder/skills/yss-technical-design/references/technical-design.schema.json +759 -0
  224. package/template/.qoder/skills/yss-technical-design/scripts/generate-schema.mjs +20 -0
  225. package/template/.qoder/skills/yss-technical-design/scripts/validate-technical-design.mjs +134 -0
  226. package/template/.qoder/skills/yss-technical-design/tests/fixtures.mjs +32 -0
  227. package/template/.qoder/skills/yss-technical-design/tests/run-scenarios.mjs +82 -0
  228. package/template/.qoder/skills/yss-ui/SKILL.md +2 -0
  229. package/template/.trae/skills/yss-domain/SKILL.md +3 -3
  230. package/template/.trae/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  231. package/template/.trae/skills/yss-implementation-contract-compiler/SKILL.md +4 -2
  232. package/template/.trae/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
  233. package/template/.trae/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -2
  234. package/template/.trae/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +10 -1
  235. package/template/.trae/skills/yss-mvc-design/SKILL.md +16 -0
  236. package/template/.trae/skills/yss-mvc-design/agents/openai.yaml +4 -0
  237. package/template/.trae/skills/yss-mvc-design/references/mvc-design.schema.json +315 -0
  238. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +4 -2
  239. package/template/.trae/skills/yss-product-lifecycle/references/artifact-dependencies.md +2 -2
  240. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +14 -9
  241. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +1 -1
  242. package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +1 -1
  243. package/template/.trae/skills/yss-tactical-design/SKILL.md +2 -2
  244. package/template/.trae/skills/yss-tactical-design/references/output-template.md +2 -0
  245. package/template/.trae/skills/yss-tactical-design/references/tactical-design-contract.md +3 -1
  246. package/template/.trae/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +3 -2
  247. package/template/.trae/skills/yss-technical-design/SKILL.md +31 -0
  248. package/template/.trae/skills/yss-technical-design/agents/openai.yaml +4 -0
  249. package/template/.trae/skills/yss-technical-design/references/technical-design-common.schema.json +290 -0
  250. package/template/.trae/skills/yss-technical-design/references/technical-design-contract.md +31 -0
  251. package/template/.trae/skills/yss-technical-design/references/technical-design.schema.json +759 -0
  252. package/template/.trae/skills/yss-technical-design/scripts/generate-schema.mjs +20 -0
  253. package/template/.trae/skills/yss-technical-design/scripts/validate-technical-design.mjs +134 -0
  254. package/template/.trae/skills/yss-technical-design/tests/fixtures.mjs +32 -0
  255. package/template/.trae/skills/yss-technical-design/tests/run-scenarios.mjs +82 -0
  256. package/template/.trae/skills/yss-ui/SKILL.md +2 -0
  257. package/template/CONTEXT.md +4 -1
  258. package/template/README.md +4 -0
  259. package/template/docs/agents/digital-human-roles.yaml +3 -2
  260. package/template/docs/agents/yss-skill-registry.yaml +20 -1
  261. package/template/docs/architecture/templates/architecture-review-checklist.md +1 -1
  262. package/template/docs/architecture/templates/system-overview-design-template.md +15 -4
  263. package/template/docs/process/frontend-backend-delivery.md +72 -0
  264. package/template/docs/process/implementation-repo-integration.md +2 -0
  265. package/template/docs/process/lifecycle-artifact-map.md +3 -0
  266. package/template/docs/process/lifecycle-registry-baseline.json +4 -1
  267. package/template/docs/process/lifecycle-registry.yaml +14 -2
  268. package/template/docs/process/schemas/backend-delivery-verification.schema.json +113 -0
  269. package/template/docs/process/schemas/backend-delivery.schema.json +301 -0
  270. package/template/docs/process/schemas/digital-human-task-package.schema.json +8 -0
  271. package/template/docs/process/schemas/frontend-delivery-acceptance.schema.json +200 -0
  272. package/template/docs/process/schemas/frontend-implementation-evidence.schema.json +3 -0
  273. package/template/docs/process/schemas/strategic-design-handoff.schema.json +77 -4
  274. package/template/docs/process/strategic-handoff-package.md +4 -2
  275. package/template/docs/process/template-verification-profiles.yaml +20 -0
  276. package/template/docs/user-guide//346/210/230/346/234/257/350/256/276/350/256/241/345/255/220/351/241/271/347/233/256/347/224/250/346/210/267/346/211/213/345/206/214.md +21 -206
  277. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +63 -963
  278. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +11 -14
  279. package/template/docs/user-guide//350/256/276/345/244/207/345/200/237/347/224/250/350/264/257/347/251/277/346/241/210/344/276/213.md +127 -0
  280. package/template/scripts/backend-delivery +14 -0
  281. package/template/scripts/fixtures/backend-delivery/revision-server.mjs +11 -0
  282. package/template/scripts/fixtures/strategic-handoff/fixture.mjs +3 -2
  283. package/template/scripts/instantiate-harness +69 -0
  284. package/template/scripts/lib/backend-delivery.mjs +164 -0
  285. package/template/scripts/lib/frontend-delivery-boundary.mjs +49 -0
  286. package/template/scripts/lib/frontend-delivery.mjs +73 -0
  287. package/template/scripts/lib/harness-execution-scope.mjs +52 -0
  288. package/template/scripts/lib/implementation-contract-compiler.mjs +19 -2
  289. package/template/scripts/lib/lifecycle-transition.mjs +7 -0
  290. package/template/scripts/lib/strategic-handoff-consumption.mjs +17 -10
  291. package/template/scripts/lib/strategic-handoff-design-targets.mjs +15 -0
  292. package/template/scripts/lib/strategic-handoff.mjs +1 -1
  293. package/template/scripts/lib/task-package.mjs +7 -0
  294. package/template/scripts/lib/technical-design-boundary.mjs +23 -0
  295. package/template/scripts/sync-strategic-handoff-tools +5 -2
  296. package/template/scripts/verify-frontend-delivery +9 -0
  297. package/template/scripts/verify-frontend-delivery-scenarios +131 -0
  298. package/template/scripts/verify-frontend-implementation-evidence +2 -0
  299. package/template/scripts/verify-strategic-handoff-package-scenarios +7 -1
  300. package/template/skills-lock.json +43 -13
  301. package/template.manifest.json +43 -1
  302. package/template.snapshot.json +5 -5
@@ -0,0 +1,463 @@
1
+ "use strict";
2
+
3
+ const crypto = require("node:crypto");
4
+ const fs = require("node:fs");
5
+ const path = require("node:path");
6
+
7
+ const { treeHash } = require("../template-hash");
8
+ const { targetPath, pathKind, normalizeRelativePath } = require("../filesystem/path-utils");
9
+ const { validateTemplateSnapshot } = require("../validation/snapshot");
10
+ const { validateTemplateMetadata } = require("../validation/metadata");
11
+ const {
12
+ parseRepositoryIdentity,
13
+ convertTemplateSourceToInstance,
14
+ } = require("../validation/identity");
15
+
16
+ const PACKAGE_ROOT = path.resolve(__dirname, "../..");
17
+ const PACKAGE_MANIFEST = JSON.parse(
18
+ fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8"),
19
+ );
20
+ const BUNDLED_TEMPLATE_ROOT = path.join(PACKAGE_ROOT, "template");
21
+ const BUNDLED_MANIFEST_PATH = path.join(PACKAGE_ROOT, "template.manifest.json");
22
+ const BUNDLED_SNAPSHOT_PATH = path.join(PACKAGE_ROOT, "template.snapshot.json");
23
+ const TEMPLATE_MANIFEST_TEXT = fs.readFileSync(BUNDLED_MANIFEST_PATH, "utf8");
24
+ const TEMPLATE_MANIFEST = JSON.parse(TEMPLATE_MANIFEST_TEXT);
25
+ const TEMPLATE_METADATA_FILENAME = ".yss-template.json";
26
+ const TEMPLATE_SOURCE = "github:iloveZzz/yss-spec-project-template";
27
+ const METADATA_SCHEMA_VERSION = 2;
28
+
29
+ const ROOT_EXCLUDED_ENTRIES = new Set(TEMPLATE_MANIFEST.excludeRootEntries);
30
+ const ROOT_EXCLUDED_FILES = new Set(TEMPLATE_MANIFEST.excludeRootFiles);
31
+ const EXCLUDED_RELATIVE_PATHS = new Set(TEMPLATE_MANIFEST.excludePaths);
32
+ const ALLOWED_RELATIVE_PATHS = new Set(TEMPLATE_MANIFEST.allowFiles || []);
33
+ const INIT_EXCLUDED_ROOT_ENTRIES = new Set(
34
+ TEMPLATE_MANIFEST.initExcludeRootEntries || [],
35
+ );
36
+ const INIT_EXCLUDED_ROOT_FILES = new Set(
37
+ TEMPLATE_MANIFEST.initExcludeRootFiles || [],
38
+ );
39
+ const INIT_EXCLUDED_RELATIVE_PATHS = new Set(
40
+ TEMPLATE_MANIFEST.initExcludePaths || [],
41
+ );
42
+ const RENDERED_RELATIVE_PATHS = new Set(TEMPLATE_MANIFEST.renderPaths);
43
+ const EXAMPLE_DOC_PATHS = new Set(TEMPLATE_MANIFEST.exampleDocPaths);
44
+
45
+ let bundledPathToLogicalPath = null;
46
+
47
+ function sha256(value) {
48
+ return crypto.createHash("sha256").update(value).digest("hex");
49
+ }
50
+
51
+ const TEMPLATE_MANIFEST_VERSION = sha256(TEMPLATE_MANIFEST_TEXT);
52
+
53
+ function nowIsoString() {
54
+ return new Date().toISOString();
55
+ }
56
+
57
+ function readTemplateSnapshot() {
58
+ if (!fs.existsSync(BUNDLED_SNAPSHOT_PATH)) {
59
+ throw new Error(
60
+ "缺少模板快照元数据,请先运行 npm run sync-template;正式发布不得使用浮动模板引用",
61
+ );
62
+ }
63
+
64
+ let snapshot;
65
+ try {
66
+ snapshot = JSON.parse(fs.readFileSync(BUNDLED_SNAPSHOT_PATH, "utf8"));
67
+ } catch (error) {
68
+ throw new Error(`模板快照元数据无法解析:${error.message}`);
69
+ }
70
+
71
+ return validateTemplateSnapshot(snapshot, {
72
+ manifestHash: TEMPLATE_MANIFEST_VERSION,
73
+ treeHash: treeHash(BUNDLED_TEMPLATE_ROOT),
74
+ });
75
+ }
76
+
77
+ function logicalTemplatePath(bundledPath) {
78
+ if (bundledPathToLogicalPath === null) {
79
+ const snapshot = readTemplateSnapshot();
80
+ bundledPathToLogicalPath = new Map(
81
+ Object.entries(snapshot.encodedPaths || {}).map(([logicalPath, encodedPath]) => [
82
+ normalizeRelativePath(encodedPath),
83
+ normalizeRelativePath(logicalPath),
84
+ ]),
85
+ );
86
+ }
87
+ return (
88
+ bundledPathToLogicalPath.get(normalizeRelativePath(bundledPath)) ||
89
+ normalizeRelativePath(bundledPath)
90
+ );
91
+ }
92
+
93
+ function shouldExcludeRelativePath(relativePath, mode = "managed") {
94
+ const normalized = normalizeRelativePath(relativePath);
95
+ if (
96
+ [...ALLOWED_RELATIVE_PATHS].some(
97
+ (allowedPath) =>
98
+ allowedPath === normalized || allowedPath.startsWith(`${normalized}/`),
99
+ )
100
+ ) {
101
+ return false;
102
+ }
103
+ return (
104
+ EXCLUDED_RELATIVE_PATHS.has(normalized) ||
105
+ (mode === "init" &&
106
+ (INIT_EXCLUDED_RELATIVE_PATHS.has(normalized) ||
107
+ INIT_EXCLUDED_ROOT_FILES.has(normalized)))
108
+ );
109
+ }
110
+
111
+ function shouldSkipRootEntry(entryName, mode = "managed") {
112
+ return (
113
+ ROOT_EXCLUDED_ENTRIES.has(entryName) ||
114
+ ROOT_EXCLUDED_FILES.has(entryName) ||
115
+ (mode === "init" &&
116
+ (INIT_EXCLUDED_ROOT_ENTRIES.has(entryName) ||
117
+ INIT_EXCLUDED_ROOT_FILES.has(entryName)))
118
+ );
119
+ }
120
+
121
+ function readTargetIdentity(targetDir) {
122
+ const identityPath = targetPath(targetDir, "yss-project.yaml");
123
+ const identityKind = pathKind(identityPath);
124
+ if (identityKind === "missing") {
125
+ return { state: "missing", path: identityPath, content: null };
126
+ }
127
+ if (identityKind !== "file") {
128
+ throw new Error("yss-project.yaml 不能是符号链接或其他特殊文件");
129
+ }
130
+
131
+ const content = fs.readFileSync(identityPath, "utf8");
132
+ return {
133
+ state: "valid",
134
+ path: identityPath,
135
+ content,
136
+ fields: parseRepositoryIdentity(content),
137
+ };
138
+ }
139
+
140
+ function renderTemplateFile(relativePath, content, variables) {
141
+ if (relativePath === "yss-project.yaml") {
142
+ return convertTemplateSourceToInstance(content);
143
+ }
144
+
145
+ if (relativePath === "AGENTS.md") {
146
+ return content
147
+ .replace(
148
+ /(\*\*项目名称:\*\*\s*)\[填写\]/,
149
+ (_, prefix) => `${prefix}${variables.projectName}`,
150
+ )
151
+ .replace(
152
+ /(\*\*业务领域:\*\*\s*)\[填写\]/,
153
+ (_, prefix) => `${prefix}${variables.businessDomain}`,
154
+ )
155
+ .replace(
156
+ /(\*\*团队规模:\*\*\s*)\[填写\]/,
157
+ (_, prefix) => `${prefix}${variables.teamSize}`,
158
+ );
159
+ }
160
+
161
+ if (relativePath === "README.md") {
162
+ const renderedContent = content
163
+ .replace(/^# YSS Spec Project Template/m, `# ${variables.projectName}`)
164
+ .replace(
165
+ /^> Matt Pocock Engineering Skills/m,
166
+ `> 默认 Issue Tracker:${variables.issueTracker}\n>\n> Matt Pocock Engineering Skills`,
167
+ );
168
+
169
+ if (!variables.includeExampleDocs) {
170
+ return renderedContent.replace(
171
+ /^\| \[docs\/discovery\/IDEATION\.md\]\(\.\/docs\/discovery\/IDEATION\.md\) \|.*\r?\n/m,
172
+ "",
173
+ );
174
+ }
175
+ return renderedContent;
176
+ }
177
+
178
+ return content;
179
+ }
180
+
181
+ function buildCopyPlan(
182
+ sourceDir,
183
+ targetDir,
184
+ variables,
185
+ relativeDir = "",
186
+ mode = "managed",
187
+ ) {
188
+ const operations = [];
189
+ const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
190
+
191
+ for (const entry of entries) {
192
+ if (!relativeDir && shouldSkipRootEntry(entry.name, mode)) continue;
193
+
194
+ const bundledRelativePath = relativeDir
195
+ ? path.posix.join(relativeDir, entry.name)
196
+ : entry.name;
197
+ const relativePath = logicalTemplatePath(bundledRelativePath);
198
+
199
+ if (shouldExcludeRelativePath(relativePath, mode)) continue;
200
+ if (!variables.includeExampleDocs && EXAMPLE_DOC_PATHS.has(relativePath)) continue;
201
+
202
+ const sourcePath = path.join(sourceDir, entry.name);
203
+ const targetPathValue = targetPath(targetDir, relativePath);
204
+
205
+ if (entry.isDirectory()) {
206
+ operations.push({ type: "mkdir", relativePath, targetPath: targetPathValue });
207
+ operations.push(
208
+ ...buildCopyPlan(
209
+ sourcePath,
210
+ targetDir,
211
+ variables,
212
+ bundledRelativePath,
213
+ mode,
214
+ ),
215
+ );
216
+ continue;
217
+ }
218
+ if (!entry.isFile()) continue;
219
+
220
+ operations.push({
221
+ type: RENDERED_RELATIVE_PATHS.has(relativePath) ? "render" : "copy",
222
+ relativePath,
223
+ sourcePath,
224
+ targetPath: targetPathValue,
225
+ });
226
+ }
227
+
228
+ return operations;
229
+ }
230
+
231
+ function fileHash(filePath) {
232
+ return sha256(fs.readFileSync(filePath));
233
+ }
234
+
235
+ function buildSyncVariables(metadata) {
236
+ const variables = metadata.variables || {};
237
+ return {
238
+ projectName: variables.projectName || "待补充",
239
+ businessDomain: variables.businessDomain || "待补充",
240
+ teamSize: variables.teamSize || "待补充",
241
+ issueTracker: variables.issueTracker || "github",
242
+ includeExampleDocs:
243
+ variables.includeExampleDocs === undefined
244
+ ? true
245
+ : Boolean(variables.includeExampleDocs),
246
+ };
247
+ }
248
+
249
+ function buildDesiredManagedFile(operation, variables) {
250
+ if (operation.type === "render") {
251
+ const desiredContent = renderTemplateFile(
252
+ operation.relativePath,
253
+ fs.readFileSync(operation.sourcePath, "utf8"),
254
+ variables,
255
+ );
256
+ return {
257
+ ...operation,
258
+ desiredContent,
259
+ desiredHash: sha256(desiredContent),
260
+ };
261
+ }
262
+
263
+ return {
264
+ ...operation,
265
+ desiredContent: fs.readFileSync(operation.sourcePath),
266
+ desiredHash: fileHash(operation.sourcePath),
267
+ };
268
+ }
269
+
270
+ function buildDesiredManagedOperations(targetDir, variables, mode = "managed") {
271
+ return buildCopyPlan(BUNDLED_TEMPLATE_ROOT, targetDir, variables, "", mode)
272
+ .filter((operation) => operation.type === "copy" || operation.type === "render")
273
+ .map((operation) => buildDesiredManagedFile(operation, variables));
274
+ }
275
+
276
+ function buildSyncDesiredOperations(targetDir, metadata, identity) {
277
+ const variables = buildSyncVariables(metadata);
278
+ return buildDesiredManagedOperations(targetDir, variables, "init").map((operation) => {
279
+ if (
280
+ operation.relativePath !== "yss-project.yaml" ||
281
+ identity.state !== "valid" ||
282
+ identity.fields.repository_mode !== "template-source"
283
+ ) {
284
+ return operation;
285
+ }
286
+
287
+ const desiredContent = convertTemplateSourceToInstance(identity.content);
288
+ return {
289
+ ...operation,
290
+ desiredContent,
291
+ desiredHash: sha256(desiredContent),
292
+ identityConversion: true,
293
+ identitySourceHash: sha256(identity.content),
294
+ };
295
+ });
296
+ }
297
+
298
+ function buildAttachDesiredOperations(targetDir, variables, identity) {
299
+ return buildDesiredManagedOperations(targetDir, variables, "managed").map((operation) => {
300
+ if (operation.relativePath !== "yss-project.yaml" || identity.state === "missing") {
301
+ return operation;
302
+ }
303
+
304
+ if (identity.fields.repository_mode === "project-instance") {
305
+ return {
306
+ ...operation,
307
+ desiredContent: identity.content,
308
+ desiredHash: sha256(identity.content),
309
+ identityPreserved: true,
310
+ };
311
+ }
312
+
313
+ const desiredContent = convertTemplateSourceToInstance(identity.content);
314
+ return {
315
+ ...operation,
316
+ desiredContent,
317
+ desiredHash: sha256(desiredContent),
318
+ identityConversion: true,
319
+ };
320
+ });
321
+ }
322
+
323
+ function loadTemplateMetadata(targetDir) {
324
+ const metadataPath = targetPath(targetDir, TEMPLATE_METADATA_FILENAME);
325
+ const metadataKind = pathKind(metadataPath);
326
+ if (metadataKind === "missing") {
327
+ throw new Error(
328
+ `当前目录不是受支持的模板实例仓库,缺少模板元数据文件 ${TEMPLATE_METADATA_FILENAME};请先使用 attach`,
329
+ );
330
+ }
331
+ if (metadataKind !== "file") {
332
+ throw new Error("模板元数据不能是符号链接或其他特殊文件");
333
+ }
334
+
335
+ let metadata;
336
+ try {
337
+ metadata = JSON.parse(fs.readFileSync(metadataPath, "utf8"));
338
+ } catch (error) {
339
+ throw new Error(`模板元数据无法解析:${error.message}`);
340
+ }
341
+
342
+ validateTemplateMetadata(metadata, {
343
+ currentSchemaVersion: METADATA_SCHEMA_VERSION,
344
+ templateName: PACKAGE_MANIFEST.name,
345
+ templateSource: TEMPLATE_SOURCE,
346
+ });
347
+
348
+ return { metadataPath, metadata };
349
+ }
350
+
351
+ function collectManagedFiles(desiredOperations) {
352
+ const managedFiles = {};
353
+ for (const operation of desiredOperations) {
354
+ if (pathKind(operation.targetPath) !== "file") continue;
355
+ managedFiles[operation.relativePath] = {
356
+ type: operation.type,
357
+ contentHash: fileHash(operation.targetPath),
358
+ };
359
+ }
360
+ return managedFiles;
361
+ }
362
+
363
+ function buildMetadata(variables, desiredOperations, timestamp = nowIsoString()) {
364
+ return {
365
+ metadataSchemaVersion: METADATA_SCHEMA_VERSION,
366
+ templateName: PACKAGE_MANIFEST.name,
367
+ cliVersion: PACKAGE_MANIFEST.version,
368
+ templateVersion: PACKAGE_MANIFEST.version,
369
+ templateSource: TEMPLATE_SOURCE,
370
+ templateCommit: readTemplateSnapshot().templateCommit,
371
+ initializedAt: timestamp,
372
+ lastSyncedAt: timestamp,
373
+ managedFilesManifestVersion: TEMPLATE_MANIFEST_VERSION,
374
+ variables: {
375
+ projectName: variables.projectName,
376
+ businessDomain: variables.businessDomain,
377
+ teamSize: variables.teamSize,
378
+ issueTracker: variables.issueTracker,
379
+ includeExampleDocs: variables.includeExampleDocs,
380
+ },
381
+ managedFiles: collectManagedFiles(desiredOperations),
382
+ };
383
+ }
384
+
385
+ function writeTemplateMetadata(targetDir, metadata, transaction = null) {
386
+ const metadataPath = targetPath(targetDir, TEMPLATE_METADATA_FILENAME);
387
+ const content = `${JSON.stringify(metadata, null, 2)}\n`;
388
+ if (transaction) {
389
+ transaction.writeFile(metadataPath, content);
390
+ return;
391
+ }
392
+ fs.writeFileSync(metadataPath, content, "utf8");
393
+ }
394
+
395
+ function buildNextSyncMetadata(metadata, syncPlan) {
396
+ const nextManagedFiles = { ...(metadata.managedFiles || {}) };
397
+ for (const relativePath of syncPlan.removed) {
398
+ delete nextManagedFiles[relativePath];
399
+ }
400
+ for (const operation of syncPlan.desiredOperations) {
401
+ if (pathKind(operation.targetPath) !== "file") continue;
402
+ const currentHash = fileHash(operation.targetPath);
403
+ if (currentHash === operation.desiredHash) {
404
+ nextManagedFiles[operation.relativePath] = {
405
+ type: operation.type,
406
+ contentHash: operation.desiredHash,
407
+ };
408
+ }
409
+ }
410
+
411
+ return {
412
+ ...metadata,
413
+ metadataSchemaVersion: METADATA_SCHEMA_VERSION,
414
+ templateName: PACKAGE_MANIFEST.name,
415
+ cliVersion: PACKAGE_MANIFEST.version,
416
+ templateVersion: PACKAGE_MANIFEST.version,
417
+ templateSource: TEMPLATE_SOURCE,
418
+ templateCommit: readTemplateSnapshot().templateCommit,
419
+ lastSyncedAt: nowIsoString(),
420
+ managedFilesManifestVersion: TEMPLATE_MANIFEST_VERSION,
421
+ managedFiles: nextManagedFiles,
422
+ };
423
+ }
424
+
425
+ function verifyGeneratedSyncInstance(targetDir) {
426
+ const identity = readTargetIdentity(targetDir);
427
+ if (
428
+ identity.state !== "valid" ||
429
+ identity.fields.repository_mode !== "project-instance"
430
+ ) {
431
+ throw new Error("初始化结果的 yss-project.yaml 必须是 project-instance");
432
+ }
433
+
434
+ const agentsContent = fs.readFileSync(targetPath(targetDir, "AGENTS.md"), "utf8");
435
+ const readmeContent = fs.readFileSync(targetPath(targetDir, "README.md"), "utf8");
436
+ if (agentsContent.includes("[填写]") || readmeContent.includes("[填写]")) {
437
+ throw new Error("初始化结果仍包含模板占位信息");
438
+ }
439
+ }
440
+
441
+ module.exports = {
442
+ PACKAGE_ROOT,
443
+ PACKAGE_MANIFEST,
444
+ BUNDLED_TEMPLATE_ROOT,
445
+ TEMPLATE_MANIFEST,
446
+ TEMPLATE_MANIFEST_VERSION,
447
+ TEMPLATE_METADATA_FILENAME,
448
+ TEMPLATE_SOURCE,
449
+ METADATA_SCHEMA_VERSION,
450
+ sha256,
451
+ fileHash,
452
+ readTemplateSnapshot,
453
+ readTargetIdentity,
454
+ buildSyncVariables,
455
+ buildDesiredManagedOperations,
456
+ buildSyncDesiredOperations,
457
+ buildAttachDesiredOperations,
458
+ loadTemplateMetadata,
459
+ buildMetadata,
460
+ writeTemplateMetadata,
461
+ buildNextSyncMetadata,
462
+ verifyGeneratedSyncInstance,
463
+ };
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ const crypto = require("node:crypto");
4
+ const {
5
+ CUSTOMIZATION_POLICY,
6
+ GENERATOR_POLICY,
7
+ applyLifecycleToOperations,
8
+ } = require("./lifecycle-runtime");
9
+
10
+ function policyHash(policy) {
11
+ return crypto.createHash("sha256").update(JSON.stringify(policy)).digest("hex");
12
+ }
13
+
14
+ function decorateMetadataLifecycle(metadata) {
15
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
16
+ throw new TypeError("metadata must be an object");
17
+ }
18
+
19
+ const managedFiles = Object.fromEntries(
20
+ Object.entries(metadata.managedFiles || {}).map(([relativePath, record]) => {
21
+ const [operation] = applyLifecycleToOperations([
22
+ {
23
+ relativePath,
24
+ ownership: record?.ownership,
25
+ },
26
+ ]);
27
+ const next = { ...record };
28
+ if (operation?.mergeStrategy) next.mergeStrategy = operation.mergeStrategy;
29
+ if (operation?.generatorId) next.generatorId = operation.generatorId;
30
+ if (operation?.generatorVersion) {
31
+ next.generatorVersion = operation.generatorVersion;
32
+ }
33
+ return [relativePath, next];
34
+ }),
35
+ );
36
+
37
+ return {
38
+ ...metadata,
39
+ customizationPolicyVersion: CUSTOMIZATION_POLICY.version,
40
+ customizationPolicyHash: policyHash(CUSTOMIZATION_POLICY),
41
+ generatorPolicyVersion: GENERATOR_POLICY.version,
42
+ generatorPolicyHash: policyHash(GENERATOR_POLICY),
43
+ managedFiles,
44
+ };
45
+ }
46
+
47
+ function lifecyclePolicyDrift(metadata) {
48
+ const customizationExpected = {
49
+ version: CUSTOMIZATION_POLICY.version,
50
+ hash: policyHash(CUSTOMIZATION_POLICY),
51
+ };
52
+ const generatorExpected = {
53
+ version: GENERATOR_POLICY.version,
54
+ hash: policyHash(GENERATOR_POLICY),
55
+ };
56
+
57
+ const customizationState =
58
+ metadata?.customizationPolicyVersion === undefined ||
59
+ metadata?.customizationPolicyHash === undefined
60
+ ? "missing"
61
+ : metadata.customizationPolicyVersion !== customizationExpected.version ||
62
+ metadata.customizationPolicyHash !== customizationExpected.hash
63
+ ? "drift"
64
+ : "matched";
65
+
66
+ const generatorState =
67
+ metadata?.generatorPolicyVersion === undefined ||
68
+ metadata?.generatorPolicyHash === undefined
69
+ ? "missing"
70
+ : metadata.generatorPolicyVersion !== generatorExpected.version ||
71
+ metadata.generatorPolicyHash !== generatorExpected.hash
72
+ ? "drift"
73
+ : "matched";
74
+
75
+ return {
76
+ customization: {
77
+ state: customizationState,
78
+ expected: customizationExpected,
79
+ actual: {
80
+ version: metadata?.customizationPolicyVersion ?? null,
81
+ hash: metadata?.customizationPolicyHash ?? null,
82
+ },
83
+ },
84
+ generator: {
85
+ state: generatorState,
86
+ expected: generatorExpected,
87
+ actual: {
88
+ version: metadata?.generatorPolicyVersion ?? null,
89
+ hash: metadata?.generatorPolicyHash ?? null,
90
+ },
91
+ },
92
+ };
93
+ }
94
+
95
+ module.exports = {
96
+ policyHash,
97
+ decorateMetadataLifecycle,
98
+ lifecyclePolicyDrift,
99
+ };
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+
3
+ const {
4
+ validateOwnershipPattern,
5
+ matchesOwnershipPattern,
6
+ } = require("./ownership-policy");
7
+
8
+ const CUSTOMIZATION_POLICY_VERSION = 1;
9
+ const CUSTOMIZATION_STRATEGIES = Object.freeze([
10
+ "replace-with-force",
11
+ "manual",
12
+ ]);
13
+ const GENERATOR_POLICY_VERSION = 1;
14
+
15
+ function validateRulePattern(pattern) {
16
+ return validateOwnershipPattern(pattern);
17
+ }
18
+
19
+ function validateCustomizationPolicy(policy) {
20
+ if (!policy || typeof policy !== "object" || Array.isArray(policy)) {
21
+ throw new Error("customizationPolicy 必须是 JSON 对象");
22
+ }
23
+ if (policy.version !== CUSTOMIZATION_POLICY_VERSION) {
24
+ throw new Error(`不支持的 customizationPolicy version:${policy.version}`);
25
+ }
26
+ if (!CUSTOMIZATION_STRATEGIES.includes(policy.defaultStrategy)) {
27
+ throw new Error(`customizationPolicy defaultStrategy 非法:${policy.defaultStrategy}`);
28
+ }
29
+ if (!Array.isArray(policy.rules)) {
30
+ throw new Error("customizationPolicy rules 必须是数组");
31
+ }
32
+
33
+ const seen = new Set();
34
+ const rules = policy.rules.map((rule, index) => {
35
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) {
36
+ throw new Error(`customizationPolicy rules[${index}] 必须是对象`);
37
+ }
38
+ const pattern = validateRulePattern(rule.pattern);
39
+ if (seen.has(pattern)) {
40
+ throw new Error(`customizationPolicy pattern 重复:${pattern}`);
41
+ }
42
+ seen.add(pattern);
43
+ if (!CUSTOMIZATION_STRATEGIES.includes(rule.strategy)) {
44
+ throw new Error(
45
+ `customizationPolicy rules[${index}].strategy 非法:${rule.strategy}`,
46
+ );
47
+ }
48
+ return { pattern, strategy: rule.strategy };
49
+ });
50
+
51
+ return {
52
+ version: CUSTOMIZATION_POLICY_VERSION,
53
+ defaultStrategy: policy.defaultStrategy,
54
+ rules,
55
+ };
56
+ }
57
+
58
+ function resolveCustomizationStrategy(relativePath, policy) {
59
+ const validated = validateCustomizationPolicy(policy);
60
+ for (const rule of validated.rules) {
61
+ if (matchesOwnershipPattern(relativePath, rule.pattern)) {
62
+ return rule.strategy;
63
+ }
64
+ }
65
+ return validated.defaultStrategy;
66
+ }
67
+
68
+ function validateGeneratorPolicy(policy) {
69
+ if (!policy || typeof policy !== "object" || Array.isArray(policy)) {
70
+ throw new Error("generatorPolicy 必须是 JSON 对象");
71
+ }
72
+ if (policy.version !== GENERATOR_POLICY_VERSION) {
73
+ throw new Error(`不支持的 generatorPolicy version:${policy.version}`);
74
+ }
75
+ if (!Array.isArray(policy.rules)) {
76
+ throw new Error("generatorPolicy rules 必须是数组");
77
+ }
78
+
79
+ const seen = new Set();
80
+ const rules = policy.rules.map((rule, index) => {
81
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) {
82
+ throw new Error(`generatorPolicy rules[${index}] 必须是对象`);
83
+ }
84
+ const pattern = validateRulePattern(rule.pattern);
85
+ if (seen.has(pattern)) {
86
+ throw new Error(`generatorPolicy pattern 重复:${pattern}`);
87
+ }
88
+ seen.add(pattern);
89
+ if (!/^[a-z][a-z0-9-]*$/.test(rule.generatorId || "")) {
90
+ throw new Error(`generatorPolicy rules[${index}].generatorId 非法`);
91
+ }
92
+ if (!Number.isInteger(rule.generatorVersion) || rule.generatorVersion < 1) {
93
+ throw new Error(`generatorPolicy rules[${index}].generatorVersion 必须是正整数`);
94
+ }
95
+ return {
96
+ pattern,
97
+ generatorId: rule.generatorId,
98
+ generatorVersion: rule.generatorVersion,
99
+ };
100
+ });
101
+
102
+ return {
103
+ version: GENERATOR_POLICY_VERSION,
104
+ rules,
105
+ };
106
+ }
107
+
108
+ function resolveGenerator(relativePath, policy) {
109
+ const validated = validateGeneratorPolicy(policy);
110
+ for (const rule of validated.rules) {
111
+ if (matchesOwnershipPattern(relativePath, rule.pattern)) {
112
+ return {
113
+ generatorId: rule.generatorId,
114
+ generatorVersion: rule.generatorVersion,
115
+ };
116
+ }
117
+ }
118
+ return null;
119
+ }
120
+
121
+ module.exports = {
122
+ CUSTOMIZATION_POLICY_VERSION,
123
+ CUSTOMIZATION_STRATEGIES,
124
+ GENERATOR_POLICY_VERSION,
125
+ validateCustomizationPolicy,
126
+ resolveCustomizationStrategy,
127
+ validateGeneratorPolicy,
128
+ resolveGenerator,
129
+ };