create-yss-spec 3.3.6 → 3.3.8

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 (222) hide show
  1. package/README.md +12 -13
  2. package/package.json +1 -1
  3. package/src/template/instance-runtime.js +8 -2
  4. package/src/validation/snapshot.js +7 -0
  5. package/template/.agents/skills/.strategic-design-skills-manifest.json +6 -4
  6. package/template/.agents/skills/archify/SKILL.md +1 -1
  7. package/template/.agents/skills/implementation-repo-onboarding/SKILL.md +2 -2
  8. package/template/.agents/skills/yss-application/SKILL.md +1 -1
  9. package/template/.agents/skills/yss-application/references/profiles/existing-domain-driven-maven.md +5 -0
  10. package/template/.agents/skills/yss-application/references/profiles/existing-layered-mvc-maven.md +5 -0
  11. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -2
  12. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +4 -0
  13. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +6 -1
  14. package/template/.agents/skills/yss-prototype-stage/SKILL.md +10 -1
  15. package/template/.agents/skills/yss-stage-decision/SKILL.md +13 -4
  16. package/template/.agents/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
  17. package/template/.agents/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
  18. package/template/.agents/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
  19. package/template/.agents/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
  20. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
  21. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
  22. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
  23. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
  24. package/template/.agents/skills/yss-stage-decision/references/validation-rules.md +2 -2
  25. package/template/.agents/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
  26. package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
  27. package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
  28. package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
  29. package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
  30. package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
  31. package/template/.agents/skills/yss-technical-design/SKILL.md +2 -0
  32. package/template/.agents/skills/yss-technical-design/references/engineering-design.schema.json +224 -0
  33. package/template/.agents/skills/yss-technical-design/references/technical-design-common.schema.json +6 -0
  34. package/template/.agents/skills/yss-technical-design/references/technical-design.schema.json +288 -10
  35. package/template/.agents/skills/yss-technical-design/scripts/engineering-design.mjs +30 -0
  36. package/template/.agents/skills/yss-technical-design/scripts/generate-schema.mjs +2 -1
  37. package/template/.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs +20 -8
  38. package/template/.agents/skills/yss-technical-design/tests/engineering-scenarios.mjs +12 -0
  39. package/template/.agents/skills/yss-web-controller/SKILL.md +1 -1
  40. package/template/.agents/skills/yss-web-controller/references/profiles/existing-domain-driven-maven.md +5 -0
  41. package/template/.agents/skills/yss-web-controller/references/profiles/existing-layered-mvc-maven.md +5 -0
  42. package/template/.codex/skills/archify/SKILL.md +1 -1
  43. package/template/.codex/skills/implementation-repo-onboarding/SKILL.md +2 -2
  44. package/template/.codex/skills/yss-application/SKILL.md +1 -1
  45. package/template/.codex/skills/yss-application/references/profiles/existing-domain-driven-maven.md +5 -0
  46. package/template/.codex/skills/yss-application/references/profiles/existing-layered-mvc-maven.md +5 -0
  47. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -2
  48. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +4 -0
  49. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +6 -1
  50. package/template/.codex/skills/yss-prototype-stage/SKILL.md +10 -1
  51. package/template/.codex/skills/yss-stage-decision/SKILL.md +13 -4
  52. package/template/.codex/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
  53. package/template/.codex/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
  54. package/template/.codex/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
  55. package/template/.codex/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
  56. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
  57. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
  58. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
  59. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
  60. package/template/.codex/skills/yss-stage-decision/references/validation-rules.md +2 -2
  61. package/template/.codex/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
  62. package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
  63. package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
  64. package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
  65. package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
  66. package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
  67. package/template/.codex/skills/yss-technical-design/SKILL.md +2 -0
  68. package/template/.codex/skills/yss-technical-design/references/engineering-design.schema.json +224 -0
  69. package/template/.codex/skills/yss-technical-design/references/technical-design-common.schema.json +6 -0
  70. package/template/.codex/skills/yss-technical-design/references/technical-design.schema.json +288 -10
  71. package/template/.codex/skills/yss-technical-design/scripts/engineering-design.mjs +30 -0
  72. package/template/.codex/skills/yss-technical-design/scripts/generate-schema.mjs +2 -1
  73. package/template/.codex/skills/yss-technical-design/scripts/validate-technical-design.mjs +20 -8
  74. package/template/.codex/skills/yss-technical-design/tests/engineering-scenarios.mjs +12 -0
  75. package/template/.codex/skills/yss-web-controller/SKILL.md +1 -1
  76. package/template/.codex/skills/yss-web-controller/references/profiles/existing-domain-driven-maven.md +5 -0
  77. package/template/.codex/skills/yss-web-controller/references/profiles/existing-layered-mvc-maven.md +5 -0
  78. package/template/.cursor/skills/archify/SKILL.md +1 -1
  79. package/template/.cursor/skills/implementation-repo-onboarding/SKILL.md +2 -2
  80. package/template/.cursor/skills/yss-application/SKILL.md +1 -1
  81. package/template/.cursor/skills/yss-application/references/profiles/existing-domain-driven-maven.md +5 -0
  82. package/template/.cursor/skills/yss-application/references/profiles/existing-layered-mvc-maven.md +5 -0
  83. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -2
  84. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +4 -0
  85. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +6 -1
  86. package/template/.cursor/skills/yss-prototype-stage/SKILL.md +10 -1
  87. package/template/.cursor/skills/yss-stage-decision/SKILL.md +13 -4
  88. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
  89. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
  90. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
  91. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
  92. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
  93. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
  94. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
  95. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
  96. package/template/.cursor/skills/yss-stage-decision/references/validation-rules.md +2 -2
  97. package/template/.cursor/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
  98. package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
  99. package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
  100. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
  101. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
  102. package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
  103. package/template/.cursor/skills/yss-technical-design/SKILL.md +2 -0
  104. package/template/.cursor/skills/yss-technical-design/references/engineering-design.schema.json +224 -0
  105. package/template/.cursor/skills/yss-technical-design/references/technical-design-common.schema.json +6 -0
  106. package/template/.cursor/skills/yss-technical-design/references/technical-design.schema.json +288 -10
  107. package/template/.cursor/skills/yss-technical-design/scripts/engineering-design.mjs +30 -0
  108. package/template/.cursor/skills/yss-technical-design/scripts/generate-schema.mjs +2 -1
  109. package/template/.cursor/skills/yss-technical-design/scripts/validate-technical-design.mjs +20 -8
  110. package/template/.cursor/skills/yss-technical-design/tests/engineering-scenarios.mjs +12 -0
  111. package/template/.cursor/skills/yss-web-controller/SKILL.md +1 -1
  112. package/template/.cursor/skills/yss-web-controller/references/profiles/existing-domain-driven-maven.md +5 -0
  113. package/template/.cursor/skills/yss-web-controller/references/profiles/existing-layered-mvc-maven.md +5 -0
  114. package/template/.pi/skills/archify/SKILL.md +1 -1
  115. package/template/.pi/skills/implementation-repo-onboarding/SKILL.md +2 -2
  116. package/template/.pi/skills/yss-application/SKILL.md +1 -1
  117. package/template/.pi/skills/yss-application/references/profiles/existing-domain-driven-maven.md +5 -0
  118. package/template/.pi/skills/yss-application/references/profiles/existing-layered-mvc-maven.md +5 -0
  119. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -2
  120. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +4 -0
  121. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +6 -1
  122. package/template/.pi/skills/yss-prototype-stage/SKILL.md +10 -1
  123. package/template/.pi/skills/yss-stage-decision/SKILL.md +13 -4
  124. package/template/.pi/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
  125. package/template/.pi/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
  126. package/template/.pi/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
  127. package/template/.pi/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
  128. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
  129. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
  130. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
  131. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
  132. package/template/.pi/skills/yss-stage-decision/references/validation-rules.md +2 -2
  133. package/template/.pi/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
  134. package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
  135. package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
  136. package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
  137. package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
  138. package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
  139. package/template/.pi/skills/yss-technical-design/SKILL.md +2 -0
  140. package/template/.pi/skills/yss-technical-design/references/engineering-design.schema.json +224 -0
  141. package/template/.pi/skills/yss-technical-design/references/technical-design-common.schema.json +6 -0
  142. package/template/.pi/skills/yss-technical-design/references/technical-design.schema.json +288 -10
  143. package/template/.pi/skills/yss-technical-design/scripts/engineering-design.mjs +30 -0
  144. package/template/.pi/skills/yss-technical-design/scripts/generate-schema.mjs +2 -1
  145. package/template/.pi/skills/yss-technical-design/scripts/validate-technical-design.mjs +20 -8
  146. package/template/.pi/skills/yss-technical-design/tests/engineering-scenarios.mjs +12 -0
  147. package/template/.pi/skills/yss-web-controller/SKILL.md +1 -1
  148. package/template/.pi/skills/yss-web-controller/references/profiles/existing-domain-driven-maven.md +5 -0
  149. package/template/.pi/skills/yss-web-controller/references/profiles/existing-layered-mvc-maven.md +5 -0
  150. package/template/README.md +4 -6
  151. package/template/docs/agents/backend-architecture-profiles.md +4 -0
  152. package/template/docs/agents/yss-skill-registry.yaml +13 -0
  153. package/template/docs/design/templates/prototype-confirmation-template.md +2 -2
  154. package/template/docs/design/templates/prototype-review-checklist.md +2 -2
  155. package/template/docs/plan/templates/market-analysis-template.md +3 -3
  156. package/template/docs/plan/templates/user-pain-points-template.md +3 -3
  157. package/template/docs/process/delivery-preflight.md +72 -0
  158. package/template/docs/process/document-writing.md +5 -1
  159. package/template/docs/process/existing-backend-architecture.md +31 -0
  160. package/template/docs/process/existing-ui-baseline.md +40 -0
  161. package/template/docs/process/frontend-backend-delivery.md +21 -5
  162. package/template/docs/process/implementation-repo-integration.md +6 -0
  163. package/template/docs/process/schemas/backend-architecture-identity.schema.json +111 -14
  164. package/template/docs/process/schemas/backend-delivery.schema.json +4 -0
  165. package/template/docs/process/schemas/delivery-preflight-input.schema.json +158 -0
  166. package/template/docs/process/schemas/delivery-preflight-result.schema.json +159 -0
  167. package/template/docs/process/schemas/digital-human-task-package.schema.json +1 -1
  168. package/template/docs/process/schemas/existing-ui-baseline.schema.json +365 -0
  169. package/template/docs/process/schemas/frontend-delivery-acceptance-v1.schema.json +200 -0
  170. package/template/docs/process/schemas/frontend-delivery-acceptance-v3.schema.json +308 -0
  171. package/template/docs/process/schemas/frontend-delivery-acceptance.schema.json +37 -3
  172. package/template/docs/process/schemas/frontend-strategic-preflight-v2.schema.json +114 -0
  173. package/template/docs/process/schemas/frontend-strategic-preflight.schema.json +32 -0
  174. package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +2 -1
  175. package/template/docs/process/schemas/strategic-consumer-feedback.schema.json +40 -0
  176. package/template/docs/process/schemas/strategic-design-handoff-v3.schema.json +743 -0
  177. package/template/docs/process/schemas/strategic-design-handoff-v4.schema.json +712 -0
  178. package/template/docs/process/schemas/strategic-design-handoff-v5.schema.json +932 -0
  179. package/template/docs/process/schemas/strategic-feedback-adjudication.schema.json +42 -0
  180. package/template/docs/process/schemas/strategic-handoff-domain-strategy-v3.schema.json +216 -0
  181. package/template/docs/process/schemas/strategic-handoff-export-v2.schema.json +378 -0
  182. package/template/docs/process/schemas/strategic-handoff-import-receipt.schema.json +39 -0
  183. package/template/docs/process/schemas/strategic-handoff-stage-decision-package-v3.schema.json +148 -0
  184. package/template/docs/process/strategic-handoff-package.md +51 -10
  185. package/template/docs/process/templates/lifecycle-checkpoint-template.yaml +1 -0
  186. package/template/docs/process/templates/stage-checkpoint-template.md +1 -0
  187. package/template/docs/user-guide//346/212/200/346/234/257/350/256/276/350/256/241/347/224/250/346/210/267/346/214/207/345/215/227.md +43 -0
  188. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +83 -67
  189. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +9 -10
  190. 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 +12 -9
  191. package/template/scripts/backend-delivery +20 -4
  192. package/template/scripts/lib/approval-record.mjs +2 -1
  193. package/template/scripts/lib/approved-execution-context.mjs +106 -0
  194. package/template/scripts/lib/backend-architecture.mjs +12 -0
  195. package/template/scripts/lib/backend-delivery.mjs +21 -5
  196. package/template/scripts/lib/delivery-preflight.mjs +238 -0
  197. package/template/scripts/lib/existing-backend-architecture.mjs +139 -0
  198. package/template/scripts/lib/existing-ui-baseline.mjs +84 -0
  199. package/template/scripts/lib/frontend-delivery-boundary.mjs +20 -4
  200. package/template/scripts/lib/frontend-delivery.mjs +76 -4
  201. package/template/scripts/lib/implementation-contract-compiler.mjs +19 -5
  202. package/template/scripts/lib/json-schema.mjs +19 -4
  203. package/template/scripts/lib/profile-skill-sync.mjs +26 -0
  204. package/template/scripts/lib/strategic-feedback.mjs +33 -0
  205. package/template/scripts/lib/strategic-handoff-consumption.mjs +11 -5
  206. package/template/scripts/lib/strategic-handoff-design-targets.mjs +2 -3
  207. package/template/scripts/lib/strategic-handoff-io.mjs +11 -0
  208. package/template/scripts/lib/strategic-handoff.mjs +143 -34
  209. package/template/scripts/lib/task-package.mjs +11 -3
  210. package/template/scripts/lib/technical-design-boundary.mjs +9 -1
  211. package/template/scripts/lib/ui-baseline.mjs +26 -0
  212. package/template/scripts/lib/user-decision-reuse.mjs +38 -0
  213. package/template/scripts/lib/user-decision.mjs +4 -1
  214. package/template/scripts/preflight-delivery +14 -0
  215. package/template/scripts/strategic-feedback +10 -0
  216. package/template/scripts/strategic-handoff +19 -4
  217. package/template/scripts/sync-profile-skills +4 -5
  218. package/template/scripts/verify-frontend-strategic-preflight +12 -0
  219. package/template/skills-lock.json +18 -18
  220. package/template.manifest.json +1 -0
  221. package/template.snapshot.json +6 -5
  222. 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 +0 -35
