ai-delivery-workflow 0.2.2 → 0.4.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 (361) hide show
  1. package/README.md +45 -14
  2. package/bin/ai-delivery.mjs +181 -6
  3. package/bin/benchmark-autonomy.mjs +217 -0
  4. package/bin/release-check.mjs +382 -0
  5. package/docs/CLI-PARAMETER-REFERENCE.zh-CN.md +346 -0
  6. package/docs/CODEGRAPH-INTEGRATION.zh-CN.md +89 -0
  7. package/docs/DUAL-REPOSITORY-WORKSPACE.zh-CN.md +2 -2
  8. package/docs/EVOLUTION-MAINTENANCE.zh-CN.md +7 -7
  9. package/docs/EVOLUTION-USER-GUIDE.zh-CN.md +2 -2
  10. package/docs/FILE-REFERENCE.zh-CN.md +120 -39
  11. package/docs/MAINTENANCE-VERIFICATION.zh-CN.md +51 -0
  12. package/docs/MOCK-SYSTEM.zh-CN.md +141 -0
  13. package/docs/NPM-RELEASE-MANAGEMENT.zh-CN.md +106 -0
  14. package/docs/PROJECT-MANUAL.zh-CN.md +154 -95
  15. package/docs/PROTOTYPE-MIGRATION.zh-CN.md +164 -0
  16. package/docs/STATE-CLI-MAINTENANCE.zh-CN.md +35 -17
  17. package/docs/STATE-CLI-USER-GUIDE.zh-CN.md +115 -39
  18. package/docs/VIEWER-MAINTENANCE.zh-CN.md +101 -324
  19. package/docs/VIEWER-USER-GUIDE.zh-CN.md +68 -190
  20. package/docs/adr/0001-prototype-first-ui-governance.md +3 -0
  21. package/docs/adr/0002-microcks-as-default-mock-system.md +3 -0
  22. package/docs/adr/0003-workflow-manager-governed-control-surface.md +46 -0
  23. package/docs/adr/0004-operational-carbon-visual-standard.md +9 -0
  24. package/docs/adr/0005-prototype-management-as-routed-pages.md +7 -0
  25. package/docs/agents/domain.md +12 -0
  26. package/docs/agents/issue-tracker.md +27 -0
  27. package/docs/agents/triage-labels.md +11 -0
  28. package/docs/agents/workflow-manager-acceptance-matrix.md +93 -0
  29. package/docs/agents/workflow-manager-agent-interface.md +145 -0
  30. package/docs/agents/workflow-manager-current-state.md +205 -0
  31. package/lib/artifact-files.mjs +47 -0
  32. package/lib/codegraph.mjs +405 -0
  33. package/lib/delivery-state.mjs +3079 -198
  34. package/lib/frontend-guard.mjs +288 -0
  35. package/lib/fs-utils.mjs +53 -0
  36. package/lib/history-anchor.mjs +98 -0
  37. package/lib/mock-scenario-pack.mjs +687 -0
  38. package/lib/mock-system.mjs +897 -0
  39. package/lib/npm-package-result.mjs +11 -0
  40. package/lib/png-evidence.mjs +149 -0
  41. package/lib/project-bootstrap.mjs +96 -55
  42. package/lib/project-installer.mjs +1484 -164
  43. package/lib/project-migration.mjs +1193 -0
  44. package/lib/prototype-migration.mjs +713 -0
  45. package/lib/prototype-page-review.mjs +384 -0
  46. package/lib/prototype-review-target.mjs +334 -0
  47. package/lib/prototype-revision.mjs +812 -0
  48. package/lib/stable-value.mjs +15 -0
  49. package/lib/toml-hooks.mjs +69 -0
  50. package/lib/ui-acceptance.mjs +565 -0
  51. package/lib/visual-standard.mjs +232 -0
  52. package/lib/workflow-manager-contracts.mjs +367 -0
  53. package/lib/workflow-manager-facts.mjs +778 -0
  54. package/lib/workflow-manager-governance.mjs +514 -0
  55. package/lib/workflow-manager-mock.mjs +183 -0
  56. package/lib/workflow-manager-prototypes.mjs +1847 -0
  57. package/lib/workflow-manager-public/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  58. package/lib/workflow-manager-public/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  59. package/lib/workflow-manager-public/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  60. package/lib/workflow-manager-public/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  61. package/lib/workflow-manager-public/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  62. package/lib/workflow-manager-public/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  63. package/lib/workflow-manager-public/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  64. package/lib/workflow-manager-public/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  65. package/lib/workflow-manager-public/assets/index-Chlg6r8f.css +1 -0
  66. package/lib/workflow-manager-public/assets/index-E-hGB5ZD.js +13 -0
  67. package/lib/workflow-manager-public/fonts/OFL-1.1.txt +90 -0
  68. package/lib/workflow-manager-public/index.html +14 -0
  69. package/lib/workflow-manager-systems.mjs +519 -0
  70. package/lib/workflow-manager-transaction.mjs +397 -0
  71. package/lib/workflow-manager.mjs +745 -0
  72. package/package.json +8 -2
  73. package/skills/ai-delivery-assemble-release/agents/openai.yaml +3 -3
  74. package/skills/ai-delivery-assemble-release/assets/release-material-index.csv +1 -1
  75. package/skills/ai-delivery-assemble-release/assets/version-inclusion.csv +1 -1
  76. package/skills/ai-delivery-bootstrap/SKILL.md +4 -4
  77. package/skills/ai-delivery-bootstrap/agents/openai.yaml +3 -3
  78. package/skills/ai-delivery-bootstrap/references/bootstrap-contract.md +4 -2
  79. package/skills/ai-delivery-checkpoint-task/SKILL.md +17 -3
  80. package/skills/ai-delivery-checkpoint-task/agents/openai.yaml +3 -3
  81. package/skills/ai-delivery-checkpoint-task/assets/runtime-template/task.json +7 -1
  82. package/skills/ai-delivery-checkpoint-task/references/checkpoint-contract.md +22 -8
  83. package/skills/ai-delivery-checkpoint-task/scripts/hook-event.mjs +91 -0
  84. package/skills/ai-delivery-checkpoint-task/scripts/task-state.mjs +1086 -51
  85. package/skills/ai-delivery-close-version/SKILL.md +9 -8
  86. package/skills/ai-delivery-close-version/agents/openai.yaml +3 -3
  87. package/skills/ai-delivery-close-version/assets/version-lineage.yaml +1 -1
  88. package/skills/ai-delivery-close-version/references/version-closeout-contract.md +2 -2
  89. package/skills/ai-delivery-define-product/SKILL.md +8 -4
  90. package/skills/ai-delivery-define-product/agents/openai.yaml +4 -4
  91. package/skills/ai-delivery-define-product/references/product-contract.md +2 -2
  92. package/skills/ai-delivery-deploy-production/agents/openai.yaml +3 -3
  93. package/skills/ai-delivery-design-architecture/SKILL.md +9 -5
  94. package/skills/ai-delivery-design-architecture/assets/architecture-template/prototype-architecture-validation.yaml +16 -16
  95. package/skills/ai-delivery-design-architecture/references/architecture-contract.md +1 -1
  96. package/skills/ai-delivery-design-experience/SKILL.md +36 -30
  97. package/skills/ai-delivery-design-experience/agents/openai.yaml +4 -4
  98. package/skills/ai-delivery-design-experience/assets/experience-template/LEGACY-COMPATIBILITY.md +16 -0
  99. package/skills/ai-delivery-design-experience/assets/experience-template/design-reference-selection.yaml +32 -0
  100. package/skills/ai-delivery-design-experience/assets/experience-template/experience-baseline.yaml +2 -2
  101. package/skills/ai-delivery-design-experience/assets/experience-template/formal-ui-confirmation.yaml +16 -3
  102. package/skills/ai-delivery-design-experience/assets/experience-template/frontend-change-scope.yaml +30 -0
  103. package/skills/ai-delivery-design-experience/assets/experience-template/interaction-contract.csv +1 -1
  104. package/skills/ai-delivery-design-experience/assets/experience-template/mock-admin-export.yaml +8 -0
  105. package/skills/ai-delivery-design-experience/assets/experience-template/mock-scenario-mappings.csv +1 -0
  106. package/skills/ai-delivery-design-experience/assets/experience-template/mock-scenario-pack-request.yaml +24 -0
  107. package/skills/ai-delivery-design-experience/assets/experience-template/product-prototype-reconciliation.yaml +2 -1
  108. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-adjustment-log.csv +1 -1
  109. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-component-bindings.csv +1 -0
  110. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-component-candidates.csv +1 -0
  111. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-components.csv +1 -0
  112. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-confirmation.yaml +18 -18
  113. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-data-contracts.csv +1 -0
  114. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-impact-assessment.yaml +5 -5
  115. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-implementation-bindings.csv +1 -0
  116. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-manifest.yaml +2 -0
  117. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-pages.csv +1 -0
  118. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-product-coverage.csv +1 -1
  119. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-comments.csv +1 -1
  120. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-decision.yaml +13 -12
  121. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-scenes.csv +1 -0
  122. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-sessions.csv +1 -1
  123. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-unit-pages.csv +1 -0
  124. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-review-units.csv +1 -0
  125. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-revision.yaml +88 -0
  126. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-routes.csv +1 -0
  127. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-set.yaml +2 -0
  128. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-source-inspection-snapshots.csv +1 -0
  129. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-terminals.csv +1 -0
  130. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-tool-candidates.csv +1 -1
  131. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-tool-selection.yaml +2 -3
  132. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-traceability.csv +1 -1
  133. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-trigger-conditions.csv +1 -0
  134. package/skills/ai-delivery-design-experience/assets/experience-template/prototype.yaml +6 -0
  135. package/skills/ai-delivery-design-experience/assets/experience-template/ui-framework-selection.yaml +17 -1
  136. package/skills/ai-delivery-design-experience/assets/experience-template/ui-handoff.yaml +6 -5
  137. package/skills/ai-delivery-design-experience/assets/experience-template/ui-mapping.csv +1 -0
  138. package/skills/ai-delivery-design-experience/assets/experience-template/visual-direction.yaml +4 -0
  139. package/skills/ai-delivery-design-experience/assets/experience-template/visual-reference-manifest.yaml +23 -0
  140. package/skills/ai-delivery-design-experience/assets/experience-template/visual-reference.html +163 -0
  141. package/skills/ai-delivery-design-experience/assets/experience-template/visual-system-confirmation.yaml +11 -0
  142. package/skills/ai-delivery-design-experience/references/experience-contract.md +26 -27
  143. package/skills/ai-delivery-design-experience/references/prototype-management-contract.md +42 -21
  144. package/skills/ai-delivery-design-tests/SKILL.md +13 -7
  145. package/skills/ai-delivery-design-tests/assets/test-design-template/prototype-test-scope.csv +1 -1
  146. package/skills/ai-delivery-design-tests/assets/test-design-template/prototype-test-validation.yaml +17 -16
  147. package/skills/ai-delivery-design-tests/references/test-design-contract.md +7 -5
  148. package/skills/ai-delivery-develop-iteration/SKILL.md +18 -11
  149. package/skills/ai-delivery-develop-iteration/agents/openai.yaml +3 -3
  150. package/skills/ai-delivery-develop-iteration/assets/development-template/candidate-manifest.yaml +36 -0
  151. package/skills/ai-delivery-develop-iteration/assets/development-template/task-prototype-traceability.csv +1 -1
  152. package/skills/ai-delivery-develop-iteration/references/development-contract.md +5 -3
  153. package/skills/ai-delivery-evolve-workflow/agents/openai.yaml +3 -3
  154. package/skills/ai-delivery-execute-test/SKILL.md +6 -2
  155. package/skills/ai-delivery-execute-test/agents/openai.yaml +4 -4
  156. package/skills/ai-delivery-execute-work-package/SKILL.md +10 -3
  157. package/skills/ai-delivery-execute-work-package/agents/openai.yaml +4 -4
  158. package/skills/ai-delivery-execute-work-package/references/work-package-contract.md +1 -1
  159. package/skills/ai-delivery-manage-git/agents/openai.yaml +3 -3
  160. package/skills/ai-delivery-manage-standards/agents/openai.yaml +4 -4
  161. package/skills/ai-delivery-orchestrate/SKILL.md +47 -29
  162. package/skills/ai-delivery-orchestrate/agents/openai.yaml +3 -3
  163. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/control/migrations/.gitkeep +0 -0
  164. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/control/repairs/.gitkeep +0 -0
  165. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/delivery/project.yaml +1 -0
  166. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/delivery/workflow-state.yaml +2 -0
  167. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/frontend-guard/frontend-guard.mjs +50 -0
  168. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/hooks/hook-event.mjs +91 -0
  169. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/manager/manager.mjs +64 -0
  170. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/manager/start.cmd +4 -0
  171. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/manager/start.ps1 +2 -0
  172. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/{viewer → manager}/start.sh +1 -1
  173. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/mock/mock.mjs +50 -0
  174. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/ui-acceptance/ui-acceptance.mjs +37 -0
  175. package/skills/ai-delivery-orchestrate/assets/project-template/AGENTS.md +32 -6
  176. package/skills/ai-delivery-orchestrate/assets/project-template/gitignore.template +1 -0
  177. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/CONTRIBUTING.md +25 -0
  178. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/LICENSE +21 -0
  179. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/README.md +250 -0
  180. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/UPSTREAM.yaml +19 -0
  181. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/airbnb/DESIGN.md +545 -0
  182. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/airbnb/README.md +5 -0
  183. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/airtable/DESIGN.md +554 -0
  184. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/airtable/README.md +5 -0
  185. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/apple/DESIGN.md +562 -0
  186. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/apple/README.md +5 -0
  187. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/binance/DESIGN.md +634 -0
  188. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/binance/README.md +5 -0
  189. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/bmw/DESIGN.md +544 -0
  190. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/bmw/README.md +5 -0
  191. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/bmw-m/DESIGN.md +503 -0
  192. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/bmw-m/README.md +5 -0
  193. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/bugatti/DESIGN.md +454 -0
  194. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/bugatti/README.md +5 -0
  195. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/cal/DESIGN.md +542 -0
  196. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/cal/README.md +5 -0
  197. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/claude/DESIGN.md +589 -0
  198. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/claude/README.md +5 -0
  199. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/clay/DESIGN.md +541 -0
  200. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/clay/README.md +5 -0
  201. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/clickhouse/DESIGN.md +544 -0
  202. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/clickhouse/README.md +5 -0
  203. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/cohere/DESIGN.md +451 -0
  204. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/cohere/README.md +5 -0
  205. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/coinbase/DESIGN.md +570 -0
  206. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/coinbase/README.md +5 -0
  207. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/composio/DESIGN.md +506 -0
  208. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/composio/README.md +5 -0
  209. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/cursor/DESIGN.md +537 -0
  210. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/cursor/README.md +5 -0
  211. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/dell-1996/DESIGN.md +632 -0
  212. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/dell-1996/README.md +5 -0
  213. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/elevenlabs/DESIGN.md +504 -0
  214. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/elevenlabs/README.md +5 -0
  215. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/expo/DESIGN.md +526 -0
  216. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/expo/README.md +5 -0
  217. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/ferrari/DESIGN.md +531 -0
  218. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/ferrari/README.md +5 -0
  219. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/figma/DESIGN.md +578 -0
  220. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/figma/README.md +5 -0
  221. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/framer/DESIGN.md +544 -0
  222. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/framer/README.md +5 -0
  223. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/hashicorp/DESIGN.md +575 -0
  224. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/hashicorp/README.md +5 -0
  225. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/hp/DESIGN.md +670 -0
  226. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/hp/README.md +5 -0
  227. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/ibm/DESIGN.md +550 -0
  228. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/ibm/README.md +5 -0
  229. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/intercom/DESIGN.md +546 -0
  230. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/intercom/README.md +5 -0
  231. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/kraken/DESIGN.md +125 -0
  232. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/kraken/README.md +5 -0
  233. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/lamborghini/DESIGN.md +288 -0
  234. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/lamborghini/README.md +5 -0
  235. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/linear.app/DESIGN.md +548 -0
  236. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/linear.app/README.md +5 -0
  237. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/lovable/DESIGN.md +298 -0
  238. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/lovable/README.md +5 -0
  239. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/mastercard/DESIGN.md +365 -0
  240. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/mastercard/README.md +5 -0
  241. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/meta/DESIGN.md +683 -0
  242. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/meta/README.md +5 -0
  243. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/minimax/DESIGN.md +746 -0
  244. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/minimax/README.md +5 -0
  245. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/mintlify/DESIGN.md +852 -0
  246. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/mintlify/README.md +5 -0
  247. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/miro/DESIGN.md +825 -0
  248. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/miro/README.md +5 -0
  249. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/mistral.ai/DESIGN.md +773 -0
  250. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/mistral.ai/README.md +5 -0
  251. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/mongodb/DESIGN.md +767 -0
  252. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/mongodb/README.md +5 -0
  253. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/nike/DESIGN.md +575 -0
  254. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/nike/README.md +5 -0
  255. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/nintendo-2001/DESIGN.md +649 -0
  256. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/nintendo-2001/README.md +5 -0
  257. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/notion/DESIGN.md +821 -0
  258. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/notion/README.md +5 -0
  259. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/nvidia/DESIGN.md +640 -0
  260. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/nvidia/README.md +5 -0
  261. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/ollama/DESIGN.md +539 -0
  262. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/ollama/README.md +5 -0
  263. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/opencode.ai/DESIGN.md +521 -0
  264. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/opencode.ai/README.md +5 -0
  265. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/pinterest/DESIGN.md +597 -0
  266. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/pinterest/README.md +5 -0
  267. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/playstation/DESIGN.md +661 -0
  268. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/playstation/README.md +5 -0
  269. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/posthog/DESIGN.md +690 -0
  270. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/posthog/README.md +5 -0
  271. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/raycast/DESIGN.md +669 -0
  272. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/raycast/README.md +5 -0
  273. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/renault/DESIGN.md +589 -0
  274. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/renault/README.md +5 -0
  275. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/replicate/DESIGN.md +616 -0
  276. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/replicate/README.md +5 -0
  277. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/resend/DESIGN.md +585 -0
  278. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/resend/README.md +5 -0
  279. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/revolut/DESIGN.md +636 -0
  280. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/revolut/README.md +5 -0
  281. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/runwayml/DESIGN.md +244 -0
  282. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/runwayml/README.md +5 -0
  283. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/sanity/DESIGN.md +357 -0
  284. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/sanity/README.md +5 -0
  285. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/sentry/DESIGN.md +551 -0
  286. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/sentry/README.md +5 -0
  287. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/shopify/DESIGN.md +516 -0
  288. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/shopify/README.md +5 -0
  289. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/slack/DESIGN.md +482 -0
  290. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/spacex/DESIGN.md +363 -0
  291. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/spacex/README.md +5 -0
  292. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/spotify/DESIGN.md +246 -0
  293. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/spotify/README.md +5 -0
  294. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/starbucks/DESIGN.md +580 -0
  295. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/starbucks/README.md +5 -0
  296. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/stripe/DESIGN.md +487 -0
  297. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/stripe/README.md +5 -0
  298. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/supabase/DESIGN.md +462 -0
  299. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/supabase/README.md +5 -0
  300. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/superhuman/DESIGN.md +448 -0
  301. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/superhuman/README.md +5 -0
  302. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/tesla/DESIGN.md +286 -0
  303. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/tesla/README.md +5 -0
  304. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/theverge/DESIGN.md +339 -0
  305. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/theverge/README.md +5 -0
  306. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/together.ai/DESIGN.md +633 -0
  307. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/together.ai/README.md +5 -0
  308. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/uber/DESIGN.md +636 -0
  309. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/uber/README.md +5 -0
  310. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/vercel/DESIGN.md +736 -0
  311. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/vercel/README.md +5 -0
  312. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/vodafone/DESIGN.md +538 -0
  313. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/vodafone/README.md +5 -0
  314. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/voltagent/DESIGN.md +521 -0
  315. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/voltagent/README.md +5 -0
  316. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/warp/DESIGN.md +526 -0
  317. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/warp/README.md +5 -0
  318. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/webflow/DESIGN.md +588 -0
  319. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/webflow/README.md +5 -0
  320. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/wired/DESIGN.md +497 -0
  321. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/wired/README.md +5 -0
  322. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/wise/DESIGN.md +544 -0
  323. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/wise/README.md +5 -0
  324. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/x.ai/DESIGN.md +465 -0
  325. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/x.ai/README.md +5 -0
  326. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/zapier/DESIGN.md +537 -0
  327. package/skills/ai-delivery-orchestrate/assets/project-template/ref/ui/design-md/zapier/README.md +5 -0
  328. package/skills/ai-delivery-orchestrate/references/formal-state-contract.md +65 -20
  329. package/skills/ai-delivery-orchestrate/references/workflow-model.md +7 -5
  330. package/skills/ai-delivery-orchestrate-release/SKILL.md +2 -2
  331. package/skills/ai-delivery-orchestrate-release/agents/openai.yaml +4 -4
  332. package/skills/ai-delivery-orchestrate-release/references/release-workflow-contract.md +1 -1
  333. package/skills/ai-delivery-plan-iteration/SKILL.md +11 -4
  334. package/skills/ai-delivery-plan-iteration/agents/openai.yaml +4 -4
  335. package/skills/ai-delivery-plan-iteration/assets/planning-template/scope-manifest.yaml +17 -0
  336. package/skills/ai-delivery-plan-iteration/assets/planning-template/slice-prototype-traceability.csv +1 -1
  337. package/skills/ai-delivery-plan-iteration/references/planning-contract.md +8 -2
  338. package/skills/ai-delivery-prepare-platform/SKILL.md +5 -1
  339. package/skills/ai-delivery-prepare-platform/agents/openai.yaml +4 -4
  340. package/skills/ai-delivery-prepare-release/agents/openai.yaml +4 -4
  341. package/skills/ai-delivery-review-change/SKILL.md +14 -4
  342. package/skills/ai-delivery-review-change/agents/openai.yaml +4 -4
  343. package/skills/ai-delivery-review-change/references/review-contract.md +6 -0
  344. package/skills/ai-delivery-triage-failure/SKILL.md +5 -1
  345. package/skills/ai-delivery-triage-failure/agents/openai.yaml +4 -4
  346. package/skills/ai-delivery-validate-artifacts/SKILL.md +6 -3
  347. package/skills/ai-delivery-validate-artifacts/agents/openai.yaml +4 -4
  348. package/skills/ai-delivery-validate-artifacts/references/artifact-contract.md +13 -13
  349. package/skills/ai-delivery-verify-candidate/SKILL.md +13 -6
  350. package/skills/ai-delivery-verify-candidate/agents/openai.yaml +3 -3
  351. package/skills/ai-delivery-verify-candidate/assets/verification-template/ui-acceptance-request.yaml +19 -0
  352. package/skills/ai-delivery-verify-candidate/assets/verification-template/ui-acceptance-results.yaml +53 -0
  353. package/skills/ai-delivery-verify-candidate/references/candidate-verification-contract.md +10 -2
  354. package/skills/ai-delivery-verify-production/agents/openai.yaml +3 -3
  355. package/skills/ai-delivery-design-experience/assets/experience-template/low-fidelity-confirmation.yaml +0 -32
  356. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/public/app.js +0 -546
  357. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/public/index.html +0 -199
  358. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/public/styles.css +0 -529
  359. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/server.mjs +0 -780
  360. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/start.cmd +0 -4
  361. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/viewer/start.ps1 +0 -2
@@ -0,0 +1,15 @@
1
+ import crypto from "node:crypto";
2
+
3
+ export function stableValue(value) {
4
+ if (Array.isArray(value)) return value.map(stableValue);
5
+ if (!value || typeof value !== "object") return value;
6
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])]));
7
+ }
8
+
9
+ export function sameStableValue(left, right) {
10
+ return JSON.stringify(stableValue(left)) === JSON.stringify(stableValue(right));
11
+ }
12
+
13
+ export function stableValueChecksum(value) {
14
+ return crypto.createHash("sha256").update(JSON.stringify(stableValue(value))).digest("hex");
15
+ }
@@ -49,6 +49,55 @@ function replaceFeatureHooksFalse(text) {
49
49
  return `${text.slice(0, sectionStart)}${section.replace(assignment, "$1true$2")}${text.slice(sectionEnd)}`;
50
50
  }
51
51
 
52
+ function removeDeliveryHandlerBlocks(text) {
53
+ const newline = text.includes("\r\n") ? "\r\n" : "\n";
54
+ const lines = text.split(/\r?\n/);
55
+ const groups = [];
56
+ for (let index = 0; index < lines.length; index += 1) {
57
+ if (!/^\s*\[\[hooks\.[^.\]]+\]\]\s*$/.test(lines[index])) continue;
58
+ const end = lines.findIndex((line, candidate) =>
59
+ candidate > index && /^\s*\[\[hooks\.[^.\]]+\]\]\s*$/.test(line));
60
+ groups.push({ start: index, end: end === -1 ? lines.length : end });
61
+ index = (end === -1 ? lines.length : end) - 1;
62
+ }
63
+ let changed = false;
64
+ const output = [];
65
+ let cursor = 0;
66
+ for (const group of groups) {
67
+ output.push(...lines.slice(cursor, group.start));
68
+ const groupLines = lines.slice(group.start, group.end);
69
+ const remove = new Set();
70
+ for (let index = 0; index < groupLines.length; index += 1) {
71
+ if (!/^\s*\[\[hooks\.[^.\]]+\.hooks\]\]\s*$/.test(groupLines[index])) continue;
72
+ let end = index + 1;
73
+ while (end < groupLines.length && !/^\s*\[\[/.test(groupLines[end])) end += 1;
74
+ const handlerLines = groupLines.slice(index, end);
75
+ if (handlerLines.some((line) =>
76
+ /^\s*command(?:Windows)?\s*=/.test(line) && line.includes(`"${HOOK_COMMAND}"`))) {
77
+ for (let candidate = index; candidate < end; candidate += 1) {
78
+ if (candidate === index
79
+ || (groupLines[candidate].trim() && !/^\s*#/.test(groupLines[candidate]))) {
80
+ remove.add(candidate);
81
+ }
82
+ }
83
+ changed = true;
84
+ }
85
+ index = end - 1;
86
+ }
87
+ const retained = groupLines.filter((_, index) => !remove.has(index));
88
+ const onlyEmptyManagedMatcher = retained.every((line) =>
89
+ !line.trim()
90
+ || /^\s*#/.test(line)
91
+ || /^\s*\[\[hooks\.[^.\]]+\]\]\s*$/.test(line)
92
+ || /^\s*matcher\s*=\s*""\s*$/.test(line));
93
+ const hasUserComment = retained.some((line) => /^\s*#/.test(line));
94
+ if (!onlyEmptyManagedMatcher || hasUserComment || !remove.size) output.push(...retained);
95
+ cursor = group.end;
96
+ }
97
+ output.push(...lines.slice(cursor));
98
+ return { text: output.join(newline), changed };
99
+ }
100
+
52
101
  export function mergeHookConfig(existing, { force = false } = {}) {
53
102
  const newline = existing.includes("\r\n") ? "\r\n" : "\n";
54
103
  let text = existing.trimEnd();
@@ -57,6 +106,11 @@ export function mergeHookConfig(existing, { force = false } = {}) {
57
106
  text = text.replaceAll(LEGACY_HOOK_COMMAND, HOOK_COMMAND);
58
107
  actions.push("migrate AI delivery Hook command into .workflow/tools");
59
108
  }
109
+ if (force) {
110
+ const refreshed = removeDeliveryHandlerBlocks(text);
111
+ text = refreshed.text.trimEnd();
112
+ if (refreshed.changed) actions.push("refresh AI delivery Hook handlers");
113
+ }
60
114
  let config = text ? parse(text) : {};
61
115
 
62
116
  if (config.features?.hooks === false && !force) {
@@ -95,3 +149,18 @@ export function inspectHookConfig(text) {
95
149
  registeredEvents: HOOK_EVENTS.filter((event) => hasDeliveryHandler(config, event)),
96
150
  };
97
151
  }
152
+
153
+ export function managedHookConfigProjection(text) {
154
+ const config = parse(text);
155
+ return {
156
+ hooks_enabled: config.features?.hooks ?? null,
157
+ events: HOOK_EVENTS.map((event) => ({
158
+ event,
159
+ handlers: (config.hooks?.[event] || []).flatMap((matcher) =>
160
+ (matcher.hooks || [])
161
+ .filter((handler) => handler?.command === HOOK_COMMAND
162
+ || handler?.commandWindows === HOOK_COMMAND)
163
+ .map((handler) => ({ matcher: matcher.matcher ?? null, handler }))),
164
+ })),
165
+ };
166
+ }
@@ -0,0 +1,565 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {
4
+ readYamlMapping,
5
+ requireSha256,
6
+ resolveContainedPath,
7
+ sha256File,
8
+ } from "./artifact-files.mjs";
9
+ import { parsePrototypeCatalogCsv } from "./prototype-revision.mjs";
10
+ import { buildPrototypeReviewTarget } from "./prototype-review-target.mjs";
11
+ import { validateFrozenMockScenarioPack } from "./mock-scenario-pack.mjs";
12
+ import { mockProtocolExpectation, validateMockDefinition } from "./mock-system.mjs";
13
+ import { verifyVisualPngEvidence } from "./png-evidence.mjs";
14
+ import { sameStableValue, stableValueChecksum } from "./stable-value.mjs";
15
+
16
+ const candidateAssuranceNode = "05-candidate-assurance";
17
+ const defaultViewports = [
18
+ { viewport_id: "mobile", width: 390, height: 844 },
19
+ { viewport_id: "tablet", width: 768, height: 1024 },
20
+ { viewport_id: "desktop", width: 1440, height: 900 },
21
+ ];
22
+ const traceabilityHeader = [
23
+ "task_id", "prototype_revision_id", "source_revision", "terminal_id", "page_id",
24
+ "review_scene_id", "prototype_element_id", "ui_mapping_id", "ui_component_id",
25
+ "frontend_source_ref", "acceptance_id",
26
+ ].join(",");
27
+
28
+ function boundFile(root, binding, label) {
29
+ if (binding?.algorithm !== "sha256") throw new Error(`${label} must use sha256.`);
30
+ const file = resolveContainedPath(root, binding.ref, label);
31
+ const checksum = requireSha256(binding.checksum, label);
32
+ if (!fs.existsSync(file) || !fs.statSync(file).isFile() || sha256File(file) !== checksum) {
33
+ throw new Error(`${label} checksum mismatch.`);
34
+ }
35
+ return { file, checksum };
36
+ }
37
+
38
+ function registeredArtifact(projectRoot, registry, artifactId, type, checksum, status) {
39
+ const artifact = (registry.artifacts || []).find((item) =>
40
+ (item.artifact_id || item.id) === artifactId);
41
+ if (!artifact || artifact.artifact_type !== type || artifact.status !== status
42
+ || artifact.checksum_algorithm !== "sha256" || artifact.checksum !== checksum) {
43
+ throw new Error(`${type} ${artifactId} is not registered as ${status} with the exact checksum.`);
44
+ }
45
+ const file = resolveContainedPath(projectRoot, artifact.path, `${type} registry path`);
46
+ if (!fs.existsSync(file) || sha256File(file) !== checksum) {
47
+ throw new Error(`${type} ${artifactId} registry path checksum mismatch.`);
48
+ }
49
+ return { artifact, file };
50
+ }
51
+
52
+ function candidateBinding(projectRoot, request) {
53
+ const state = readYamlMapping(
54
+ path.join(projectRoot, ".workflow", "delivery", "workflow-state.yaml"),
55
+ "Workflow state",
56
+ );
57
+ const scope = state.schema_version >= 2 ? state.development_state : state;
58
+ const attempt = Number(scope?.node_attempts?.[candidateAssuranceNode] || 0);
59
+ const binding = scope?.node_candidate_bindings?.[candidateAssuranceNode];
60
+ if (scope?.scope_id !== request.iteration_id
61
+ || !scope?.active_nodes?.includes(candidateAssuranceNode)
62
+ || attempt !== Number(request.node_attempt)
63
+ || Number(binding?.node_attempt) !== attempt
64
+ || binding?.candidate_id !== request.candidate_id) {
65
+ throw new Error("UI Acceptance request does not bind the active Candidate Assurance attempt.");
66
+ }
67
+ const registry = readYamlMapping(
68
+ path.join(projectRoot, ".workflow", "delivery", "artifact-registry.yaml"),
69
+ "Artifact registry",
70
+ );
71
+ const candidateEntry = (registry.artifacts || []).find((item) =>
72
+ (item.artifact_id || item.id) === request.candidate_id);
73
+ if (!candidateEntry || candidateEntry.status !== "immutable"
74
+ || candidateEntry.artifact_type !== "candidate-manifest"
75
+ || candidateEntry.checksum_algorithm !== "sha256") {
76
+ throw new Error(`Candidate ${request.candidate_id} is not registered as immutable.`);
77
+ }
78
+ const candidateFile = resolveContainedPath(
79
+ projectRoot,
80
+ candidateEntry.path,
81
+ "Candidate manifest registry path",
82
+ );
83
+ const candidateChecksum = requireSha256(candidateEntry.checksum, "Candidate manifest");
84
+ if (!fs.existsSync(candidateFile) || sha256File(candidateFile) !== candidateChecksum) {
85
+ throw new Error("Immutable candidate manifest checksum mismatch.");
86
+ }
87
+ const candidate = readYamlMapping(candidateFile, "Candidate manifest");
88
+ if (candidate.candidate_id !== request.candidate_id
89
+ || candidate.iteration_id !== request.iteration_id
90
+ || candidate.status !== "immutable") {
91
+ throw new Error("Candidate manifest identity or immutable status is invalid.");
92
+ }
93
+ return { state, scope, registry, candidate, candidateFile, candidateChecksum, attempt };
94
+ }
95
+
96
+ function readTraceability(candidateBindingValue, target, revision) {
97
+ const { candidate, candidateFile } = candidateBindingValue;
98
+ const file = resolveContainedPath(
99
+ path.dirname(candidateFile),
100
+ candidate.task_prototype_traceability_ref,
101
+ "Candidate Prototype traceability",
102
+ );
103
+ const checksum = requireSha256(
104
+ candidate.task_prototype_traceability_checksum,
105
+ "Candidate Prototype traceability",
106
+ );
107
+ if (!fs.existsSync(file) || sha256File(file) !== checksum) {
108
+ throw new Error("Candidate Prototype traceability checksum mismatch.");
109
+ }
110
+ const parsed = parsePrototypeCatalogCsv(
111
+ fs.readFileSync(file, "utf8"),
112
+ "Candidate Prototype traceability",
113
+ );
114
+ if (parsed.header !== traceabilityHeader || !parsed.rows.length) {
115
+ throw new Error("Candidate Prototype traceability is empty or has an invalid header.");
116
+ }
117
+ const mappingFile = resolveContainedPath(
118
+ target.contentRoot,
119
+ revision.catalogs.ui_mapping.ref,
120
+ "Prototype UI Mapping catalog",
121
+ );
122
+ if (sha256File(mappingFile) !== requireSha256(
123
+ revision.catalogs.ui_mapping.checksum,
124
+ "Prototype UI Mapping catalog",
125
+ ) || candidate.ui_mapping_checksum !== revision.catalogs.ui_mapping.checksum) {
126
+ throw new Error("Candidate UI Mapping checksum mismatch.");
127
+ }
128
+ const mappings = parsePrototypeCatalogCsv(
129
+ fs.readFileSync(mappingFile, "utf8"),
130
+ "Prototype UI Mapping catalog",
131
+ ).rows.filter((item) => item.lifecycle_status === "active");
132
+ const mappingById = new Map(mappings.map((item) => [item.ui_mapping_id, item]));
133
+ const pageIds = new Set();
134
+ const explicitSceneIds = new Set();
135
+ for (const row of parsed.rows) {
136
+ const mapping = mappingById.get(row.ui_mapping_id);
137
+ if (row.prototype_revision_id !== target.targetId
138
+ || row.source_revision !== revision.source.revision
139
+ || !mapping
140
+ || mapping.page_id !== row.page_id
141
+ || mapping.review_scene_id !== row.review_scene_id) {
142
+ throw new Error(`Candidate Prototype traceability row is inconsistent: ${row.task_id}.`);
143
+ }
144
+ pageIds.add(row.page_id);
145
+ if (row.review_scene_id) explicitSceneIds.add(row.review_scene_id);
146
+ }
147
+ const scenes = target.reviewScenes.filter((scene) => pageIds.has(scene.page_id));
148
+ if (explicitSceneIds.size && scenes.some((scene) => !explicitSceneIds.has(scene.review_scene_id))) {
149
+ throw new Error("Candidate Prototype traceability does not cover every Scene of an affected Page.");
150
+ }
151
+ return {
152
+ checksum,
153
+ pageIds: [...pageIds].sort(),
154
+ scenes: scenes.sort((left, right) => left.review_scene_id.localeCompare(right.review_scene_id)),
155
+ mappings,
156
+ };
157
+ }
158
+
159
+ function acceptancePolicy(experienceRoot) {
160
+ const confirmation = readYamlMapping(
161
+ path.join(experienceRoot, "visual-system-confirmation.yaml"),
162
+ "Visual Standard confirmation",
163
+ );
164
+ const directionBinding = boundFile(experienceRoot, {
165
+ ref: confirmation.visual_direction_ref,
166
+ algorithm: "sha256",
167
+ checksum: confirmation.visual_direction_checksum,
168
+ }, "Visual direction");
169
+ const direction = readYamlMapping(directionBinding.file, "Visual direction");
170
+ const configured = direction.responsive_breakpoints;
171
+ const viewports = Array.isArray(configured) && configured.length ? configured : defaultViewports;
172
+ const normalized = viewports.map((item) => ({
173
+ viewport_id: String(item?.viewport_id || ""),
174
+ width: Number(item?.width),
175
+ height: Number(item?.height),
176
+ }));
177
+ if (normalized.some((item) => !/^[a-z][a-z0-9-]*$/.test(item.viewport_id)
178
+ || !Number.isInteger(item.width) || item.width <= 0
179
+ || !Number.isInteger(item.height) || item.height <= 0)
180
+ || new Set(normalized.map((item) => item.viewport_id)).size !== normalized.length) {
181
+ throw new Error("Approved Visual Standard responsive breakpoints are invalid.");
182
+ }
183
+ const maxDiff = direction.ui_acceptance?.max_diff_pixel_ratio ?? 0.001;
184
+ if (typeof maxDiff !== "number" || maxDiff < 0 || maxDiff > 1) {
185
+ throw new Error("Approved maxDiffPixelRatio is invalid.");
186
+ }
187
+ return { viewports: normalized, max_diff_pixel_ratio: maxDiff };
188
+ }
189
+
190
+ function pinnedRunner(runner) {
191
+ if (runner?.engine !== "playwright"
192
+ || !/^\d+\.\d+\.\d+$/.test(String(runner.package_version || ""))
193
+ || !new Set(["chromium", "firefox", "webkit"]).has(runner.browser)
194
+ || !/^\d+(?:\.\d+)+$/.test(String(runner.browser_version || ""))
195
+ || !String(runner.environment_image || "")
196
+ || !/^sha256:[a-f0-9]{64}$/.test(String(runner.environment_digest || ""))) {
197
+ throw new Error("UI Acceptance requires an exact pinned Playwright environment.");
198
+ }
199
+ return {
200
+ engine: runner.engine,
201
+ package_version: runner.package_version,
202
+ browser: runner.browser,
203
+ browser_version: runner.browser_version,
204
+ environment_image: runner.environment_image,
205
+ environment_digest: runner.environment_digest,
206
+ };
207
+ }
208
+
209
+ export function planUiAcceptance(projectRoot, options = {}) {
210
+ const root = path.resolve(projectRoot);
211
+ const requestFile = resolveContainedPath(root, options.request, "UI Acceptance request");
212
+ const request = readYamlMapping(requestFile, "UI Acceptance request");
213
+ if (request.schema_version !== 1 || request.artifact_type !== "ui-acceptance-request"
214
+ || !request.iteration_id || !request.candidate_id || !Number.isInteger(request.node_attempt)) {
215
+ throw new Error("UI Acceptance request identity or Candidate Assurance binding is invalid.");
216
+ }
217
+ const candidate = candidateBinding(root, request);
218
+ const experienceRef = String(request.experience_ref || "");
219
+ const experienceRoot = resolveContainedPath(root, experienceRef, "UI Acceptance experience");
220
+ const target = buildPrototypeReviewTarget(root, {
221
+ experience: experienceRef,
222
+ revision: request.prototype_revision_ref,
223
+ now: options.now,
224
+ });
225
+ if (target.mode !== "prototype-revision" || target.iterationId !== request.iteration_id) {
226
+ throw new Error("UI Acceptance requires the exact frozen Prototype revision.");
227
+ }
228
+ const revision = readYamlMapping(target.revisionFile, "Prototype revision");
229
+ if (candidate.candidate.prototype_revision_ref
230
+ !== `${revision.artifact_id}@${revision.version}`
231
+ || candidate.candidate.prototype_revision_checksum !== target.targetChecksum
232
+ || candidate.candidate.prototype_source_revision !== revision.source.revision
233
+ || candidate.candidate.prototype_source_checksum !== revision.source.checksum
234
+ || candidate.candidate.prototype_review_bundle_checksum !== revision.review_bundle.checksum) {
235
+ throw new Error("Candidate Prototype revision binding is missing or inconsistent.");
236
+ }
237
+ const currentVisualStandardFile = path.join(experienceRoot, "visual-system-confirmation.yaml");
238
+ if (sha256File(currentVisualStandardFile) !== requireSha256(
239
+ revision.visual_standard?.checksum,
240
+ "Frozen Prototype Visual Standard",
241
+ )) {
242
+ throw new Error("Current Visual Standard does not match the frozen Prototype revision.");
243
+ }
244
+ registeredArtifact(
245
+ root,
246
+ candidate.registry,
247
+ revision.artifact_id,
248
+ "prototype-revision",
249
+ target.targetChecksum,
250
+ "frozen",
251
+ );
252
+ const traceability = readTraceability(candidate, target, revision);
253
+ for (const page of target.pages.filter((item) => traceability.pageIds.includes(item.page_id))) {
254
+ if (page.review_status !== "approved") {
255
+ throw new Error(`UI Acceptance requires a current approved Page receipt: ${page.page_id}.`);
256
+ }
257
+ }
258
+
259
+ const packValidation = validateFrozenMockScenarioPack(root, {
260
+ experience: experienceRef,
261
+ scenarioPack: request.mock_scenario_pack_ref,
262
+ }, {
263
+ validateMockDefinition,
264
+ protocolExpectation: mockProtocolExpectation,
265
+ });
266
+ const { pack } = packValidation;
267
+ const packChecksum = packValidation.checksum;
268
+ if (pack.artifact_type !== "mock-scenario-pack" || pack.status !== "frozen"
269
+ || pack.iteration_id !== request.iteration_id
270
+ || pack.prototype_revision?.artifact_id !== target.targetId
271
+ || pack.prototype_revision?.checksum !== target.targetChecksum
272
+ || pack.prototype_revision?.source_checksum !== revision.source.checksum
273
+ || pack.prototype_revision?.review_bundle_checksum !== revision.review_bundle.checksum
274
+ || candidate.candidate.mock_scenario_pack_ref !== `${pack.artifact_id}@${pack.version}`
275
+ || candidate.candidate.mock_scenario_pack_checksum !== packChecksum) {
276
+ throw new Error("Candidate Mock Scenario Pack binding is missing, stale, or inconsistent.");
277
+ }
278
+ registeredArtifact(
279
+ root,
280
+ candidate.registry,
281
+ pack.artifact_id,
282
+ "mock-scenario-pack",
283
+ packChecksum,
284
+ "frozen",
285
+ );
286
+ const scenarioByScene = new Map();
287
+ for (const mapping of Array.isArray(pack.scenario_mappings) ? pack.scenario_mappings : []) {
288
+ if (!mapping.review_scene_id || !mapping.scenario_id
289
+ || !new Set(["external-dependency", "product-backend"]).has(mapping.mock_kind)
290
+ || scenarioByScene.has(mapping.review_scene_id)) {
291
+ throw new Error("Mock Scenario Pack contains an invalid or duplicate Scene mapping.");
292
+ }
293
+ scenarioByScene.set(mapping.review_scene_id, mapping);
294
+ }
295
+ if (traceability.scenes.some((scene) => !scenarioByScene.has(scene.review_scene_id))) {
296
+ throw new Error("Mock Scenario Pack does not cover every affected Review Scene.");
297
+ }
298
+ const strategyBinding = boundFile(root, request.test_strategy, "Approved test strategy");
299
+ const strategy = readYamlMapping(strategyBinding.file, "Approved test strategy");
300
+ if (strategy.decision !== "approved") throw new Error("Candidate test strategy is not approved.");
301
+ const mockKinds = new Set(traceability.scenes.map((scene) =>
302
+ scenarioByScene.get(scene.review_scene_id).mock_kind));
303
+ if (mockKinds.has("external-dependency")
304
+ && strategy.external_dependency_mock_allowed_in_candidate_assurance !== true) {
305
+ throw new Error("External Dependency Mock is not allowed by the approved test strategy.");
306
+ }
307
+ const policy = acceptancePolicy(experienceRoot);
308
+ const runner = pinnedRunner(request.runner);
309
+ const mappingByScene = new Map(traceability.mappings
310
+ .filter((item) => item.target_kind === "review-scene")
311
+ .map((item) => [item.review_scene_id, item]));
312
+ const executions = [];
313
+ for (const scene of traceability.scenes) {
314
+ const scenario = scenarioByScene.get(scene.review_scene_id);
315
+ const mapping = mappingByScene.get(scene.review_scene_id);
316
+ for (const viewport of policy.viewports) {
317
+ executions.push({
318
+ execution_id: `${scene.page_id}/${scene.review_scene_id}/${viewport.viewport_id}`,
319
+ terminal_id: scene.terminal_id,
320
+ page_id: scene.page_id,
321
+ review_scene_id: scene.review_scene_id,
322
+ viewport_id: viewport.viewport_id,
323
+ prototype_route: scene.prototype_route,
324
+ production_route: scene.production_route,
325
+ trigger_condition_id: scene.trigger_condition_id,
326
+ fixture_ref: scene.trigger?.fixture_ref || null,
327
+ ui_mapping_id: mapping.ui_mapping_id,
328
+ acceptance_id: mapping.acceptance_id,
329
+ mock_id: scenario.mock_id,
330
+ mock_kind: scenario.mock_kind,
331
+ scenario_id: scenario.scenario_id,
332
+ protocol: scenario.protocol,
333
+ operation_or_channel: scenario.operation_or_channel,
334
+ protocol_expectation: packValidation.expectations.get(scene.review_scene_id),
335
+ });
336
+ }
337
+ }
338
+ const plan = {
339
+ valid: true,
340
+ action: "plan",
341
+ iteration_id: request.iteration_id,
342
+ candidate: {
343
+ candidate_id: request.candidate_id,
344
+ manifest_checksum: candidate.candidateChecksum,
345
+ node_attempt: candidate.attempt,
346
+ },
347
+ prototype_revision: {
348
+ artifact_id: target.targetId,
349
+ checksum: target.targetChecksum,
350
+ source_revision: revision.source.revision,
351
+ review_bundle_checksum: revision.review_bundle.checksum,
352
+ ui_mapping_checksum: revision.catalogs.ui_mapping.checksum,
353
+ },
354
+ visual_standard: {
355
+ ref: revision.visual_standard.ref,
356
+ checksum: revision.visual_standard.checksum,
357
+ },
358
+ mock_scenario_pack: {
359
+ artifact_id: pack.artifact_id,
360
+ checksum: packChecksum,
361
+ },
362
+ test_strategy: {
363
+ ref: request.test_strategy.ref,
364
+ checksum: strategyBinding.checksum,
365
+ },
366
+ runner,
367
+ policy,
368
+ affected_page_ids: traceability.pageIds,
369
+ affected_review_scene_ids: traceability.scenes.map((scene) => scene.review_scene_id),
370
+ real_product_backend_evidence_required: mockKinds.has("product-backend"),
371
+ executions,
372
+ };
373
+ return { ...plan, plan_checksum: stableValueChecksum(plan) };
374
+ }
375
+
376
+ function requirePassingCheck(projectRoot, check, label) {
377
+ if (check?.status !== "passed") throw new Error(`${label} did not pass.`);
378
+ boundFile(projectRoot, check.evidence, `${label} evidence`);
379
+ }
380
+
381
+ function readJsonEvidence(file, label) {
382
+ try {
383
+ const value = JSON.parse(fs.readFileSync(file, "utf8"));
384
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
385
+ throw new Error("expected an object");
386
+ }
387
+ return value;
388
+ } catch (error) {
389
+ throw new Error(`${label} is not valid JSON evidence: ${error.message}`);
390
+ }
391
+ }
392
+
393
+ function validateRealProductBackendEvidence(projectRoot, real, plan) {
394
+ const environment = real?.environment;
395
+ if (real?.status !== "passed" || real?.source !== "real-product-backend"
396
+ || !sameStableValue(real.candidate, plan.candidate)
397
+ || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(String(environment?.environment_id || ""))
398
+ || !String(environment?.deployment_ref || "").trim()
399
+ || !/^sha256:[a-f0-9]{64}$/.test(String(environment?.deployment_digest || ""))
400
+ || environment?.product_backend_mock_enabled !== false) {
401
+ throw new Error(
402
+ "Product Backend Mock cannot replace real product backend acceptance evidence: candidate or environment binding is invalid.",
403
+ );
404
+ }
405
+ const binding = boundFile(projectRoot, real.evidence, "Real product backend acceptance evidence");
406
+ const evidence = readJsonEvidence(binding.file, "Real product backend acceptance evidence");
407
+ const expected = {
408
+ schema_version: 1,
409
+ artifact_type: "real-product-backend-acceptance-evidence",
410
+ status: "passed",
411
+ source: "real-product-backend",
412
+ candidate: plan.candidate,
413
+ environment,
414
+ checks: {
415
+ contract: "passed",
416
+ end_to_end: "passed",
417
+ },
418
+ };
419
+ if (!sameStableValue(evidence, expected)) {
420
+ throw new Error("Real product backend evidence does not bind the candidate and non-Mock environment.");
421
+ }
422
+ }
423
+
424
+ export function verifyUiAcceptance(projectRoot, options = {}) {
425
+ const root = path.resolve(projectRoot);
426
+ const plan = planUiAcceptance(root, options);
427
+ const resultsFile = resolveContainedPath(root, options.results, "UI Acceptance results");
428
+ const results = readYamlMapping(resultsFile, "UI Acceptance results");
429
+ if (results.schema_version !== 1 || results.artifact_type !== "ui-acceptance-results"
430
+ || results.iteration_id !== plan.iteration_id
431
+ || results.status !== "passed" || results.decision !== "pass") {
432
+ throw new Error("UI Acceptance results identity or passing decision is invalid.");
433
+ }
434
+ if (results.plan_checksum !== plan.plan_checksum
435
+ || !sameStableValue(results.candidate, plan.candidate)
436
+ || !sameStableValue(results.prototype_revision, plan.prototype_revision)
437
+ || !sameStableValue(results.mock_scenario_pack, plan.mock_scenario_pack)
438
+ || !sameStableValue(results.runner, plan.runner)) {
439
+ throw new Error("UI Acceptance results do not bind the current immutable plan.");
440
+ }
441
+ if (results.candidate_assurance?.node !== candidateAssuranceNode
442
+ || Number(results.candidate_assurance?.node_attempt) !== plan.candidate.node_attempt
443
+ || results.candidate_assurance?.candidate_id !== plan.candidate.candidate_id) {
444
+ throw new Error("UI Acceptance results do not bind the active Candidate Assurance attempt.");
445
+ }
446
+ const expectedById = new Map(plan.executions.map((item) => [item.execution_id, item]));
447
+ const executions = Array.isArray(results.executions) ? results.executions : [];
448
+ if (executions.length !== expectedById.size
449
+ || new Set(executions.map((item) => item?.execution_id)).size !== executions.length
450
+ || executions.some((item) => !expectedById.has(item?.execution_id))) {
451
+ throw new Error("UI Acceptance execution coverage is incomplete or contains unexpected entries.");
452
+ }
453
+ const viewportById = new Map(plan.policy.viewports.map((item) => [item.viewport_id, item]));
454
+ for (const execution of executions) {
455
+ const expected = expectedById.get(execution.execution_id);
456
+ if (execution.status !== "passed" || execution.scenario_id !== expected.scenario_id
457
+ || execution.deterministic_rendering?.status !== "passed"
458
+ || execution.deterministic_rendering?.scenario_id !== expected.scenario_id
459
+ || (execution.deterministic_rendering?.fixture_ref || null) !== expected.fixture_ref) {
460
+ throw new Error(`Deterministic rendering did not pass: ${execution.execution_id}.`);
461
+ }
462
+ const visual = execution.visual;
463
+ const viewport = viewportById.get(expected.viewport_id);
464
+ const expectedPixels = viewport.width * viewport.height;
465
+ const changedPixels = Number(visual?.changed_pixels);
466
+ const totalPixels = Number(visual?.total_pixels);
467
+ const ratio = totalPixels > 0 ? changedPixels / totalPixels : Number.NaN;
468
+ if (visual?.status !== "passed" || !Number.isInteger(changedPixels) || changedPixels < 0
469
+ || !Number.isInteger(totalPixels) || totalPixels !== expectedPixels
470
+ || changedPixels > totalPixels
471
+ || Math.abs(Number(visual.changed_pixel_ratio) - ratio) > Number.EPSILON
472
+ || visual.max_diff_pixel_ratio !== plan.policy.max_diff_pixel_ratio
473
+ || ratio > plan.policy.max_diff_pixel_ratio) {
474
+ throw new Error(`Visual diff exceeds or misreports the approved policy: ${execution.execution_id}.`);
475
+ }
476
+ const prototypeEvidence = boundFile(
477
+ root,
478
+ visual.prototype,
479
+ `Prototype screenshot ${execution.execution_id}`,
480
+ );
481
+ const candidateEvidence = boundFile(
482
+ root,
483
+ visual.candidate,
484
+ `Candidate screenshot ${execution.execution_id}`,
485
+ );
486
+ const diffEvidence = boundFile(root, visual.diff, `Visual diff ${execution.execution_id}`);
487
+ const measured = verifyVisualPngEvidence({
488
+ prototype: prototypeEvidence.file,
489
+ candidate: candidateEvidence.file,
490
+ diff: diffEvidence.file,
491
+ }, viewport, `UI Acceptance ${execution.execution_id}`);
492
+ if (measured.changedPixels !== changedPixels || measured.totalPixels !== totalPixels) {
493
+ throw new Error(
494
+ `Visual diff pixel counts do not match the PNG evidence: ${execution.execution_id}.`,
495
+ );
496
+ }
497
+ for (const name of [
498
+ "interaction", "keyboard", "focus", "touch_target", "text_overflow", "responsive", "recovery",
499
+ ]) {
500
+ requirePassingCheck(root, execution[name], `${name} check ${execution.execution_id}`);
501
+ }
502
+ requirePassingCheck(root, execution.accessibility, `accessibility check ${execution.execution_id}`);
503
+ if (execution.accessibility.standard !== "WCAG 2.2 AA"
504
+ || !Array.isArray(execution.accessibility.violations)
505
+ || execution.accessibility.violations.length) {
506
+ throw new Error(`WCAG 2.2 AA acceptance failed: ${execution.execution_id}.`);
507
+ }
508
+ requirePassingCheck(root, execution.protocol, `protocol check ${execution.execution_id}`);
509
+ if (execution.protocol.scenario_id !== expected.scenario_id
510
+ || execution.protocol.operation_or_channel !== expected.operation_or_channel) {
511
+ throw new Error(`Protocol evidence does not match the frozen Scenario: ${execution.execution_id}.`);
512
+ }
513
+ const protocolRequest = boundFile(
514
+ root,
515
+ execution.protocol.request,
516
+ `Protocol request ${execution.execution_id}`,
517
+ );
518
+ const protocolResponse = boundFile(
519
+ root,
520
+ execution.protocol.response,
521
+ `Protocol response ${execution.execution_id}`,
522
+ );
523
+ const actualRequest = readJsonEvidence(
524
+ protocolRequest.file,
525
+ `Protocol request ${execution.execution_id}`,
526
+ );
527
+ const actualResponse = readJsonEvidence(
528
+ protocolResponse.file,
529
+ `Protocol response ${execution.execution_id}`,
530
+ );
531
+ if (!sameStableValue(actualRequest, expected.protocol_expectation?.request)
532
+ || !sameStableValue(actualResponse, expected.protocol_expectation?.response)) {
533
+ throw new Error(
534
+ `Protocol response or request contradicts the frozen Scenario: ${execution.execution_id}.`,
535
+ );
536
+ }
537
+ }
538
+ const expectedProductBackendMock = plan.real_product_backend_evidence_required;
539
+ if (results.mock_boundary?.product_backend_mock_used_for_rendering !== expectedProductBackendMock
540
+ || results.mock_boundary?.product_backend_mock_accepted_as_real_backend_evidence !== false) {
541
+ throw new Error("Product Backend Mock authority is misrepresented in UI Acceptance results.");
542
+ }
543
+ if (expectedProductBackendMock) {
544
+ const real = results.real_product_backend_acceptance;
545
+ validateRealProductBackendEvidence(root, real, plan);
546
+ }
547
+ return {
548
+ valid: true,
549
+ action: "verify",
550
+ decision: "pass",
551
+ candidate: plan.candidate,
552
+ plan_checksum: plan.plan_checksum,
553
+ results_checksum: sha256File(resultsFile),
554
+ execution_count: executions.length,
555
+ affected_page_ids: plan.affected_page_ids,
556
+ affected_review_scene_ids: plan.affected_review_scene_ids,
557
+ real_product_backend_evidence_verified: expectedProductBackendMock,
558
+ };
559
+ }
560
+
561
+ export function runUiAcceptanceCommand(projectRoot, action, options = {}) {
562
+ if (action === "plan") return planUiAcceptance(projectRoot, options);
563
+ if (action === "verify") return verifyUiAcceptance(projectRoot, options);
564
+ throw new Error(`Unknown ui-acceptance action: ${action}`);
565
+ }