@@ -1,4 +1,4 @@
1
- # 内部设备借用:贯穿五类项目的教学案例
1
+ # 内部设备借用:贯穿四个现行家族的教学案例
2
2
 
3
3
  本文是虚构教学说明,不是已批准 Spec、真实交付包或可直接执行的产品合同。示例路径、规则和标识由学习者在独立 `project-instance` 中确认后落盘;模板源不登记这些业务词汇。
4
4
 
@@ -17,7 +17,7 @@
17
17
 
18
18
  ## 1. 选择工作组织
19
19
 
20
- 综合模板可承载从澄清到业务验收的管理资产。战略模板只负责上游方案;研发既可以交给通用研发模板,也可以交给后端/前端专职模板。后两者必须登记统一管理方及独立实现仓,不把源代码复制进模板。
20
+ 综合模板可承载从澄清到业务验收的管理资产。战略模板只负责上游方案,后端和前端模板按交付协议接力;专职路线必须登记统一管理方及独立实现仓,不把源代码复制进模板。既有通用研发实例只按原固定版本维护,不作为本案例的新建路线。
21
21
 
22
22
  ## 2. 战略方形成可交接输入
23
23
 
@@ -29,18 +29,19 @@
29
29
  列出准备交接的规则、关键场景、版本、批准证据和研发待决事项。
30
30
  ```
31
31
 
32
- 用户逐项核对业务和页面。预期输出为本仓合同要求的已批准战略资产、根词汇、交接描述及证据闭包,不能只交聊天摘要。战略方不在此生成后端工程或冻结工程 API。
32
+ 用户逐项核对业务和页面。预期输出为本仓合同要求的已批准战略资产、根词汇、交接描述及证据闭包,不能只交聊天摘要。战略方不在此生成后端工程或冻结工程 API。UI 有变化时交付已批准原型;确认无 UI 变化的既有工程使用 `existing-ui-baseline`,不能用截图把既有界面伪装成原型。
33
33
 
34
34
  由 Agent 给出实际交接描述路径后,在战略实例执行以下命令。尖括号必须替换为真实路径,输出目录须新建;`--handoff` 是相对源实例的文件路径。
35
35
 
36
36
  ```bash
37
+ node scripts/preflight-delivery --input <战略预检输入.json> --stage export --json
37
38
  node scripts/strategic-handoff export --source-root . --handoff <交接描述相对路径> --output <新包目录> --zip
38
39
  node scripts/strategic-handoff verify --bundle <包目录或ZIP>
39
40
  ```
40
41
 
41
- 预期验包输出 `result: verified` 及包摘要;缺证据或被修改的包会失败。把完整目录/ZIP交给下游,保留版本与摘要,不手改包内文件。
42
+ 预检输入使用 `delivery_kind: strategic-handoff`,绑定交接描述和当前 UI 基线。预期验包输出 `result: verified` 及包摘要;缺证据或被修改的包会失败。把完整目录/ZIP 交给下游,保留版本与摘要,不手改包内文件。
42
43
 
43
- ## 3. 后端或通用研发接收
44
+ ## 3. 后端接收
44
45
 
45
46
  在接收实例运行:
46
47
 
@@ -52,11 +53,11 @@ node scripts/strategic-handoff import --bundle <包目录或ZIP> --target-root .
52
53
  ```text
53
54
  请读取导入收据和包内规则/场景,核对本仓根 CONTEXT.md 并形成正式对账。
54
55
  为“已批准申请领用设备”逐项登记承接、冲突和依赖。
55
- 按当前流程准备战术设计、API Draft/Freeze 和后端 Slice Contract,
56
+ 按当前流程核对既有工程身份,准备技术设计、API Draft/Freeze 和后端 Slice Contract,
56
57
  登记真实实现仓、验证命令和回滚点。合同获准前只准备材料,不写代码。
57
58
  ```
58
59
 
59
- 预期保存导入收据、目标词汇对账、规则/场景承接与当前合同;导入成功不会自动批准这些资产。设备占用竞争应落实到设计与可执行测试,不能只在页面禁用按钮。API 变化回到冻结流程。后端专职只承担后端;通用研发按本仓合同承担实际受影响的两端工作。
60
+ 预期保存导入收据、目标词汇对账、规则/场景承接与当前合同;导入成功不会自动批准这些资产。既有 Java/Maven 工程需要 repository registration、engineering baseline 和 observation manifest 三份当前原始证据。设备占用竞争应落实到设计与可执行测试,不能只在页面禁用按钮;API 变化回到冻结流程。
60
61
 
61
62
  ## 4. 后端交付当前切片
62
63
 
@@ -70,11 +71,12 @@ node scripts/strategic-handoff import --bundle <包目录或ZIP> --target-root .
70
71
  在后端实例执行(交付描述已由实际证据形成):
71
72
 
72
73
  ```bash
74
+ node scripts/preflight-delivery --input <后端预检输入.json> --stage export --json
73
75
  node scripts/backend-delivery export --source-root . --delivery <交付描述相对路径> --output <新交付包目录> --zip
74
76
  node scripts/backend-delivery verify --bundle <交付包目录或ZIP>
75
77
  ```
76
78
 
77
- 预期 `verified`,但 `live_service_checked: false`:这里只验证快照,不能证明环境当前可用。交付包含战略绑定、冻结接口、批准合同、构建部署及成功/失败验证;不把账号密码装入包。实际 API 和版本探测由已登记环境提供,不凭手册假设已有固定端点。
79
+ 后端预检输入使用 `delivery_kind: backend-delivery`,绑定工程身份、技术设计、冻结接口、批准合同、实际制品、部署证据和后端交付源记录。预期 `verified`,但离线验包不能证明环境当前可用。交付不包含账号密码;实际 API 和版本探测由已登记环境提供。
78
80
 
79
81
  ## 5. 前端联合接收与准备
80
82
 
@@ -82,6 +84,7 @@ node scripts/backend-delivery verify --bundle <交付包目录或ZIP>
82
84
 
83
85
  ```bash
84
86
  node scripts/backend-delivery import --bundle <交付包目录或ZIP> --target-root .
87
+ node scripts/preflight-delivery --input <前端接收预检输入.json> --stage accept --json
85
88
  ```
86
89
 
87
90
  战略快照随交付链导入,后端包与战略包分别验证。导入产生接收草稿;核对目标词汇、规则/场景、接口和视觉用例后才能形成正式接收记录。
@@ -99,7 +102,7 @@ node scripts/backend-delivery import --bundle <交付包目录或ZIP> --target-r
99
102
  node scripts/verify-frontend-delivery --root . --slice <切片ID> <接收记录相对路径>
100
103
  ```
101
104
 
102
- 成功是 `inputs-verified` 且 `ready_for_agent: false`。这允许准备计划和合同,正式编码仍须批准且当前的 Slice Contract。前端实现消费冻结 API;发现业务/API缺口回交维护方,不在前端仓悄悄重定义。
105
+ 成功是 `inputs-verified` 且 `ready_for_agent: false`。这允许准备前端工程设计、实现计划和合同,正式编码仍须批准且当前的 Slice Contract。Handoff v5 必须明确 `prototype`、`existing-ui-baseline` 或有原因的 `not-applicable`;发现业务、API 或 UI 基线缺口时回交维护方,不在前端仓悄悄重定义。
103
106
 
104
107
  ## 6. 实现、联调与业务验收
105
108
 
@@ -2,13 +2,29 @@
2
2
  import { parseArgs } from 'node:util';
3
3
  import path from 'node:path';
4
4
  import { exportBackendDelivery, openBackendDelivery, importBackendDelivery } from './lib/backend-delivery.mjs';
5
+ import { read, safe, ensure } from './lib/strategic-handoff-io.mjs';
6
+
7
+ async function preflightExport(values) {
8
+ const root=path.resolve(values['source-root']),delivery=read(safe(root,values.delivery));
9
+ const document=read(safe(root,delivery.slice_contract.ref)),contract=document.slice_contract||document;
10
+ const identity=contract.resolution?.architecture_identity||contract.backend?.architecture_identity||contract.architecture_identity;
11
+ ensure(identity?.schema_version!==2||values['preflight-input'],'preflight-required: 既有工程身份 v2 导出必须提供 --preflight-input <file>;先按 docs/process/delivery-preflight.md 准备源资产清单并执行 export 阶段只读预检');
12
+ if(!values['preflight-input'])return;
13
+ const {preflightDelivery}=await import('./lib/delivery-preflight.mjs');
14
+ const input=read(path.resolve(values['preflight-input']));
15
+ ensure(!input.delivery_kind||input.delivery_kind==='backend-delivery','preflight-scope-conflict: 后端导出只能使用 backend-delivery 清单');
16
+ ensure(path.resolve(root,input.governance_root||'.')===root,'preflight-scope-conflict: 预检治理根与导出来源不同');
17
+ ensure(input.assets?.backend_delivery?.ref===values.delivery,'preflight-scope-conflict: 预检未绑定本次导出的 delivery 源文件');
18
+ const result=await preflightDelivery({...input,delivery_kind:'backend-delivery'},{stage:'export',root});
19
+ if(result.exit_code!==0){const error=new TypeError('delivery-preflight-blocked: 请按每项 recovery 补证,再从正常导出入口重试');error.preflight=result;throw error;}
20
+ }
5
21
  try {
6
- const {values,positionals}=parseArgs({allowPositionals:true,options:{'source-root':{type:'string'},delivery:{type:'string'},output:{type:'string'},zip:{type:'boolean'},bundle:{type:'string'},'target-root':{type:'string'}}});
22
+ const {values,positionals}=parseArgs({allowPositionals:true,options:{'source-root':{type:'string'},delivery:{type:'string'},output:{type:'string'},zip:{type:'boolean'},bundle:{type:'string'},'target-root':{type:'string'},'preflight-input':{type:'string'}}});
7
23
  if(positionals.length!==1)throw new TypeError('必须指定 export / verify / import');
8
24
  let result;
9
- if(positionals[0]==='export'&&values['source-root']&&values.delivery&&values.output)result=await exportBackendDelivery({sourceRoot:values['source-root'],deliveryRef:values.delivery,output:values.output,zip:values.zip});
25
+ if(positionals[0]==='export'&&values['source-root']&&values.delivery&&values.output){await preflightExport(values);result=await exportBackendDelivery({sourceRoot:values['source-root'],deliveryRef:values.delivery,output:values.output,zip:values.zip});}
10
26
  else if(positionals[0]==='verify'&&values.bundle)result=await openBackendDelivery(path.resolve(values.bundle),b=>({result:'verified',delivery_id:b.delivery.delivery_id,version:b.delivery.version,bundle_digest:b.manifest.bundle_digest,live_service_checked:false}));
11
27
  else if(positionals[0]==='import'&&values.bundle&&values['target-root'])result=await importBackendDelivery({bundle:path.resolve(values.bundle),targetRoot:values['target-root']});
12
- else throw new TypeError('用法: backend-delivery export --source-root <repo> --delivery <ref> --output <new-dir> [--zip] | verify --bundle <dir|zip> | import --bundle <dir|zip> --target-root <repo>');
28
+ else throw new TypeError('用法: backend-delivery export --source-root <repo> --delivery <ref> --output <new-dir> [--preflight-input <file>] [--zip] | verify --bundle <dir|zip> | import --bundle <dir|zip> --target-root <repo>');
13
29
  process.stdout.write(`${JSON.stringify(result,null,2)}\n`);
14
- } catch(error) { process.stderr.write(`${JSON.stringify({result:'blocked',error:error.message})}\n`);process.exitCode=1; }
30
+ } catch(error) { process.stderr.write(`${JSON.stringify({result:'blocked',error:error.message,...(error.preflight?{preflight:error.preflight}:{})})}\n`);process.exitCode=1; }
@@ -1,3 +1,4 @@
1
+ import { assertApprovalUserDecision } from './user-decision-reuse.mjs';
1
2
  import { existsSync, readFileSync } from "node:fs";
2
3
  import path from "node:path";
3
4
  import { parseDocument } from "../vendor/yaml.mjs";
@@ -101,7 +102,7 @@ export function validateApprovalRecord(record, { rolesDoc, requireApproved = fal
101
102
 
102
103
  function assertRecordUserDecision(record, registry, options) {
103
104
  if (!registry.user_decision_policy.gates.includes(record.gate_id) && countersignRuleForGate(registry.gate_policy, record.gate_id)?.bucket !== "biological_human") return;
104
- const result = assertUserDecisionRequirement({ boundary: record.gate_id, subject_ref: record.subject_ref, scope: record.approval_scope, user_decision_ref: record.user_decision_ref }, { ...options, rolesDoc: registry });
105
+ const result = assertApprovalUserDecision(record, registry, options);
105
106
  if (record.actor_kind === "biological-human" && result.validated.some((item) => item.principal_ref !== record.principal_ref)) fail("user-decision-responder-mismatch: 生物人会签者与原始回复者不一致");
106
107
  }
107
108
 
@@ -0,0 +1,106 @@
1
+ import path from 'node:path';
2
+ import { readFileSync } from 'node:fs';
3
+ import { ROOT, read, parse, safe, hash, digest, schema } from './strategic-handoff-io.mjs';
4
+ import { countersignRuleForGate } from './digital-human-roles.mjs';
5
+ import { validateApprovalRecord } from './approval-record.mjs';
6
+ import { assertImplementationDecision } from './user-decision.mjs';
7
+ import { loadSkillRegistry } from './skill-registry.mjs';
8
+
9
+ const contexts=new WeakMap();
10
+ const gateId='gate.slice-contract-approved';
11
+ const check=(ok,code,message)=>{if(!ok){const error=new TypeError(`${code}: ${message}`);error.code=code;throw error;}};
12
+ const same=(a,b)=>digest(a)===digest(b);
13
+ const ioFor=root=>({root,read:ref=>readFileSync(safe(root,path.relative(root,ref).split(path.sep).join('/')))});
14
+
15
+ /** Existing registrations require persisted input sections; array properties disappear in JSON. */
16
+ export function assertExistingSliceStructure(contract) {
17
+ const identity=contract?.resolution?.architecture_identity;
18
+ if(identity?.schema_version!==2||identity.source_kind!=='existing-registration')return;
19
+ const object=value=>value!==null&&typeof value==='object'&&!Array.isArray(value);
20
+ check(object(contract.lifecycle_refs),'EXECUTION_CONTRACT_INVALID','既有工程 Slice lifecycle_refs 必须是对象');
21
+ for(const field of ['ticket','engineering_baseline'])check(typeof contract.lifecycle_refs[field]==='string'&&contract.lifecycle_refs[field].trim(),'EXECUTION_CONTRACT_INVALID',`既有工程 Slice 缺少 lifecycle_refs.${field}`);
22
+ check(object(contract.readiness),'EXECUTION_CONTRACT_INVALID','既有工程 Slice readiness 必须是对象');
23
+ for(const field of ['blockers','stale_inputs','not_applicable'])check(Array.isArray(contract.readiness[field]),'EXECUTION_CONTRACT_INVALID',`既有工程 Slice readiness.${field} 必须是数组`);
24
+ }
25
+
26
+ /** Reads original local approval; a contract's own approved status never establishes permission. */
27
+ export function verifySliceContractApproval(binding,{root=process.cwd(),contract:expected}={}) {
28
+ root=path.resolve(root);
29
+ check(binding?.ref&&binding?.digest&&binding?.approval_ref,'EXECUTION_APPROVAL_REQUIRED','需要当前持久化 Slice 的 ref/digest/approval_ref');
30
+ const bytes=readFileSync(safe(root,binding.ref));
31
+ check(hash(bytes)===binding.digest,'EXECUTION_CONTRACT_STALE','Slice 原始字节与绑定不一致');
32
+ const document=parse(bytes),contract=document.slice_contract||document;
33
+ check(contract.schema_version===2&&contract.status==='approved'&&typeof contract.contract_id==='string'&&contract.contract_id&&typeof contract.contract_version==='string'&&contract.contract_version&&typeof contract.slice_id==='string'&&contract.slice_id,'EXECUTION_CONTRACT_INVALID','需要已批准且有身份的持久化 Slice v2');
34
+ assertExistingSliceStructure(contract);
35
+ check((!binding.id||binding.id===contract.contract_id)&&(!binding.version||binding.version===contract.contract_version),'EXECUTION_CONTRACT_CONFLICT','当前合同身份或版本不匹配');
36
+ if(expected)check(same(expected.slice_contract||expected,contract),'EXECUTION_CONTRACT_CONFLICT','调用方合同与持久化原字节不一致');
37
+ const roles=read(safe(root,'docs/agents/digital-human-roles.yaml'));
38
+ const approval=read(safe(root,binding.approval_ref));
39
+ if(!countersignRuleForGate(roles.gate_policy,gateId)&&roles.gate_policy.orchestrator?.includes(gateId)) {
40
+ // The current lifecycle already owns this gate. Consume its checkpoint and existing
41
+ // implementation-scope decision instead of inventing an approval-record countersignature.
42
+ check(approval?.gates&&approval?.human_review&&!approval.gate_id,'EXECUTION_APPROVAL_PROTOCOL','当前主控 gate 需要生命周期 checkpoint,不接受伪造 approval-record');
43
+ schema(approval,'docs/process/schemas/lifecycle-checkpoint.schema.json');
44
+ const gate=approval.gates?.[gateId],review=approval.human_review||{};
45
+ check(approval.repository_mode==='project-instance'&&approval.status!=='blocked'&&approval.blockers.length===0,'EXECUTION_APPROVAL_BLOCKED','主控 checkpoint 仍有阻断或不是产品实例');
46
+ check(gate?.status==='approved'&&gate.subject_ref===binding.ref,'EXECUTION_APPROVAL_SCOPE','主控 gate 未批准当前持久化 Slice');
47
+ check(review.implementation?.slice_contract_ref===binding.ref,'EXECUTION_APPROVAL_SCOPE','实施批准不指向当前 Slice 原始文件');
48
+ assertImplementationDecision({...review.implementation,user_decisions:review.user_decisions||[]},{...ioFor(root),rolesDoc:roles});
49
+ } else {
50
+ // Historical source profiles that declared this a countersign gate retain their policy.
51
+ validateApprovalRecord(approval,{...ioFor(root),rolesDoc:roles,requireApproved:true});
52
+ check(approval.gate_id===gateId&&approval.artifact_bindings?.some(item=>item.id===contract.contract_id&&item.version===contract.contract_version&&item.digest===binding.digest),'EXECUTION_APPROVAL_SCOPE','源会签未绑定当前 Slice 的身份、版本和字节');
53
+ }
54
+ return {contract,binding:structuredClone(binding),root};
55
+ }
56
+
57
+ function executionBasis(verified,{allowCompilerDrift=false}={}) {
58
+ const {contract}=verified,resolution=contract.resolution||{};
59
+ const compiler=read(safe(ROOT,'.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml'));
60
+ if(!allowCompilerDrift)for(const [section,fields] of Object.entries(compiler.slice_contract_required))for(const field of fields)check(Object.hasOwn(section==='root'?contract:contract[section]||{},field),'EXECUTION_CONTRACT_INVALID',`合同缺少 ${section}.${field}`);
61
+ check(Array.isArray(contract.common?.allowed_write_paths)&&contract.common.allowed_write_paths.length>0&&contract.common.allowed_write_paths.every(value=>typeof value==='string'&&value.trim()),'EXECUTION_SCOPE_MISSING','合同缺少已批准写范围');
62
+ check(Array.isArray(contract.readiness?.blockers)&&!contract.readiness.blockers.length&&Array.isArray(contract.readiness?.stale_inputs)&&!contract.readiness.stale_inputs.length,'EXECUTION_CONTRACT_STALE','合同就绪条件仍有阻断或过期输入');
63
+ if(!allowCompilerDrift)check(resolution.registry_digest===digest(loadSkillRegistry()).slice(7)&&resolution.compiler_contract_digest===digest(compiler).slice(7),'EXECUTION_CONTRACT_STALE','编译依据已改变,应重新编译批准');
64
+ check(resolution.freshness==='current','EXECUTION_CONTRACT_STALE','resolution 不是 current');
65
+ if(resolution.architecture_identity)check(resolution.architecture_identity_digest===digest(resolution.architecture_identity).slice(7),'EXECUTION_CONTRACT_STALE','编译架构身份摘要不一致');
66
+ return verified;
67
+ }
68
+
69
+ export function createApprovedExecutionContext(binding,options={}) {
70
+ const verified=executionBasis(verifySliceContractApproval(binding,options));
71
+ const context=Object.freeze({kind:'approved-slice-execution-context'});
72
+ contexts.set(context,{binding:verified.binding,root:verified.root,allowCompilerDrift:false});
73
+ return context;
74
+ }
75
+
76
+ /** A prior approved scope may authenticate bounded output while current compiler facts are recomputed. */
77
+ export function createApprovedRecompilationContext(binding,options={}) {
78
+ const verified=executionBasis(verifySliceContractApproval(binding,options),{allowCompilerDrift:true});
79
+ const context=Object.freeze({kind:'approved-slice-recompilation-context'});
80
+ contexts.set(context,{binding:verified.binding,root:verified.root,allowCompilerDrift:true});
81
+ return context;
82
+ }
83
+
84
+ /** Revalidate original bytes on every boundary; a serialized/caller-invented context is rejected. */
85
+ export function assertApprovedExecutionContext(context,{root=process.cwd(),contract,architectureIdentity,architectureEvidence,technicalDesign,sliceId}={}) {
86
+ const saved=contexts.get(context);
87
+ check(saved&&saved.root===path.resolve(root),'EXECUTION_CONTEXT_UNTRUSTED','只允许从当前原始 Slice 与本地批准生成的执行上下文');
88
+ const verified=executionBasis(verifySliceContractApproval(saved.binding,{root,contract}),{allowCompilerDrift:saved.allowCompilerDrift});
89
+ const current=verified.contract,resolution=current.resolution;
90
+ if(sliceId)check(current.slice_id===sliceId,'EXECUTION_SCOPE_CONFLICT','执行上下文属于另一切片');
91
+ if(architectureIdentity)check(same(resolution.architecture_identity,architectureIdentity),'EXECUTION_INPUT_REPLACED','执行上下文不允许替换架构身份或固定源码输入');
92
+ if(architectureEvidence)check(same(resolution.architecture_evidence,architectureEvidence),'EXECUTION_INPUT_REPLACED','执行上下文不允许替换原始架构证据');
93
+ if(technicalDesign) {
94
+ const binding=resolution.technical_design;
95
+ check(binding?.ref&&binding.digest,'EXECUTION_DESIGN_MISSING','Slice 未绑定当前技术设计');
96
+ const bytes=readFileSync(safe(root,binding.ref));
97
+ check(hash(bytes)===binding.digest&&same(parse(bytes),technicalDesign),'EXECUTION_INPUT_REPLACED','执行上下文不允许替换技术设计');
98
+ }
99
+ return {allowed_write_paths:[...current.common.allowed_write_paths],contract:current};
100
+ }
101
+
102
+ export function approvedExecutionBinding(context) {
103
+ const saved=contexts.get(context);
104
+ check(saved,'EXECUTION_CONTEXT_UNTRUSTED','不能序列化未验证的执行上下文');
105
+ return structuredClone(saved.binding);
106
+ }
@@ -1,5 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { loadSkillRegistry } from "./skill-registry.mjs";
3
+ import { validateExistingArchitecture, verifyExistingArchitecture } from "./existing-backend-architecture.mjs";
3
4
 
4
5
  export function architectureDigest(value) {
5
6
  const stable = (item) => Array.isArray(item) ? item.map(stable) : item && typeof item === "object"
@@ -9,6 +10,8 @@ export function architectureDigest(value) {
9
10
 
10
11
  export function validateArchitectureIdentity(identity, registry = loadSkillRegistry()) {
11
12
  if (!identity || typeof identity !== "object") throw new TypeError("缺少 architecture_identity;请从工程基线重新编译");
13
+ if (identity.schema_version === 2) return validateExistingArchitecture(identity, registry);
14
+ if (identity.schema_version !== undefined || identity.source_kind !== undefined) throw new TypeError("不支持的 architecture_identity 来源或版本");
12
15
  const profile = registry.architecture_profiles?.[identity.architecture_profile];
13
16
  if (!profile || identity.architecture_family !== profile.architecture_family || identity.generator_skill !== profile.generator_skill) {
14
17
  throw new TypeError("architecture_identity 的架构族、Profile 或生成器不匹配");
@@ -28,6 +31,15 @@ export function validateArchitectureIdentity(identity, registry = loadSkillRegis
28
31
  return profile;
29
32
  }
30
33
 
34
+ // Existing projects must present original on-disk evidence, not three copies supplied by a caller.
35
+ export function verifyArchitectureEvidence(identity, evidence, { root, registry = loadSkillRegistry(), execution } = {}) {
36
+ validateArchitectureIdentity(identity, registry);
37
+ if (identity.schema_version === 2) return verifyExistingArchitecture(identity, evidence, { root, registry, execution });
38
+ if (!evidence?.engineering_baseline || !evidence?.repository_registration || !evidence?.manifest) throw new TypeError("缺少工程基线、仓库登记或 Manifest 架构证据");
39
+ assertArchitectureAgreement(identity, evidence, registry);
40
+ return { source_kind: "scaffold", bindings: evidence };
41
+ }
42
+
31
43
  export function assertArchitectureAgreement(identity, evidence, registry) {
32
44
  validateArchitectureIdentity(identity, registry);
33
45
  for (const [name, source] of Object.entries(evidence ?? {})) {
@@ -1,3 +1,4 @@
1
+ import { verifySliceContractApproval } from './approved-execution-context.mjs';
1
2
  import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync } from 'node:fs';
2
3
  import path from 'node:path';
3
4
  import { tmpdir } from 'node:os';
@@ -6,7 +7,7 @@ import { read, safe, ensure, hash, digest, json, files, write, project, schema,
6
7
 
7
8
  // Evidence binds the deliverable basis, avoiding a circular hash through its own logs.
8
9
  export function backendDeliveryBasis(delivery) {
9
- return digest(Object.fromEntries(['delivery_id','version','strategic_bundle_digest','scope','openapi','slice_contract','build','environment'].map(key=>[key,delivery[key]])));
10
+ return digest(Object.fromEntries(['delivery_id','version','strategic_bundle_digest','strategic_route_id','scope','openapi','slice_contract','build','environment'].filter(key=>delivery[key]!==undefined).map(key=>[key,delivery[key]])));
10
11
  }
11
12
 
12
13
  function boundFile(root, binding) {
@@ -17,6 +18,7 @@ function boundFile(root, binding) {
17
18
 
18
19
  async function approvedFile(root, binding, gate) {
19
20
  const bytes=boundFile(root,binding);
21
+ if(gate==='gate.slice-contract-approved'){verifySliceContractApproval(binding,{root});return bytes;}
20
22
  const record=read(safe(root,binding.approval_ref));
21
23
  const roles=sourceApprovalPolicy(read(safe(root,'docs/agents/digital-human-roles.yaml')));
22
24
  await sourceApproval(record,roles,root);
@@ -38,7 +40,7 @@ function verification(root, binding, basis, kind) {
38
40
  return record;
39
41
  }
40
42
 
41
- export async function inspectBackendDelivery(root, ref) {
43
+ export async function inspectBackendDelivery(root, ref, { readOnly = false } = {}) {
42
44
  project(root);
43
45
  const delivery=read(safe(root,ref));
44
46
  schema(delivery,'docs/process/schemas/backend-delivery.schema.json');
@@ -57,6 +59,10 @@ export async function inspectBackendDelivery(root, ref) {
57
59
  verification(root,delivery.verification.deployment,basis,'backend-deployment');
58
60
  return openBundle(safe(root,delivery.strategic_bundle_ref),bundle=>{
59
61
  ensure(bundle.manifest.bundle_digest===delivery.strategic_bundle_digest,'后端交付与战略版本不一致');
62
+ if([4,5].includes(bundle.handoff.schema_version)){
63
+ const route=bundle.handoff.consumer_routes.find(item=>item.capability==='backend-technical-design');
64
+ ensure(route&&route.activation!=='not-applicable'&&delivery.strategic_route_id===route.route_id,'后端交付未绑定当前 backend-technical-design route_id');
65
+ }
60
66
  const sources=new Set([...bundle.indexes.rules.map(x=>x.rule_id),...bundle.indexes.scenarios.map(x=>x.scenario_id)]);
61
67
  ensure(delivery.scope.source_ids.every(id=>sources.has(id)),'后端交付引用未知规则/场景');
62
68
  const scenarios=bundle.indexes.scenarios.filter(x=>delivery.scope.source_ids.includes(x.scenario_id));
@@ -67,7 +73,7 @@ export async function inspectBackendDelivery(root, ref) {
67
73
  }
68
74
  ensure(delivery.scope.operation_ids.every(id=>tests.operation_ids.includes(id)),'后端契约验证未覆盖交付接口');
69
75
  return {delivery,basis};
70
- });
76
+ }, { readOnly });
71
77
  }
72
78
 
73
79
  function validManifest(manifest,root) {
@@ -152,14 +158,24 @@ export async function importBackendDelivery({bundle,targetRoot}) {
152
158
  }
153
159
  const strategic=await importBundle({bundle:safe(b.source,b.delivery.strategic_bundle_ref),targetRoot:target});
154
160
  const strategicReceipt=read(safe(target,strategic.receipt_ref));
155
- const trace=read(safe(target,`${path.posix.dirname(strategic.receipt_ref)}/tactical-traceability-draft.json`));
161
+ const strategicBase=path.posix.dirname(strategic.receipt_ref);
162
+ const frontendRoute=strategicReceipt.schema_version===2?strategicReceipt.routes.find(route=>route.capability==='frontend-engineering-design'):null;
163
+ const traceRef=frontendRoute?.artifact_refs.find(item=>item.endsWith('frontend-traceability-draft.json'))||`${strategicBase}/tactical-traceability-draft.json`;
164
+ const trace=read(safe(target,traceRef));
156
165
  mkdirSync(path.dirname(destination),{recursive:true});
157
166
  const staging=mkdtempSync(path.join(path.dirname(destination),'.backend-import-'));
158
167
  try {
159
168
  cpSync(b.root,path.join(staging,'package'),{recursive:true,errorOnExist:true,force:false});
160
169
  const receipt={schema_version:1,delivery_id:b.delivery.delivery_id,version:b.delivery.version,bundle_digest:b.manifest.bundle_digest,package_ref:`${ref}/package`};
161
170
  write(staging,'import-receipt.json',json(receipt));
162
- write(staging,'frontend-acceptance-draft.json',json({schema_version:1,status:'draft',slice_id:b.delivery.scope.slice_id,backend_delivery:{import_receipt_ref:`${ref}/import-receipt.json`,bundle_digest:b.manifest.bundle_digest},strategic_handoff:{import_receipt_ref:strategic.receipt_ref,bundle_digest:strategicReceipt.bundle_digest,context_reconciliation_ref:'',rows:trace.rows.map(({tactical_refs,test_seam_refs,...row})=>({...row,frontend_case_refs:[]}))},frontend_cases:[]}));
171
+ const strategicHandoff={import_receipt_ref:strategic.receipt_ref,bundle_digest:strategicReceipt.bundle_digest,...(trace.route_id?{route_id:trace.route_id}:{}),context_reconciliation_ref:'',rows:trace.rows.map(({tactical_refs,test_seam_refs,...row})=>({...row,frontend_case_refs:[]}))};
172
+ if(strategicReceipt.schema_version===2){
173
+ ensure(frontendRoute,'Import Receipt 未选择 frontend-engineering-design 能力');
174
+ const preflightRef=frontendRoute.artifact_refs.find(item=>item.endsWith('frontend-strategic-preflight-draft.json'));ensure(preflightRef,'缺少 Frontend Strategic Preflight 草案');
175
+ const preflight=read(safe(target,preflightRef));
176
+ ensure([1,2].includes(preflight.schema_version),'未知 Frontend Strategic Preflight 版本');
177
+ write(staging,'frontend-acceptance-draft.json',json({schema_version:preflight.schema_version===2?3:2,...(preflight.schema_version===2?{ui_baseline_kind:preflight.ui_baseline_kind}:{}),status:'draft',slice_id:b.delivery.scope.slice_id,strategic_preflight:{ref:preflightRef,digest:hash(readFileSync(safe(target,preflightRef)))},backend_dependency:{mode:'required',route_id:b.delivery.strategic_route_id},backend_delivery:{import_receipt_ref:`${ref}/import-receipt.json`,bundle_digest:b.manifest.bundle_digest},strategic_handoff:strategicHandoff,frontend_cases:[]}));
178
+ }else write(staging,'frontend-acceptance-draft.json',json({schema_version:1,status:'draft',slice_id:b.delivery.scope.slice_id,backend_delivery:{import_receipt_ref:`${ref}/import-receipt.json`,bundle_digest:b.manifest.bundle_digest},strategic_handoff:strategicHandoff,frontend_cases:[]}));
163
179
  ensure(!existsSync(destination),'后端包导入并发冲突');renameSync(staging,destination);
164
180
  return {result:'imported-pending-acceptance',receipt_ref:`${ref}/import-receipt.json`,acceptance_ref:`${ref}/frontend-acceptance-draft.json`};
165
181
  } finally { rmSync(staging,{recursive:true,force:true}); }
@@ -0,0 +1,238 @@
1
+ import { assertExistingSliceStructure, createApprovedExecutionContext, verifySliceContractApproval } from './approved-execution-context.mjs';
2
+ import path from 'node:path';
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { ROOT, read, parse, safe, hash, digest, ensure, schema, sourceApprovalPolicy } from './strategic-handoff-io.mjs';
5
+ import { parseContextContract } from './context-contract.mjs';
6
+ import { validateApprovalRecord } from './approval-record.mjs';
7
+ import { loadSkillRegistry } from './skill-registry.mjs';
8
+ import * as architecture from './backend-architecture.mjs';
9
+
10
+ const STAGES=['prepare','build','export','accept'];
11
+ const LATER=stage=>STAGES.slice(STAGES.indexOf(stage));
12
+ const OWNERS={governance:'工程接入负责人',architecture:'工程基线与边界审查负责人',openapi:'接口契约负责人',technical_design:'技术设计负责人','slice_contract.execution':'生命周期编排与合同批准方',slice_contract:'生命周期编排与合同批准方',ui_baseline:'产品设计负责人',build_artifact:'后端构建负责人',build:'后端构建负责人',deployment:'后端部署与验证负责人',strategic_handoff:'战略交付方',backend_delivery:'后端交付方',local_authorization:'目标仓接收负责人'};
13
+ const RECOVERY={governance:'完成工程接入登记或正常初始化,读取有效 yss-project.yaml 和根 CONTEXT.md 后重跑预检',architecture:'按工程接入与 backend-architecture 合同补齐原始登记、工程基线、观测 manifest,重新边界审查并编译',openapi:'回到 OpenAPI Draft 审查与 Freeze,绑定当前字节及真实批准',technical_design:'按已登记架构更新技术设计,补齐输入与审查并重新批准','slice_contract.execution':'读取当前 Slice 字节及本地主控批准,恢复 implementation-scope 原始决定后重试',slice_contract:'重新编译 Slice Implementation Contract 并取得当前合同批准',ui_baseline:'由产品设计负责人确认现有 UI 基线;有 UI 改动则回到原型设计',build_artifact:'绑定实际 JAR 文件并与构建 artifact_digest 核对',build:'使用批准合同正常构建,并记录实际输出摘要与验证',deployment:'部署当前构建,采集同进程身份与真实验证证据',strategic_handoff:'修正战略来源与批准,重新导出当前交接资产',backend_delivery:'从正常后端交付入口重新生成与当前来源一致的交付记录',local_authorization:'由目标仓负责人确认当前生产方、项目、route 和 slice 范围'};
14
+ const ASSETS={openapi:'build',technical_design:'build',slice_contract:'build',ui_baseline:'export',build:'export',build_artifact:'export',deployment:'export',strategic_handoff:'export',backend_delivery:'accept',local_authorization:'accept'};
15
+ const errorStatus=error=>error.code==='EXECUTION_CONTRACT_INVALID'?'conflict':/stale|漂移|过期|摘要/.test(error.message)?'stale':/unsupported|未知|不支持|版本/.test(error.message)?'unsupported':/不可读|ENOENT|缺少/.test(error.message)?'missing':'conflict';
16
+ const docBinding=(root,binding)=>{
17
+ const bytes=readFileSync(safe(root,binding.ref));
18
+ ensure(hash(bytes)===binding.digest,`stale: 资产摘要漂移: ${binding.ref}`);
19
+ return parse(bytes);
20
+ };
21
+ const fileBinding=(root,binding)=>{ ensure(binding?.ref&&binding?.digest,'缺少证据 ref/digest');const bytes=readFileSync(safe(root,binding.ref));ensure(bytes.length>0&&hash(bytes)===binding.digest,`stale: 证据为空或摘要漂移: ${binding.ref}`);return bytes; };
22
+ const same=(a,b)=>digest(a)===digest(b);
23
+
24
+ export function invalidPreflightInput(message) {
25
+ return {schema_version:1,result:'invalid-input',exit_code:2,stage:null,checks:[{code:'input.protocol',status:'conflict',owner:'交付编排方',source:'delivery-preflight-input',evidence:[],blocking_stages:STAGES,recovery:'按 delivery-preflight-input.schema.json 修正输入协议',message}]};
26
+ }
27
+
28
+ /** Installed profile owns backend technical authoring; caller governance_root cannot grant it. */
29
+ export function backendPreflightCapability() {
30
+ const profileRef=path.join(ROOT,'docs/process/harness-profile.yaml');
31
+ const profile=existsSync(profileRef)?read(profileRef):null;
32
+ const profileId=profile?.profile_id||'template.full';
33
+ const ownsBackend=!profile||profileId==='harness.backend-delivery';
34
+ const required=['.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs','.agents/skills/yss-technical-design/references/technical-design.schema.json','.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml'];
35
+ const missing=ownsBackend?required.filter(ref=>!existsSync(path.join(ROOT,ref))):[];
36
+ return {profile_id:profileId,owns_backend:ownsBackend,available:ownsBackend&&!missing.length,missing,
37
+ reason:ownsBackend?(missing.length?`后端验证运行时分发不完整: ${missing.join(', ')}`:'当前安装拥有后端技术设计与合同验证能力'):`${profileId} 不拥有后端技术设计与实现合同编译职责;由主模板或 backend profile 验证`};
38
+ }
39
+ function requireBackendCapability(key) {
40
+ const capability=backendPreflightCapability();
41
+ if(capability.available)return;
42
+ const error=new TypeError(`unsupported: ${capability.reason}`);
43
+ error.code=key==='technical_design'?'PREFLIGHT_TECHNICAL_DESIGN_UNSUPPORTED':'PREFLIGHT_SLICE_CONTRACT_UNSUPPORTED';
44
+ error.recovery=capability.owns_backend?'恢复当前后端 profile 的完整验证运行时分发,再从正常预检入口重跑':'将原始技术资产与批准绑定交给主模板或 backend 工程负责人核验;本 profile 通过已导出的受控交付包接收,不安装另一方创作技能';
45
+ throw error;
46
+ }
47
+
48
+ /** Read-only: never executes input commands, starts services, unpacks archives or writes receipts. */
49
+ export async function preflightDelivery(input,{stage,root=process.cwd()}={}) {
50
+ try { ensure(STAGES.includes(stage),'未知阶段;必须是 prepare|build|export|accept');schema(input,'docs/process/schemas/delivery-preflight-input.schema.json'); }
51
+ catch(error) { return invalidPreflightInput(error.message); }
52
+ root=path.resolve(root,input.governance_root||'.');
53
+ const deliveryKind=input.delivery_kind||'complete';
54
+ const checks=[], documents={}, valid=new Set(), assets=input.assets||{};
55
+ let execution;
56
+ const downstream=key=>deliveryKind==='strategic-handoff'&&(key.startsWith('architecture')||key.startsWith('slice_contract')||['technical_design','slice_contract','build','build_artifact','deployment','backend_delivery','local_authorization'].includes(key)||(key==='openapi'&&!input.assets?.openapi));
57
+ const add=(key,status,message,{code=key,from='prepare',evidence=[],source=key,recovery=RECOVERY[key]||RECOVERY.architecture}={})=>checks.push({code,status,owner:OWNERS[key]||OWNERS.architecture,source,evidence,blocking_stages:downstream(key)?[]:LATER(from),recovery,message});
58
+ const run=async(key,action,options={})=>{
59
+ try { await action();valid.add(key);add(key,'ready','当前原始资产与语义核验通过',options); }
60
+ catch(error) { add(key,errorStatus(error),error.message,{...options,...(error.recovery?{recovery:error.recovery}:{}),code:typeof error.code==='string'?error.code:options.code||key}); }
61
+ };
62
+ const pending=(key,deps,options)=>add(key,'pending',`尚不可判定,依赖未就绪: ${deps.join(', ')}`,options);
63
+ await run('governance',()=>{
64
+ const project=read(safe(root,'yss-project.yaml'));
65
+ ensure(project.schema_version===1&&project.repository_mode==='project-instance','交付预检只消费显式 project-instance;未知身份不得推测');
66
+ parseContextContract({root});
67
+ },{source:'yss-project.yaml / CONTEXT.md',evidence:['yss-project.yaml','CONTEXT.md']});
68
+ if(input.planned_initialization) add('governance','not-applicable','此为拟初始化目标的只读诊断;预检不初始化,缺失的当前阶段前提仍明确阻断',{code:'governance.planned-initialization'});
69
+
70
+ const bindings=input.architecture_bindings||{};
71
+ for(const key of ['repository_registration','engineering_baseline','manifest']) {
72
+ if(!bindings[key]) add('architecture','missing',`缺少原始架构来源: ${key}`,{code:`architecture.${key}`,source:key});
73
+ else await run(`architecture.${key}`,()=>{documents[key]=docBinding(root,bindings[key]);verifyArchitectureDocument(key,documents[key],input.scope,root);},{source:key,evidence:[bindings[key].ref]});
74
+ }
75
+ if(!input.architecture_identity) add('architecture','missing','缺少 architecture_identity;不能从目录或数据库字符串推断',{code:'architecture.identity'});
76
+ else await run('architecture.identity',()=>{
77
+ architecture.validateArchitectureIdentity(input.architecture_identity);
78
+ if(input.architecture_identity.source_kind==='existing-registration')ensure(input.architecture_identity.repository_id===input.scope.repository_id&&input.architecture_identity.project_id===input.scope.project_id,'架构身份与预检目标仓库/项目不一致');
79
+ },{source:'architecture_identity'});
80
+ if(valid.has('architecture.identity'))await run('architecture.profile',()=>{ensure(architecture.validateArchitectureIdentity(input.architecture_identity).maturity==='supported','不支持未达到 supported 的 architecture_profile');},{source:'architecture_profile'});
81
+ if(['build','export','accept'].includes(stage)&&assets.slice_contract)await run('slice_contract.execution',()=>{requireBackendCapability('slice_contract');execution=createApprovedExecutionContext(assets.slice_contract,{root});},{from:'build',source:assets.slice_contract.approval_ref||'slice_contract',evidence:[assets.slice_contract.ref,...(assets.slice_contract.approval_ref?[assets.slice_contract.approval_ref]:[])]});
82
+ const archDeps=['architecture.identity',...['repository_registration','engineering_baseline','manifest'].map(x=>`architecture.${x}`)];
83
+ if(archDeps.every(x=>valid.has(x)))await run('architecture',()=>{
84
+ ensure(typeof architecture.verifyArchitectureEvidence==='function','不支持既有工程原始证据验证,请同步支持该身份版本的模板运行时');
85
+ architecture.verifyArchitectureEvidence(input.architecture_identity,bindings,{root,registry:loadSkillRegistry(),execution});
86
+ },{source:'backend-architecture',evidence:Object.values(bindings).map(x=>x.ref)});
87
+ else pending('architecture',archDeps.filter(x=>!valid.has(x)),{source:'backend-architecture'});
88
+
89
+ // Load the delivery basis before checking deployment; dependency order must not hide drift.
90
+ if(assets.backend_delivery)try{documents.backend_delivery=docBinding(root,assets.backend_delivery);}catch{}
91
+ for(const [key,earliest] of Object.entries(ASSETS)) {
92
+ const from=key==='backend_delivery'&&deliveryKind==='backend-delivery'?'export':earliest;
93
+ if(deliveryKind==='backend-delivery'&&['ui_baseline','strategic_handoff'].includes(key)&&!assets[key])continue;
94
+ if(key==='local_authorization'&&documents.backend_delivery?.schema_version!==2) {
95
+ add(key,'not-applicable','当前未确认交付 v2;本地授权只在受控目标绑定协议 v2 生效',{from});continue;
96
+ }
97
+ if(key==='ui_baseline'&&input.scope.ui_mode==='not-applicable') {
98
+ if(input.scope.ui_not_applicable_reason?.trim())add(key,'not-applicable',input.scope.ui_not_applicable_reason,{from});
99
+ else add(key,'missing','无 UI 影响必须说明不适用原因',{from});
100
+ continue;
101
+ }
102
+ if(!assets[key]) {add(key,'missing',`缺少 ${key} 原始资产绑定${STAGES.indexOf(stage)<STAGES.indexOf(from)?`;到 ${from} 阶段前补齐`:''}`,{from});continue;}
103
+ await run(key,async()=>{
104
+ const binding=assets[key];
105
+ if(['technical_design','slice_contract'].includes(key))requireBackendCapability(key);
106
+ if(key==='build_artifact'){fileBinding(root,binding);ensure(documents.build?.artifact_digest===binding.digest,'stale: 实际制品与构建记录摘要不一致');return;}
107
+ const data=documents[key]=docBinding(root,binding);
108
+ await verifyAsset(key,data,binding,{root,input,documents,assets,execution});
109
+ },{from,source:assets[key].ref,evidence:[assets[key].ref,...(assets[key].approval_ref?[assets[key].approval_ref]:[])]});
110
+ }
111
+ if(deliveryKind==='backend-delivery')for(const key of ['ui_baseline','strategic_handoff'])if(!assets[key])add(key,valid.has('backend_delivery')?'ready':'pending',valid.has('backend_delivery')?'已通过交付绑定的战略包完成来源与 UI 基线只读复验':'尚不可判定,等待后端交付绑定的战略包核验',{code:`${key}.delivery-package`,from:'export',source:'backend_delivery.strategic_bundle_ref',evidence:assets.backend_delivery?[assets.backend_delivery.ref]:[]});
112
+ // A present but unreadable dependency must not turn cross-asset agreement into a success.
113
+ if(assets.slice_contract&&(!valid.has('technical_design')||!valid.has('architecture')))pending('slice_contract',['technical_design','architecture'].filter(x=>!valid.has(x)),{code:'slice_contract.dependencies',from:'build'});
114
+ if(assets.deployment&&!valid.has('backend_delivery'))pending('deployment',['backend_delivery'],{code:'deployment.delivery-basis',from:'export'});
115
+ const blocked=checks.some(x=>!['ready','not-applicable'].includes(x.status)&&x.blocking_stages.includes(stage));
116
+ return {schema_version:1,result:blocked?'blocked':'ready',exit_code:blocked?1:0,stage,checks};
117
+ }
118
+
119
+ async function approved(root,binding,gates) {
120
+ ensure(binding.approval_ref&&binding.id&&binding.version,'缺少当前资产 id/version/approval_ref');
121
+ const record=read(safe(root,binding.approval_ref));
122
+ const roles=sourceApprovalPolicy(read(safe(root,'docs/agents/digital-human-roles.yaml')));
123
+ validateApprovalRecord(record,{rolesDoc:roles,requireApproved:true,root,read:ref=>readFileSync(safe(root,path.relative(root,ref).split(path.sep).join('/')))});
124
+ ensure(gates.includes(record.gate_id),'资产批准使用错误门禁');
125
+ ensure(record.artifact_bindings?.some(x=>x.id===binding.id&&x.version===binding.version&&x.digest===binding.digest),'stale: 批准未绑定当前资产身份、版本及字节');
126
+ }
127
+
128
+ async function verifyAsset(key,data,binding,{root,input,documents,assets,execution}) {
129
+ ensure(data&&typeof data==='object'&&!Array.isArray(data),`${key} 必须是结构化资产`);
130
+ if(key==='openapi') {
131
+ ensure(/^3\.1\./.test(data.openapi)&&data.info?.title&&data.info?.version&&data.paths,'必须提供完整 OpenAPI 3.1 文档');
132
+ const operations=Object.values(data.paths).flatMap(item=>Object.entries(item).filter(([method])=>['get','put','post','delete','options','head','patch','trace'].includes(method)).map(([,operation])=>operation.operationId));
133
+ ensure(new Set(operations).size===operations.length&&operations.every(x=>typeof x==='string'&&x),'OpenAPI operationId 缺失或重复');
134
+ ensure(input.scope.operation_ids.every(id=>operations.includes(id)),'预检接口范围不在冻结 OpenAPI 中');
135
+ await approved(root,binding,['gate.engineering-contract-approved','gate.openapi-frozen','gate.openapi-freeze-confirmed']);
136
+ } else if(key==='technical_design') {
137
+ schema(data,'.agents/skills/yss-technical-design/references/technical-design.schema.json');
138
+ ensure(data.schema_version===2&&data.status==='approved','技术设计必须是已批准 v2 合同');
139
+ ensure(data.digest===digest(Object.fromEntries(Object.entries(data).filter(([k])=>k!=='digest'))),'stale: 技术设计自摘要不匹配');
140
+ ensure(data.architecture?.family===input.architecture_identity?.architecture_family,'技术设计与登记架构冲突');
141
+ for(const asset of data.inputs)fileBinding(root,asset);
142
+ const {validateTechnicalDesign}=await import('../../.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs');
143
+ await validateTechnicalDesign(data,{root,sliceRef:input.scope.slice_id,readOnly:true,execution});
144
+ await approved(root,binding,['gate.engineering-contract-approved','gate.technical-design-approved']);
145
+ } else if(key==='slice_contract') {
146
+ const contract=data.slice_contract||data;
147
+ assertExistingSliceStructure(contract);
148
+ ensure(contract.schema_version===2&&contract.status==='approved'&&contract.slice_id===input.scope.slice_id,'Slice 合同版本、批准状态或切片不匹配');
149
+ ensure(binding.id===contract.contract_id&&binding.version===contract.contract_version,'Slice 合同身份与引用不一致');
150
+ const compiler=read(safe(ROOT,'.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml'));
151
+ for(const [section,fields]of Object.entries(compiler.slice_contract_required))for(const field of fields)ensure(Object.hasOwn(section==='root'?contract:contract[section]||{},field),`合同缺少 ${section}.${field}`);
152
+ ensure(contract.readiness.blockers.length===0&&contract.readiness.stale_inputs.length===0,'Slice 合同仍有 blocker 或 stale input');
153
+ const resolution=contract.resolution;
154
+ ensure(resolution.freshness==='current','Slice resolution 非 current');
155
+ ensure(resolution.registry_digest===digest(loadSkillRegistry()).slice(7)&&resolution.compiler_contract_digest===digest(compiler).slice(7),'stale: 编译事实源已变化,需重新编译');
156
+ ensure(same(resolution.architecture_identity,input.architecture_identity),'Slice 架构与登记不一致');
157
+ ensure(resolution.architecture_identity_digest===architecture.architectureDigest(input.architecture_identity),'stale: Slice 架构摘要漂移');
158
+ if(assets.technical_design)ensure(resolution.technical_design?.ref===assets.technical_design.ref&&resolution.technical_design?.digest===assets.technical_design.digest,'Slice 技术设计绑定不一致');
159
+ verifySliceContractApproval(binding,{root,contract});
160
+ } else if(key==='strategic_handoff') {
161
+ const {inspectSource}=await import('./strategic-handoff.mjs');
162
+ await inspectSource(root,binding.ref);
163
+ const {uiBaselineRef,uiBaselineKind}=await import('./ui-baseline.mjs');
164
+ const ui=uiBaselineRef(data);
165
+ ensure(input.scope.ui_mode!=='not-applicable','战略交接含 UI 基线,不能声明为无 UI 影响');
166
+ ensure(uiBaselineKind(data)===(input.scope.ui_mode==='prototype'?'prototype':'existing-ui-baseline'),'战略交接与预检 UI 来源类型不一致');
167
+ if(assets.ui_baseline)ensure(assets.ui_baseline.ref===`${ui.persisted_ref}/${ui.manifest_ref}`,'战略交接 UI 基线与预检输入不一致');
168
+ } else if(key==='ui_baseline') {
169
+ if(input.scope.ui_mode==='prototype') {
170
+ const {validateVisualBaseline}=await import('../../.agents/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs');
171
+ const result=await validateVisualBaseline(data,{bundleRoot:path.dirname(safe(root,binding.ref))});
172
+ ensure(!result.errors.length,result.errors.join('; '));
173
+ } else {
174
+ const modulePath=path.join(ROOT,'scripts/lib/existing-ui-baseline.mjs');
175
+ ensure(existsSync(modulePath),'不支持 existing-ui-baseline 的当前运行时');
176
+ const module=await import('./existing-ui-baseline.mjs');
177
+ const verify=module.validateExistingUiBaseline||module.verifyExistingUiBaseline;
178
+ ensure(typeof verify==='function','不支持既有 UI 基线验证接口');
179
+ const result=await verify(data,{bundleRoot:path.dirname(safe(root,binding.ref))});
180
+ ensure(!result?.errors?.length&&result?.result!=='blocked',JSON.stringify(result));
181
+ }
182
+ ensure((input.scope.ui_mode==='prototype'?data.status==='approved':['ready-for-human','approved'].includes(data.status))&&data.baseline_id===binding.id&&data.version===binding.version,'UI 基线身份、版本或待确认状态不一致');
183
+ if(data.openapi&&assets.openapi)ensure(data.openapi.digest===assets.openapi.digest,'UI 基线与冻结 API 字节不一致');
184
+ await approved(root,binding,['gate.product-design-approved','gate.user-confirmation']);
185
+ } else if(key==='build') {
186
+ // Same build shape used by backend-delivery, captured independently before packaging.
187
+ const deliverySchema=read(safe(ROOT,'docs/process/schemas/backend-delivery.schema.json'));
188
+ const buildRules=deliverySchema.properties.build;
189
+ // Validate in memory to avoid writing a generated schema: check the published required fields.
190
+ for(const field of buildRules.required||[])ensure(Object.hasOwn(data,field),`构建缺少交付协议字段: ${field}`);
191
+ ensure(/^[a-f0-9]{40}$/.test(data.source_commit),'构建 source_commit 必须为固定 Git 提交');
192
+ if(documents.manifest?.source?.base_commit)ensure(documents.manifest.source.base_commit===data.source_commit,'构建与观测 manifest 的固定源码提交不一致');
193
+ ensure(/^sha256:[0-9a-f]{64}$/.test(data.artifact_digest),'构建制品摘要无效');
194
+ ensure(Object.keys(data).every(key=>Object.hasOwn(buildRules.properties,key)),'构建包含交付协议未定义字段');
195
+ } else if(key==='deployment') {
196
+ schema(data,'docs/process/schemas/backend-delivery-verification.schema.json');
197
+ ensure(data.kind==='backend-deployment','部署证据 kind 必须为 backend-deployment');
198
+ for(const result of data.results) {
199
+ ensure(result.exit_code===0&&Number.isFinite(Date.parse(result.executed_at))&&Date.parse(result.executed_at)<=Date.now()+60000,'部署执行结果或时间无效');
200
+ result.evidence.forEach(file=>fileBinding(root,file));
201
+ }
202
+ if(documents.backend_delivery) {
203
+ const {backendDeliveryBasis}=await import('./backend-delivery.mjs');
204
+ ensure(data.subject_digest===backendDeliveryBasis(documents.backend_delivery),'stale: 部署验证未绑定当前交付依据');
205
+ }
206
+ } else if(key==='backend_delivery') {
207
+ schema(data,`docs/process/schemas/backend-delivery${data.schema_version===2?'-v2':''}.schema.json`);
208
+ ensure(data.scope.slice_id===input.scope.slice_id&&input.scope.operation_ids.every(id=>data.scope.operation_ids.includes(id)),'后端交付切片/接口范围不匹配');
209
+ for(const asset of ['openapi','slice_contract'])if(assets[asset])ensure(data[asset].ref===assets[asset].ref&&data[asset].digest===assets[asset].digest,'后端交付引用与预检输入不一致');
210
+ for(const asset of [data.environment.test_data,...Object.values(data.verification)])fileBinding(root,asset);
211
+ if(assets.build)ensure(same(data.build,documents.build),'后端交付构建与预检构建不一致');
212
+ if(assets.deployment)ensure(data.verification.deployment.ref===assets.deployment.ref&&data.verification.deployment.digest===assets.deployment.digest,'后端交付部署验证不一致');
213
+ const {inspectBackendDelivery}=await import('./backend-delivery.mjs');
214
+ await inspectBackendDelivery(root,binding.ref,{readOnly:true});
215
+ } else if(key==='local_authorization') {
216
+ throw new TypeError('不支持尚未确认的目标授权协议;须先执行 S3a 并使用确认后的只读授权验证入口');
217
+ }
218
+ }
219
+
220
+ function verifyArchitectureDocument(key,data,scope,root) {
221
+ ensure(data&&typeof data==='object'&&!Array.isArray(data),'原始架构资产必须是对象');
222
+ architecture.validateArchitectureIdentity(data.architecture_identity);
223
+ if(data.architecture_identity.schema_version!==2)return;
224
+ ensure(data.repository_id===scope.repository_id&&data.project_id===scope.project_id,'原始来源与目标仓库/项目不一致');
225
+ if(key==='repository_registration') {
226
+ ensure(data.status==='current','stale: 工程登记不是 current');
227
+ for(const field of ['local_worktree','project_root','owner','repository_url'])ensure(typeof data[field]==='string'&&data[field].trim(),`工程登记缺少 ${field}`);
228
+ ensure(Array.isArray(data.verification_commands)&&data.verification_commands.length,'工程登记缺少可执行验证入口');
229
+ } else {
230
+ ensure(data.schema_version===1&&data.kind===(key==='manifest'?'existing-project-observation':'existing-engineering-baseline'),'既有原始资产来源类型/版本不匹配');
231
+ ensure(data.source&&/^[a-f0-9]{40}$/.test(data.source.base_commit)&&Array.isArray(data.source.files)&&data.source.files.length&&Array.isArray(data.source.roots)&&data.source.roots.length,'原始资产缺少固定源码提交/文件集/根范围');
232
+ ensure(Array.isArray(data.build_units)&&data.build_units.length,'原始资产缺少实际构建单元');
233
+ if(key==='engineering_baseline') {
234
+ ensure(data.status==='current','stale: 工程基线不是 current');
235
+ fileBinding(root,data.boundary_review);
236
+ }
237
+ }
238
+ }