create-yss-spec 2.2.10 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1721) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/cli.js +0 -1
  4. package/template/.agents/skills/.strategic-design-skills-manifest.json +35 -0
  5. package/template/.agents/skills/.yss-skills-manifest.json +0 -2
  6. package/template/.agents/skills/ask-matt/SKILL.md +1 -1
  7. package/template/.agents/skills/code-review/SKILL.md +1 -1
  8. package/template/.agents/skills/code-review/references/yss-review-standards.md +1 -1
  9. package/template/.agents/skills/lombok/SKILL.md +1 -1
  10. package/template/.agents/skills/mapstruct/SKILL.md +1 -1
  11. package/template/.agents/skills/prototype-review/SKILL.md +7 -6
  12. package/template/.agents/skills/wayfinder/SKILL.md +2 -2
  13. package/template/.agents/skills/yss-antd-design/SKILL.md +8 -7
  14. package/template/.agents/skills/yss-antd-design/references/evidence.md +11 -6
  15. package/template/.agents/skills/yss-api-integration/SKILL.md +2 -2
  16. package/template/.agents/skills/yss-application/SKILL.md +2 -2
  17. package/template/.agents/skills/yss-audit-log/SKILL.md +2 -2
  18. package/template/.agents/skills/yss-audit-log/references/source-index.md +1 -1
  19. package/template/.agents/skills/yss-cache/SKILL.md +2 -2
  20. package/template/.agents/skills/yss-cache/references/source-index.md +1 -1
  21. package/template/.agents/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  22. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  23. package/template/.agents/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  24. package/template/.agents/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  25. package/template/.agents/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  26. package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  27. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  28. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  29. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  30. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  31. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  32. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  33. package/template/.agents/skills/yss-design-system/SKILL.md +6 -6
  34. package/template/.agents/skills/yss-distributed-id/SKILL.md +2 -2
  35. package/template/.agents/skills/yss-distributed-id/references/source-index.md +1 -1
  36. package/template/.agents/skills/yss-domain/SKILL.md +3 -2
  37. package/template/.agents/skills/yss-domain/agents/openai.yaml +2 -2
  38. package/template/.agents/skills/yss-domain/references/domain-layer-guide.md +2 -0
  39. package/template/.agents/skills/yss-dto/SKILL.md +8 -2
  40. package/template/.agents/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  41. package/template/.agents/skills/yss-dto/references/source-index.md +1 -1
  42. package/template/.agents/skills/yss-excel-mvc/SKILL.md +2 -2
  43. package/template/.agents/skills/yss-excel-mvc/references/source-index.md +1 -1
  44. package/template/.agents/skills/yss-exception/SKILL.md +1 -1
  45. package/template/.agents/skills/yss-exception/references/source-index.md +1 -1
  46. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  47. package/template/.agents/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  48. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  49. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  50. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  51. package/template/.agents/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  52. package/template/.agents/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  53. package/template/.agents/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  54. package/template/.agents/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  55. package/template/.agents/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  56. package/template/.agents/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  57. package/template/.agents/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  58. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  59. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  60. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  61. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  62. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  63. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  64. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  65. package/template/.agents/skills/yss-mybatis/SKILL.md +2 -2
  66. package/template/.agents/skills/yss-mybatis/references/source-index.md +1 -1
  67. package/template/.agents/skills/yss-page-module-development/SKILL.md +1 -1
  68. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +7 -7
  69. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  70. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  71. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  72. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +1 -1
  73. package/template/.agents/skills/yss-prototype-stage/SKILL.md +34 -24
  74. package/template/.agents/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  75. package/template/.agents/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  76. package/template/.agents/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  77. package/template/.agents/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  78. package/template/.agents/skills/yss-repository/SKILL.md +16 -14
  79. package/template/.agents/skills/yss-repository/agents/openai.yaml +2 -2
  80. package/template/.agents/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  81. package/template/.agents/skills/yss-research/SKILL.md +73 -0
  82. package/template/.agents/skills/yss-research/agents/openai.yaml +3 -0
  83. package/template/.agents/skills/yss-research/assets/evidence-template.yaml +68 -0
  84. package/template/.agents/skills/yss-research/assets/research-brief-template.md +36 -0
  85. package/template/.agents/skills/yss-research/references/evidence-contract.md +42 -0
  86. package/template/.agents/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  87. package/template/.agents/skills/yss-research/tests/run-scenarios.mjs +81 -0
  88. package/template/.agents/skills/yss-resilience4j/SKILL.md +1 -1
  89. package/template/.agents/skills/yss-resilience4j/references/source-index.md +1 -1
  90. package/template/.agents/skills/yss-security-algorithm/SKILL.md +1 -1
  91. package/template/.agents/skills/yss-security-algorithm/references/source-index.md +1 -1
  92. package/template/.agents/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  93. package/template/.agents/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  94. package/template/.agents/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  95. package/template/.agents/skills/yss-stage-decision/SKILL.md +2 -1
  96. package/template/.agents/skills/yss-ui/SKILL.md +2 -1
  97. package/template/.agents/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  98. package/template/.agents/skills/yss-up-springboot3/SKILL.md +1 -1
  99. package/template/.agents/skills/yss-userinfo/SKILL.md +1 -1
  100. package/template/.agents/skills/yss-userinfo/references/source-index.md +1 -1
  101. package/template/.agents/skills/yss-validation/SKILL.md +1 -1
  102. package/template/.agents/skills/yss-validation/references/source-index.md +1 -1
  103. package/template/.agents/skills/yss-web-controller/SKILL.md +17 -11
  104. package/template/.agents/skills/yss-web-controller/agents/openai.yaml +2 -2
  105. package/template/.agents/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  106. package/template/.agents/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  107. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  108. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  109. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  110. package/template/.agents/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  111. package/template/.agents/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  112. package/template/.agents/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  113. package/template/.agents/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  114. package/template/.claude/skills/ask-matt/SKILL.md +1 -1
  115. package/template/.claude/skills/code-review/SKILL.md +1 -1
  116. package/template/.claude/skills/code-review/references/yss-review-standards.md +1 -1
  117. package/template/.claude/skills/lombok/SKILL.md +1 -1
  118. package/template/.claude/skills/mapstruct/SKILL.md +1 -1
  119. package/template/.claude/skills/prototype-review/SKILL.md +7 -6
  120. package/template/.claude/skills/wayfinder/SKILL.md +2 -2
  121. package/template/.claude/skills/yss-antd-design/SKILL.md +8 -7
  122. package/template/.claude/skills/yss-antd-design/references/evidence.md +11 -6
  123. package/template/.claude/skills/yss-api-integration/SKILL.md +2 -2
  124. package/template/.claude/skills/yss-application/SKILL.md +2 -2
  125. package/template/.claude/skills/yss-audit-log/SKILL.md +2 -2
  126. package/template/.claude/skills/yss-audit-log/references/source-index.md +1 -1
  127. package/template/.claude/skills/yss-cache/SKILL.md +2 -2
  128. package/template/.claude/skills/yss-cache/references/source-index.md +1 -1
  129. package/template/.claude/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  130. package/template/.claude/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  131. package/template/.claude/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  132. package/template/.claude/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  133. package/template/.claude/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  134. package/template/.claude/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  135. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  136. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  137. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  138. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  139. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  140. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  141. package/template/.claude/skills/yss-design-system/SKILL.md +6 -6
  142. package/template/.claude/skills/yss-distributed-id/SKILL.md +2 -2
  143. package/template/.claude/skills/yss-distributed-id/references/source-index.md +1 -1
  144. package/template/.claude/skills/yss-domain/SKILL.md +3 -2
  145. package/template/.claude/skills/yss-domain/agents/openai.yaml +2 -2
  146. package/template/.claude/skills/yss-domain/references/domain-layer-guide.md +2 -0
  147. package/template/.claude/skills/yss-dto/SKILL.md +8 -2
  148. package/template/.claude/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  149. package/template/.claude/skills/yss-dto/references/source-index.md +1 -1
  150. package/template/.claude/skills/yss-excel-mvc/SKILL.md +2 -2
  151. package/template/.claude/skills/yss-excel-mvc/references/source-index.md +1 -1
  152. package/template/.claude/skills/yss-exception/SKILL.md +1 -1
  153. package/template/.claude/skills/yss-exception/references/source-index.md +1 -1
  154. package/template/.claude/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  155. package/template/.claude/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  156. package/template/.claude/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  157. package/template/.claude/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  158. package/template/.claude/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  159. package/template/.claude/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  160. package/template/.claude/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  161. package/template/.claude/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  162. package/template/.claude/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  163. package/template/.claude/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  164. package/template/.claude/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  165. package/template/.claude/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  166. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  167. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  168. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  169. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  170. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  171. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  172. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  173. package/template/.claude/skills/yss-mybatis/SKILL.md +2 -2
  174. package/template/.claude/skills/yss-mybatis/references/source-index.md +1 -1
  175. package/template/.claude/skills/yss-page-module-development/SKILL.md +1 -1
  176. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +7 -7
  177. package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  178. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  179. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  180. package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +1 -1
  181. package/template/.claude/skills/yss-prototype-stage/SKILL.md +34 -24
  182. package/template/.claude/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  183. package/template/.claude/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  184. package/template/.claude/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  185. package/template/.claude/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  186. package/template/.claude/skills/yss-repository/SKILL.md +16 -14
  187. package/template/.claude/skills/yss-repository/agents/openai.yaml +2 -2
  188. package/template/.claude/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  189. package/template/.claude/skills/yss-research/SKILL.md +73 -0
  190. package/template/.claude/skills/yss-research/agents/openai.yaml +3 -0
  191. package/template/.claude/skills/yss-research/assets/evidence-template.yaml +68 -0
  192. package/template/.claude/skills/yss-research/assets/research-brief-template.md +36 -0
  193. package/template/.claude/skills/yss-research/references/evidence-contract.md +42 -0
  194. package/template/.claude/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  195. package/template/.claude/skills/yss-research/tests/run-scenarios.mjs +81 -0
  196. package/template/.claude/skills/yss-resilience4j/SKILL.md +1 -1
  197. package/template/.claude/skills/yss-resilience4j/references/source-index.md +1 -1
  198. package/template/.claude/skills/yss-security-algorithm/SKILL.md +1 -1
  199. package/template/.claude/skills/yss-security-algorithm/references/source-index.md +1 -1
  200. package/template/.claude/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  201. package/template/.claude/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  202. package/template/.claude/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  203. package/template/.claude/skills/yss-stage-decision/SKILL.md +2 -1
  204. package/template/.claude/skills/yss-ui/SKILL.md +2 -1
  205. package/template/.claude/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  206. package/template/.claude/skills/yss-up-springboot3/SKILL.md +1 -1
  207. package/template/.claude/skills/yss-userinfo/SKILL.md +1 -1
  208. package/template/.claude/skills/yss-userinfo/references/source-index.md +1 -1
  209. package/template/.claude/skills/yss-validation/SKILL.md +1 -1
  210. package/template/.claude/skills/yss-validation/references/source-index.md +1 -1
  211. package/template/.claude/skills/yss-web-controller/SKILL.md +17 -11
  212. package/template/.claude/skills/yss-web-controller/agents/openai.yaml +2 -2
  213. package/template/.claude/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  214. package/template/.claude/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  215. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  216. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  217. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  218. package/template/.claude/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  219. package/template/.claude/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  220. package/template/.claude/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  221. package/template/.claude/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  222. package/template/.codex/skills/ask-matt/SKILL.md +1 -1
  223. package/template/.codex/skills/code-review/SKILL.md +1 -1
  224. package/template/.codex/skills/code-review/references/yss-review-standards.md +1 -1
  225. package/template/.codex/skills/lombok/SKILL.md +1 -1
  226. package/template/.codex/skills/mapstruct/SKILL.md +1 -1
  227. package/template/.codex/skills/product-design/package.json +4 -2
  228. package/template/.codex/skills/product-design/skills/research/SKILL.md +22 -0
  229. package/template/.codex/skills/product-design/skills/research/assets/evidence-template.yaml +65 -0
  230. package/template/.codex/skills/product-design/skills/research/assets/research-brief-template.md +33 -0
  231. package/template/.codex/skills/product-design/skills/research/references/evidence-audited.md +80 -0
  232. package/template/.codex/skills/product-design/skills/research/scripts/validate-research-package.mjs +149 -0
  233. package/template/.codex/skills/product-design/skills/research/tests/run-scenarios.mjs +72 -0
  234. package/template/.codex/skills/prototype-review/SKILL.md +7 -6
  235. package/template/.codex/skills/wayfinder/SKILL.md +2 -2
  236. package/template/.codex/skills/yss-antd-design/SKILL.md +8 -7
  237. package/template/.codex/skills/yss-antd-design/references/evidence.md +11 -6
  238. package/template/.codex/skills/yss-api-integration/SKILL.md +2 -2
  239. package/template/.codex/skills/yss-application/SKILL.md +2 -2
  240. package/template/.codex/skills/yss-audit-log/SKILL.md +2 -2
  241. package/template/.codex/skills/yss-audit-log/references/source-index.md +1 -1
  242. package/template/.codex/skills/yss-cache/SKILL.md +2 -2
  243. package/template/.codex/skills/yss-cache/references/source-index.md +1 -1
  244. package/template/.codex/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  245. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  246. package/template/.codex/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  247. package/template/.codex/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  248. package/template/.codex/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  249. package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  250. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  251. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  252. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  253. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  254. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  255. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  256. package/template/.codex/skills/yss-design-system/SKILL.md +6 -6
  257. package/template/.codex/skills/yss-distributed-id/SKILL.md +2 -2
  258. package/template/.codex/skills/yss-distributed-id/references/source-index.md +1 -1
  259. package/template/.codex/skills/yss-domain/SKILL.md +3 -2
  260. package/template/.codex/skills/yss-domain/agents/openai.yaml +2 -2
  261. package/template/.codex/skills/yss-domain/references/domain-layer-guide.md +2 -0
  262. package/template/.codex/skills/yss-dto/SKILL.md +8 -2
  263. package/template/.codex/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  264. package/template/.codex/skills/yss-dto/references/source-index.md +1 -1
  265. package/template/.codex/skills/yss-excel-mvc/SKILL.md +2 -2
  266. package/template/.codex/skills/yss-excel-mvc/references/source-index.md +1 -1
  267. package/template/.codex/skills/yss-exception/SKILL.md +1 -1
  268. package/template/.codex/skills/yss-exception/references/source-index.md +1 -1
  269. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  270. package/template/.codex/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  271. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  272. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  273. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  274. package/template/.codex/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  275. package/template/.codex/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  276. package/template/.codex/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  277. package/template/.codex/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  278. package/template/.codex/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  279. package/template/.codex/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  280. package/template/.codex/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  281. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  282. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  283. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  284. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  285. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  286. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  287. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  288. package/template/.codex/skills/yss-mybatis/SKILL.md +2 -2
  289. package/template/.codex/skills/yss-mybatis/references/source-index.md +1 -1
  290. package/template/.codex/skills/yss-page-module-development/SKILL.md +1 -1
  291. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +7 -7
  292. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  293. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  294. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  295. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +1 -1
  296. package/template/.codex/skills/yss-prototype-stage/SKILL.md +34 -24
  297. package/template/.codex/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  298. package/template/.codex/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  299. package/template/.codex/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  300. package/template/.codex/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  301. package/template/.codex/skills/yss-repository/SKILL.md +16 -14
  302. package/template/.codex/skills/yss-repository/agents/openai.yaml +2 -2
  303. package/template/.codex/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  304. package/template/.codex/skills/yss-research/SKILL.md +73 -0
  305. package/template/.codex/skills/yss-research/agents/openai.yaml +3 -0
  306. package/template/.codex/skills/yss-research/assets/evidence-template.yaml +68 -0
  307. package/template/.codex/skills/yss-research/assets/research-brief-template.md +36 -0
  308. package/template/.codex/skills/yss-research/references/evidence-contract.md +42 -0
  309. package/template/.codex/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  310. package/template/.codex/skills/yss-research/tests/run-scenarios.mjs +81 -0
  311. package/template/.codex/skills/yss-resilience4j/SKILL.md +1 -1
  312. package/template/.codex/skills/yss-resilience4j/references/source-index.md +1 -1
  313. package/template/.codex/skills/yss-security-algorithm/SKILL.md +1 -1
  314. package/template/.codex/skills/yss-security-algorithm/references/source-index.md +1 -1
  315. package/template/.codex/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  316. package/template/.codex/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  317. package/template/.codex/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  318. package/template/.codex/skills/yss-stage-decision/SKILL.md +2 -1
  319. package/template/.codex/skills/yss-ui/SKILL.md +2 -1
  320. package/template/.codex/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  321. package/template/.codex/skills/yss-up-springboot3/SKILL.md +1 -1
  322. package/template/.codex/skills/yss-userinfo/SKILL.md +1 -1
  323. package/template/.codex/skills/yss-userinfo/references/source-index.md +1 -1
  324. package/template/.codex/skills/yss-validation/SKILL.md +1 -1
  325. package/template/.codex/skills/yss-validation/references/source-index.md +1 -1
  326. package/template/.codex/skills/yss-web-controller/SKILL.md +17 -11
  327. package/template/.codex/skills/yss-web-controller/agents/openai.yaml +2 -2
  328. package/template/.codex/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  329. package/template/.codex/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  330. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  331. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  332. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  333. package/template/.codex/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  334. package/template/.codex/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  335. package/template/.codex/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  336. package/template/.codex/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  337. package/template/.cursor/skills/ask-matt/SKILL.md +1 -1
  338. package/template/.cursor/skills/code-review/SKILL.md +1 -1
  339. package/template/.cursor/skills/code-review/references/yss-review-standards.md +1 -1
  340. package/template/.cursor/skills/lombok/SKILL.md +1 -1
  341. package/template/.cursor/skills/mapstruct/SKILL.md +1 -1
  342. package/template/.cursor/skills/prototype-review/SKILL.md +7 -6
  343. package/template/.cursor/skills/wayfinder/SKILL.md +2 -2
  344. package/template/.cursor/skills/yss-antd-design/SKILL.md +8 -7
  345. package/template/.cursor/skills/yss-antd-design/references/evidence.md +11 -6
  346. package/template/.cursor/skills/yss-api-integration/SKILL.md +2 -2
  347. package/template/.cursor/skills/yss-application/SKILL.md +2 -2
  348. package/template/.cursor/skills/yss-audit-log/SKILL.md +2 -2
  349. package/template/.cursor/skills/yss-audit-log/references/source-index.md +1 -1
  350. package/template/.cursor/skills/yss-cache/SKILL.md +2 -2
  351. package/template/.cursor/skills/yss-cache/references/source-index.md +1 -1
  352. package/template/.cursor/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  353. package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  354. package/template/.cursor/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  355. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  356. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  357. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  358. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  359. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  360. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  361. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  362. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  363. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  364. package/template/.cursor/skills/yss-design-system/SKILL.md +6 -6
  365. package/template/.cursor/skills/yss-distributed-id/SKILL.md +2 -2
  366. package/template/.cursor/skills/yss-distributed-id/references/source-index.md +1 -1
  367. package/template/.cursor/skills/yss-domain/SKILL.md +3 -2
  368. package/template/.cursor/skills/yss-domain/agents/openai.yaml +2 -2
  369. package/template/.cursor/skills/yss-domain/references/domain-layer-guide.md +2 -0
  370. package/template/.cursor/skills/yss-dto/SKILL.md +8 -2
  371. package/template/.cursor/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  372. package/template/.cursor/skills/yss-dto/references/source-index.md +1 -1
  373. package/template/.cursor/skills/yss-excel-mvc/SKILL.md +2 -2
  374. package/template/.cursor/skills/yss-excel-mvc/references/source-index.md +1 -1
  375. package/template/.cursor/skills/yss-exception/SKILL.md +1 -1
  376. package/template/.cursor/skills/yss-exception/references/source-index.md +1 -1
  377. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  378. package/template/.cursor/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  379. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  380. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  381. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  382. package/template/.cursor/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  383. package/template/.cursor/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  384. package/template/.cursor/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  385. package/template/.cursor/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  386. package/template/.cursor/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  387. package/template/.cursor/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  388. package/template/.cursor/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  389. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  390. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  391. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  392. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  393. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  394. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  395. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  396. package/template/.cursor/skills/yss-mybatis/SKILL.md +2 -2
  397. package/template/.cursor/skills/yss-mybatis/references/source-index.md +1 -1
  398. package/template/.cursor/skills/yss-page-module-development/SKILL.md +1 -1
  399. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +7 -7
  400. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  401. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  402. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  403. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +1 -1
  404. package/template/.cursor/skills/yss-prototype-stage/SKILL.md +34 -24
  405. package/template/.cursor/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  406. package/template/.cursor/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  407. package/template/.cursor/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  408. package/template/.cursor/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  409. package/template/.cursor/skills/yss-repository/SKILL.md +16 -14
  410. package/template/.cursor/skills/yss-repository/agents/openai.yaml +2 -2
  411. package/template/.cursor/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  412. package/template/.cursor/skills/yss-research/SKILL.md +73 -0
  413. package/template/.cursor/skills/yss-research/agents/openai.yaml +3 -0
  414. package/template/.cursor/skills/yss-research/assets/evidence-template.yaml +68 -0
  415. package/template/.cursor/skills/yss-research/assets/research-brief-template.md +36 -0
  416. package/template/.cursor/skills/yss-research/references/evidence-contract.md +42 -0
  417. package/template/.cursor/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  418. package/template/.cursor/skills/yss-research/tests/run-scenarios.mjs +81 -0
  419. package/template/.cursor/skills/yss-resilience4j/SKILL.md +1 -1
  420. package/template/.cursor/skills/yss-resilience4j/references/source-index.md +1 -1
  421. package/template/.cursor/skills/yss-security-algorithm/SKILL.md +1 -1
  422. package/template/.cursor/skills/yss-security-algorithm/references/source-index.md +1 -1
  423. package/template/.cursor/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  424. package/template/.cursor/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  425. package/template/.cursor/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  426. package/template/.cursor/skills/yss-stage-decision/SKILL.md +2 -1
  427. package/template/.cursor/skills/yss-ui/SKILL.md +2 -1
  428. package/template/.cursor/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  429. package/template/.cursor/skills/yss-up-springboot3/SKILL.md +1 -1
  430. package/template/.cursor/skills/yss-userinfo/SKILL.md +1 -1
  431. package/template/.cursor/skills/yss-userinfo/references/source-index.md +1 -1
  432. package/template/.cursor/skills/yss-validation/SKILL.md +1 -1
  433. package/template/.cursor/skills/yss-validation/references/source-index.md +1 -1
  434. package/template/.cursor/skills/yss-web-controller/SKILL.md +17 -11
  435. package/template/.cursor/skills/yss-web-controller/agents/openai.yaml +2 -2
  436. package/template/.cursor/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  437. package/template/.cursor/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  438. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  439. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  440. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  441. package/template/.cursor/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  442. package/template/.cursor/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  443. package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  444. package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  445. package/template/.pi/skills/ask-matt/SKILL.md +1 -1
  446. package/template/.pi/skills/code-review/SKILL.md +1 -1
  447. package/template/.pi/skills/code-review/references/yss-review-standards.md +1 -1
  448. package/template/.pi/skills/lombok/SKILL.md +1 -1
  449. package/template/.pi/skills/mapstruct/SKILL.md +1 -1
  450. package/template/.pi/skills/prototype-review/SKILL.md +7 -6
  451. package/template/.pi/skills/wayfinder/SKILL.md +2 -2
  452. package/template/.pi/skills/yss-antd-design/SKILL.md +8 -7
  453. package/template/.pi/skills/yss-antd-design/references/evidence.md +11 -6
  454. package/template/.pi/skills/yss-api-integration/SKILL.md +2 -2
  455. package/template/.pi/skills/yss-application/SKILL.md +2 -2
  456. package/template/.pi/skills/yss-audit-log/SKILL.md +2 -2
  457. package/template/.pi/skills/yss-audit-log/references/source-index.md +1 -1
  458. package/template/.pi/skills/yss-cache/SKILL.md +2 -2
  459. package/template/.pi/skills/yss-cache/references/source-index.md +1 -1
  460. package/template/.pi/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  461. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  462. package/template/.pi/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  463. package/template/.pi/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  464. package/template/.pi/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  465. package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  466. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  467. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  468. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  469. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  470. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  471. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  472. package/template/.pi/skills/yss-design-system/SKILL.md +6 -6
  473. package/template/.pi/skills/yss-distributed-id/SKILL.md +2 -2
  474. package/template/.pi/skills/yss-distributed-id/references/source-index.md +1 -1
  475. package/template/.pi/skills/yss-domain/SKILL.md +3 -2
  476. package/template/.pi/skills/yss-domain/agents/openai.yaml +2 -2
  477. package/template/.pi/skills/yss-domain/references/domain-layer-guide.md +2 -0
  478. package/template/.pi/skills/yss-dto/SKILL.md +8 -2
  479. package/template/.pi/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  480. package/template/.pi/skills/yss-dto/references/source-index.md +1 -1
  481. package/template/.pi/skills/yss-excel-mvc/SKILL.md +2 -2
  482. package/template/.pi/skills/yss-excel-mvc/references/source-index.md +1 -1
  483. package/template/.pi/skills/yss-exception/SKILL.md +1 -1
  484. package/template/.pi/skills/yss-exception/references/source-index.md +1 -1
  485. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  486. package/template/.pi/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  487. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  488. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  489. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  490. package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  491. package/template/.pi/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  492. package/template/.pi/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  493. package/template/.pi/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  494. package/template/.pi/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  495. package/template/.pi/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  496. package/template/.pi/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  497. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  498. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  499. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  500. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  501. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  502. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  503. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  504. package/template/.pi/skills/yss-mybatis/SKILL.md +2 -2
  505. package/template/.pi/skills/yss-mybatis/references/source-index.md +1 -1
  506. package/template/.pi/skills/yss-page-module-development/SKILL.md +1 -1
  507. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +7 -7
  508. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  509. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  510. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  511. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +1 -1
  512. package/template/.pi/skills/yss-prototype-stage/SKILL.md +34 -24
  513. package/template/.pi/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  514. package/template/.pi/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  515. package/template/.pi/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  516. package/template/.pi/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  517. package/template/.pi/skills/yss-repository/SKILL.md +16 -14
  518. package/template/.pi/skills/yss-repository/agents/openai.yaml +2 -2
  519. package/template/.pi/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  520. package/template/.pi/skills/yss-research/SKILL.md +73 -0
  521. package/template/.pi/skills/yss-research/agents/openai.yaml +3 -0
  522. package/template/.pi/skills/yss-research/assets/evidence-template.yaml +68 -0
  523. package/template/.pi/skills/yss-research/assets/research-brief-template.md +36 -0
  524. package/template/.pi/skills/yss-research/references/evidence-contract.md +42 -0
  525. package/template/.pi/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  526. package/template/.pi/skills/yss-research/tests/run-scenarios.mjs +81 -0
  527. package/template/.pi/skills/yss-resilience4j/SKILL.md +1 -1
  528. package/template/.pi/skills/yss-resilience4j/references/source-index.md +1 -1
  529. package/template/.pi/skills/yss-security-algorithm/SKILL.md +1 -1
  530. package/template/.pi/skills/yss-security-algorithm/references/source-index.md +1 -1
  531. package/template/.pi/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  532. package/template/.pi/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  533. package/template/.pi/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  534. package/template/.pi/skills/yss-stage-decision/SKILL.md +2 -1
  535. package/template/.pi/skills/yss-ui/SKILL.md +2 -1
  536. package/template/.pi/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  537. package/template/.pi/skills/yss-up-springboot3/SKILL.md +1 -1
  538. package/template/.pi/skills/yss-userinfo/SKILL.md +1 -1
  539. package/template/.pi/skills/yss-userinfo/references/source-index.md +1 -1
  540. package/template/.pi/skills/yss-validation/SKILL.md +1 -1
  541. package/template/.pi/skills/yss-validation/references/source-index.md +1 -1
  542. package/template/.pi/skills/yss-web-controller/SKILL.md +17 -11
  543. package/template/.pi/skills/yss-web-controller/agents/openai.yaml +2 -2
  544. package/template/.pi/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  545. package/template/.pi/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  546. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  547. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  548. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  549. package/template/.pi/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  550. package/template/.pi/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  551. package/template/.pi/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  552. package/template/.pi/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  553. package/template/.qoder/skills/ask-matt/SKILL.md +1 -1
  554. package/template/.qoder/skills/code-review/SKILL.md +1 -1
  555. package/template/.qoder/skills/code-review/references/yss-review-standards.md +1 -1
  556. package/template/.qoder/skills/lombok/SKILL.md +1 -1
  557. package/template/.qoder/skills/mapstruct/SKILL.md +1 -1
  558. package/template/.qoder/skills/prototype-review/SKILL.md +7 -6
  559. package/template/.qoder/skills/wayfinder/SKILL.md +2 -2
  560. package/template/.qoder/skills/yss-antd-design/SKILL.md +8 -7
  561. package/template/.qoder/skills/yss-antd-design/references/evidence.md +11 -6
  562. package/template/.qoder/skills/yss-api-integration/SKILL.md +2 -2
  563. package/template/.qoder/skills/yss-application/SKILL.md +2 -2
  564. package/template/.qoder/skills/yss-audit-log/SKILL.md +2 -2
  565. package/template/.qoder/skills/yss-audit-log/references/source-index.md +1 -1
  566. package/template/.qoder/skills/yss-cache/SKILL.md +2 -2
  567. package/template/.qoder/skills/yss-cache/references/source-index.md +1 -1
  568. package/template/.qoder/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  569. package/template/.qoder/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  570. package/template/.qoder/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  571. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  572. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  573. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  574. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  575. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  576. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  577. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  578. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  579. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  580. package/template/.qoder/skills/yss-design-system/SKILL.md +6 -6
  581. package/template/.qoder/skills/yss-distributed-id/SKILL.md +2 -2
  582. package/template/.qoder/skills/yss-distributed-id/references/source-index.md +1 -1
  583. package/template/.qoder/skills/yss-domain/SKILL.md +3 -2
  584. package/template/.qoder/skills/yss-domain/agents/openai.yaml +2 -2
  585. package/template/.qoder/skills/yss-domain/references/domain-layer-guide.md +2 -0
  586. package/template/.qoder/skills/yss-dto/SKILL.md +8 -2
  587. package/template/.qoder/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  588. package/template/.qoder/skills/yss-dto/references/source-index.md +1 -1
  589. package/template/.qoder/skills/yss-excel-mvc/SKILL.md +2 -2
  590. package/template/.qoder/skills/yss-excel-mvc/references/source-index.md +1 -1
  591. package/template/.qoder/skills/yss-exception/SKILL.md +1 -1
  592. package/template/.qoder/skills/yss-exception/references/source-index.md +1 -1
  593. package/template/.qoder/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  594. package/template/.qoder/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  595. package/template/.qoder/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  596. package/template/.qoder/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  597. package/template/.qoder/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  598. package/template/.qoder/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  599. package/template/.qoder/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  600. package/template/.qoder/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  601. package/template/.qoder/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  602. package/template/.qoder/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  603. package/template/.qoder/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  604. package/template/.qoder/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  605. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  606. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  607. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  608. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  609. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  610. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  611. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  612. package/template/.qoder/skills/yss-mybatis/SKILL.md +2 -2
  613. package/template/.qoder/skills/yss-mybatis/references/source-index.md +1 -1
  614. package/template/.qoder/skills/yss-page-module-development/SKILL.md +1 -1
  615. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +7 -7
  616. package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  617. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  618. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  619. package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +1 -1
  620. package/template/.qoder/skills/yss-prototype-stage/SKILL.md +34 -24
  621. package/template/.qoder/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  622. package/template/.qoder/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  623. package/template/.qoder/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  624. package/template/.qoder/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  625. package/template/.qoder/skills/yss-repository/SKILL.md +16 -14
  626. package/template/.qoder/skills/yss-repository/agents/openai.yaml +2 -2
  627. package/template/.qoder/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  628. package/template/.qoder/skills/yss-research/SKILL.md +73 -0
  629. package/template/.qoder/skills/yss-research/agents/openai.yaml +3 -0
  630. package/template/.qoder/skills/yss-research/assets/evidence-template.yaml +68 -0
  631. package/template/.qoder/skills/yss-research/assets/research-brief-template.md +36 -0
  632. package/template/.qoder/skills/yss-research/references/evidence-contract.md +42 -0
  633. package/template/.qoder/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  634. package/template/.qoder/skills/yss-research/tests/run-scenarios.mjs +81 -0
  635. package/template/.qoder/skills/yss-resilience4j/SKILL.md +1 -1
  636. package/template/.qoder/skills/yss-resilience4j/references/source-index.md +1 -1
  637. package/template/.qoder/skills/yss-security-algorithm/SKILL.md +1 -1
  638. package/template/.qoder/skills/yss-security-algorithm/references/source-index.md +1 -1
  639. package/template/.qoder/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  640. package/template/.qoder/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  641. package/template/.qoder/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  642. package/template/.qoder/skills/yss-stage-decision/SKILL.md +2 -1
  643. package/template/.qoder/skills/yss-ui/SKILL.md +2 -1
  644. package/template/.qoder/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  645. package/template/.qoder/skills/yss-up-springboot3/SKILL.md +1 -1
  646. package/template/.qoder/skills/yss-userinfo/SKILL.md +1 -1
  647. package/template/.qoder/skills/yss-userinfo/references/source-index.md +1 -1
  648. package/template/.qoder/skills/yss-validation/SKILL.md +1 -1
  649. package/template/.qoder/skills/yss-validation/references/source-index.md +1 -1
  650. package/template/.qoder/skills/yss-web-controller/SKILL.md +17 -11
  651. package/template/.qoder/skills/yss-web-controller/agents/openai.yaml +2 -2
  652. package/template/.qoder/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  653. package/template/.qoder/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  654. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  655. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  656. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  657. package/template/.qoder/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  658. package/template/.qoder/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  659. package/template/.qoder/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  660. package/template/.qoder/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  661. package/template/.trae/skills/ask-matt/SKILL.md +1 -1
  662. package/template/.trae/skills/code-review/SKILL.md +1 -1
  663. package/template/.trae/skills/code-review/references/yss-review-standards.md +1 -1
  664. package/template/.trae/skills/lombok/SKILL.md +1 -1
  665. package/template/.trae/skills/mapstruct/SKILL.md +1 -1
  666. package/template/.trae/skills/prototype-review/SKILL.md +7 -6
  667. package/template/.trae/skills/wayfinder/SKILL.md +2 -2
  668. package/template/.trae/skills/yss-antd-design/SKILL.md +8 -7
  669. package/template/.trae/skills/yss-antd-design/references/evidence.md +11 -6
  670. package/template/.trae/skills/yss-api-integration/SKILL.md +2 -2
  671. package/template/.trae/skills/yss-application/SKILL.md +2 -2
  672. package/template/.trae/skills/yss-audit-log/SKILL.md +2 -2
  673. package/template/.trae/skills/yss-audit-log/references/source-index.md +1 -1
  674. package/template/.trae/skills/yss-cache/SKILL.md +2 -2
  675. package/template/.trae/skills/yss-cache/references/source-index.md +1 -1
  676. package/template/.trae/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  677. package/template/.trae/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  678. package/template/.trae/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  679. package/template/.trae/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  680. package/template/.trae/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  681. package/template/.trae/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  682. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  683. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  684. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  685. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  686. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  687. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  688. package/template/.trae/skills/yss-design-system/SKILL.md +6 -6
  689. package/template/.trae/skills/yss-distributed-id/SKILL.md +2 -2
  690. package/template/.trae/skills/yss-distributed-id/references/source-index.md +1 -1
  691. package/template/.trae/skills/yss-domain/SKILL.md +3 -2
  692. package/template/.trae/skills/yss-domain/agents/openai.yaml +2 -2
  693. package/template/.trae/skills/yss-domain/references/domain-layer-guide.md +2 -0
  694. package/template/.trae/skills/yss-dto/SKILL.md +8 -2
  695. package/template/.trae/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  696. package/template/.trae/skills/yss-dto/references/source-index.md +1 -1
  697. package/template/.trae/skills/yss-excel-mvc/SKILL.md +2 -2
  698. package/template/.trae/skills/yss-excel-mvc/references/source-index.md +1 -1
  699. package/template/.trae/skills/yss-exception/SKILL.md +1 -1
  700. package/template/.trae/skills/yss-exception/references/source-index.md +1 -1
  701. package/template/.trae/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  702. package/template/.trae/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  703. package/template/.trae/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  704. package/template/.trae/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  705. package/template/.trae/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  706. package/template/.trae/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  707. package/template/.trae/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  708. package/template/.trae/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  709. package/template/.trae/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  710. package/template/.trae/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  711. package/template/.trae/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  712. package/template/.trae/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  713. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  714. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  715. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  716. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  717. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  718. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  719. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  720. package/template/.trae/skills/yss-mybatis/SKILL.md +2 -2
  721. package/template/.trae/skills/yss-mybatis/references/source-index.md +1 -1
  722. package/template/.trae/skills/yss-page-module-development/SKILL.md +1 -1
  723. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +7 -7
  724. package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  725. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  726. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  727. package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +1 -1
  728. package/template/.trae/skills/yss-prototype-stage/SKILL.md +34 -24
  729. package/template/.trae/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  730. package/template/.trae/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  731. package/template/.trae/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  732. package/template/.trae/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  733. package/template/.trae/skills/yss-repository/SKILL.md +16 -14
  734. package/template/.trae/skills/yss-repository/agents/openai.yaml +2 -2
  735. package/template/.trae/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  736. package/template/.trae/skills/yss-research/SKILL.md +73 -0
  737. package/template/.trae/skills/yss-research/agents/openai.yaml +3 -0
  738. package/template/.trae/skills/yss-research/assets/evidence-template.yaml +68 -0
  739. package/template/.trae/skills/yss-research/assets/research-brief-template.md +36 -0
  740. package/template/.trae/skills/yss-research/references/evidence-contract.md +42 -0
  741. package/template/.trae/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  742. package/template/.trae/skills/yss-research/tests/run-scenarios.mjs +81 -0
  743. package/template/.trae/skills/yss-resilience4j/SKILL.md +1 -1
  744. package/template/.trae/skills/yss-resilience4j/references/source-index.md +1 -1
  745. package/template/.trae/skills/yss-security-algorithm/SKILL.md +1 -1
  746. package/template/.trae/skills/yss-security-algorithm/references/source-index.md +1 -1
  747. package/template/.trae/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  748. package/template/.trae/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  749. package/template/.trae/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  750. package/template/.trae/skills/yss-stage-decision/SKILL.md +2 -1
  751. package/template/.trae/skills/yss-ui/SKILL.md +2 -1
  752. package/template/.trae/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  753. package/template/.trae/skills/yss-up-springboot3/SKILL.md +1 -1
  754. package/template/.trae/skills/yss-userinfo/SKILL.md +1 -1
  755. package/template/.trae/skills/yss-userinfo/references/source-index.md +1 -1
  756. package/template/.trae/skills/yss-validation/SKILL.md +1 -1
  757. package/template/.trae/skills/yss-validation/references/source-index.md +1 -1
  758. package/template/.trae/skills/yss-web-controller/SKILL.md +17 -11
  759. package/template/.trae/skills/yss-web-controller/agents/openai.yaml +2 -2
  760. package/template/.trae/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  761. package/template/.trae/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  762. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  763. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  764. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  765. package/template/.trae/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  766. package/template/.trae/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  767. package/template/.trae/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  768. package/template/.trae/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  769. package/template/AGENTS.md +13 -13
  770. package/template/CONTEXT.md +9 -8
  771. package/template/README.md +0 -1
  772. package/template/docs/agents/digital-human-roles.md +3 -3
  773. package/template/docs/agents/digital-human-roles.yaml +5 -5
  774. package/template/docs/agents/skill-migrations.md +18 -5
  775. package/template/docs/agents/skills-maintenance.md +3 -3
  776. package/template/docs/agents/strategic-design-skills-integration.md +41 -0
  777. package/template/docs/agents/yss-skill-registry.yaml +137 -16
  778. package/template/docs/api/templates/openapi-draft-review-checklist.md +3 -3
  779. package/template/docs/architecture/templates/engineering-baseline-review-template.md +3 -3
  780. package/template/docs/design/README.md +7 -7
  781. package/template/docs/design/design.md +25 -17
  782. package/template/docs/design/templates/interaction-spec-template.md +4 -4
  783. package/template/docs/design/templates/prototype-confirmation-template.md +13 -15
  784. package/template/docs/design/templates/prototype-evidence-template.yaml +99 -75
  785. package/template/docs/design/templates/prototype-review-checklist.md +5 -4
  786. package/template/docs/design/templates/state-matrix-template.md +11 -0
  787. package/template/docs/discovery/reports/agent-governance-options.md +8 -8
  788. package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +2 -1
  789. package/template/docs/process/implementation-repo-integration.md +1 -1
  790. package/template/docs/process/lifecycle-artifact-map.md +4 -2
  791. package/template/docs/process/lifecycle-registry-baseline.json +4 -1
  792. package/template/docs/process/lifecycle-registry.yaml +15 -9
  793. package/template/docs/process/template-engineering-overview.md +2 -2
  794. package/template/docs/process/template-verification-profiles.yaml +27 -6
  795. package/template/docs/templates/build-architecture-checklist-template.md +6 -6
  796. package/template/docs/templates/implementation-routing-template.md +8 -8
  797. package/template/docs/templates/requirement-freeze-template.md +2 -2
  798. package/template/docs/templates/review-report-template.md +4 -4
  799. package/template/docs/templates/spec-template.md +1 -1
  800. package/template/docs/templates/vertical-slice-ticket-template.md +4 -4
  801. package/template/docs/user-guide/yss-ui-mcp.md +0 -2
  802. 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 +2 -2
  803. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +5 -5
  804. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +4 -4
  805. package/template/scripts/audit-yss-backend-skills +56 -0
  806. package/template/scripts/generate-yss-skill-boundaries +67 -0
  807. package/template/scripts/lib/export-skills.mjs +4 -4
  808. package/template/scripts/lib/implementation-contract-compiler.mjs +194 -0
  809. package/template/scripts/lib/scenario-checks.mjs +17 -10
  810. package/template/scripts/lib/skill-governance.mjs +27 -5
  811. package/template/scripts/lib/skill-registry.mjs +129 -41
  812. package/template/scripts/lib/skill-supply-chain.mjs +47 -6
  813. package/template/scripts/verify-repository-scope-scenarios +2 -2
  814. package/template/scripts/verify-retired-skill-ids +37 -0
  815. package/template/scripts/verify-scaffold-generator-scenarios +18 -18
  816. package/template/scripts/verify-template-verification-scenarios +4 -1
  817. package/template/scripts/verify-yss-dto-openapi-profile +8 -0
  818. package/template/scripts/verify-yss-implementation-contract-compiler-scenarios +14 -0
  819. package/template/skills-lock.json +101 -171
  820. package/template/yss-public-skills.json +4 -4
  821. package/template.manifest.json +0 -3
  822. package/template.snapshot.json +5 -5
  823. package/template/.agents/skills/research/SKILL.md +0 -12
  824. package/template/.agents/skills/research/agents/openai.yaml +0 -3
  825. package/template/.agents/skills/yss-router/SKILL.md +0 -51
  826. package/template/.agents/skills/yss-router/agents/openai.yaml +0 -4
  827. package/template/.agents/skills/yss-router/references/boundaries.md +0 -50
  828. package/template/.agents/skills/yss-router/references/router-contract.yaml +0 -345
  829. package/template/.agents/skills/yss-router/references/slice-implementation-contract.md +0 -185
  830. package/template/.agents/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  831. package/template/.agents/skills/yss-source-index/SKILL.md +0 -63
  832. package/template/.agents/skills/yss-source-index/agents/openai.yaml +0 -4
  833. package/template/.agents/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  834. package/template/.claude/skills/research/SKILL.md +0 -12
  835. package/template/.claude/skills/research/agents/openai.yaml +0 -3
  836. package/template/.claude/skills/yss-router/SKILL.md +0 -51
  837. package/template/.claude/skills/yss-router/agents/openai.yaml +0 -4
  838. package/template/.claude/skills/yss-router/references/boundaries.md +0 -50
  839. package/template/.claude/skills/yss-router/references/router-contract.yaml +0 -345
  840. package/template/.claude/skills/yss-router/references/slice-implementation-contract.md +0 -185
  841. package/template/.claude/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  842. package/template/.claude/skills/yss-source-index/SKILL.md +0 -63
  843. package/template/.claude/skills/yss-source-index/agents/openai.yaml +0 -4
  844. package/template/.claude/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  845. package/template/.codex/skills/research/SKILL.md +0 -12
  846. package/template/.codex/skills/research/agents/openai.yaml +0 -3
  847. package/template/.codex/skills/yss-router/SKILL.md +0 -51
  848. package/template/.codex/skills/yss-router/agents/openai.yaml +0 -4
  849. package/template/.codex/skills/yss-router/references/boundaries.md +0 -50
  850. package/template/.codex/skills/yss-router/references/router-contract.yaml +0 -345
  851. package/template/.codex/skills/yss-router/references/slice-implementation-contract.md +0 -185
  852. package/template/.codex/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  853. package/template/.codex/skills/yss-source-index/SKILL.md +0 -63
  854. package/template/.codex/skills/yss-source-index/agents/openai.yaml +0 -4
  855. package/template/.codex/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  856. package/template/.cursor/skills/research/SKILL.md +0 -12
  857. package/template/.cursor/skills/research/agents/openai.yaml +0 -3
  858. package/template/.cursor/skills/yss-router/SKILL.md +0 -51
  859. package/template/.cursor/skills/yss-router/agents/openai.yaml +0 -4
  860. package/template/.cursor/skills/yss-router/references/boundaries.md +0 -50
  861. package/template/.cursor/skills/yss-router/references/router-contract.yaml +0 -345
  862. package/template/.cursor/skills/yss-router/references/slice-implementation-contract.md +0 -185
  863. package/template/.cursor/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  864. package/template/.cursor/skills/yss-source-index/SKILL.md +0 -63
  865. package/template/.cursor/skills/yss-source-index/agents/openai.yaml +0 -4
  866. package/template/.cursor/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  867. package/template/.gemini/settings.json +0 -8
  868. package/template/.hermes/skills/alibaba-java-code-style/SKILL.md +0 -51
  869. package/template/.hermes/skills/alibaba-java-code-style/agents/openai.yaml +0 -4
  870. package/template/.hermes/skills/alibaba-java-code-style/references/database-engineering-design.md +0 -147
  871. package/template/.hermes/skills/alibaba-java-code-style/references/java-programming.md +0 -178
  872. package/template/.hermes/skills/alibaba-java-code-style/references/reliability-test-security.md +0 -89
  873. package/template/.hermes/skills/archify/LICENSE +0 -22
  874. package/template/.hermes/skills/archify/SKILL.md +0 -156
  875. package/template/.hermes/skills/archify/assets/template.html +0 -14787
  876. package/template/.hermes/skills/archify/bin/archify.mjs +0 -1990
  877. package/template/.hermes/skills/archify/bin/open-artifact.mjs +0 -86
  878. package/template/.hermes/skills/archify/bin/preview.mjs +0 -648
  879. package/template/.hermes/skills/archify/bin/visual-check.mjs +0 -829
  880. package/template/.hermes/skills/archify/brand-marks/README.md +0 -31
  881. package/template/.hermes/skills/archify/brand-marks/catalog.json +0 -131
  882. package/template/.hermes/skills/archify/delta/architecture-delta.mjs +0 -1214
  883. package/template/.hermes/skills/archify/examples/agent-run.lifecycle.json +0 -71
  884. package/template/.hermes/skills/archify/examples/agent-tool-call.workflow.json +0 -94
  885. package/template/.hermes/skills/archify/examples/async-job-roundtrip.sequence.json +0 -61
  886. package/template/.hermes/skills/archify/examples/brand-aware-delivery.architecture.json +0 -47
  887. package/template/.hermes/skills/archify/examples/cache-miss-request.sequence.json +0 -81
  888. package/template/.hermes/skills/archify/examples/checkout-platform.base.architecture.json +0 -31
  889. package/template/.hermes/skills/archify/examples/checkout-platform.head.architecture.json +0 -31
  890. package/template/.hermes/skills/archify/examples/dataflow-product-analytics.html +0 -14898
  891. package/template/.hermes/skills/archify/examples/deployment-release.lifecycle.json +0 -49
  892. package/template/.hermes/skills/archify/examples/event-stream.dataflow.json +0 -57
  893. package/template/.hermes/skills/archify/examples/incident-response.workflow.json +0 -64
  894. package/template/.hermes/skills/archify/examples/lifecycle-agent-run.html +0 -14847
  895. package/template/.hermes/skills/archify/examples/product-analytics.dataflow.json +0 -76
  896. package/template/.hermes/skills/archify/examples/production-deployment.architecture.json +0 -71
  897. package/template/.hermes/skills/archify/examples/release-delivery.workflow.json +0 -62
  898. package/template/.hermes/skills/archify/examples/sequence-cache-miss-request.html +0 -14913
  899. package/template/.hermes/skills/archify/examples/web-app-rendered.html +0 -14862
  900. package/template/.hermes/skills/archify/examples/web-app.architecture.json +0 -46
  901. package/template/.hermes/skills/archify/examples/workflow-agent-tool-call-rendered.html +0 -14904
  902. package/template/.hermes/skills/archify/migrations/workflow-v2.mjs +0 -279
  903. package/template/.hermes/skills/archify/package-lock.json +0 -149
  904. package/template/.hermes/skills/archify/package.json +0 -37
  905. package/template/.hermes/skills/archify/recipes/scenarios.mjs +0 -391
  906. package/template/.hermes/skills/archify/references/authoring-contract.md +0 -191
  907. package/template/.hermes/skills/archify/references/brand-marks.md +0 -65
  908. package/template/.hermes/skills/archify/references/delivery-contract.md +0 -112
  909. package/template/.hermes/skills/archify/references/viewer-runtime.md +0 -45
  910. package/template/.hermes/skills/archify/renderers/architecture/grid.mjs +0 -62
  911. package/template/.hermes/skills/archify/renderers/architecture/render-architecture.mjs +0 -1078
  912. package/template/.hermes/skills/archify/renderers/dataflow/README.md +0 -104
  913. package/template/.hermes/skills/archify/renderers/dataflow/render-dataflow.mjs +0 -483
  914. package/template/.hermes/skills/archify/renderers/lifecycle/README.md +0 -115
  915. package/template/.hermes/skills/archify/renderers/lifecycle/render-lifecycle.mjs +0 -561
  916. package/template/.hermes/skills/archify/renderers/sequence/README.md +0 -114
  917. package/template/.hermes/skills/archify/renderers/sequence/render-sequence.mjs +0 -453
  918. package/template/.hermes/skills/archify/renderers/shared/brand-marks.mjs +0 -563
  919. package/template/.hermes/skills/archify/renderers/shared/cli.mjs +0 -218
  920. package/template/.hermes/skills/archify/renderers/shared/desktop-readability.mjs +0 -26
  921. package/template/.hermes/skills/archify/renderers/shared/diagnostics.mjs +0 -127
  922. package/template/.hermes/skills/archify/renderers/shared/engineering-profiles.mjs +0 -157
  923. package/template/.hermes/skills/archify/renderers/shared/generated-brand-marks.mjs +0 -2003
  924. package/template/.hermes/skills/archify/renderers/shared/generated-validators.mjs +0 -13
  925. package/template/.hermes/skills/archify/renderers/shared/geometry.mjs +0 -1423
  926. package/template/.hermes/skills/archify/renderers/shared/i18n.mjs +0 -594
  927. package/template/.hermes/skills/archify/renderers/shared/layout-report.mjs +0 -40
  928. package/template/.hermes/skills/archify/renderers/shared/legend.mjs +0 -217
  929. package/template/.hermes/skills/archify/renderers/shared/output-path.mjs +0 -321
  930. package/template/.hermes/skills/archify/renderers/shared/repository-evidence.mjs +0 -235
  931. package/template/.hermes/skills/archify/renderers/shared/text-fit.mjs +0 -49
  932. package/template/.hermes/skills/archify/renderers/shared/utils.mjs +0 -232
  933. package/template/.hermes/skills/archify/renderers/shared/validator.mjs +0 -86
  934. package/template/.hermes/skills/archify/renderers/workflow/README.md +0 -223
  935. package/template/.hermes/skills/archify/renderers/workflow/render-workflow.mjs +0 -35
  936. package/template/.hermes/skills/archify/renderers/workflow/workflow-compiler.mjs +0 -4400
  937. package/template/.hermes/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +0 -144
  938. package/template/.hermes/skills/archify/schemas/README.md +0 -211
  939. package/template/.hermes/skills/archify/schemas/architecture.schema.json +0 -176
  940. package/template/.hermes/skills/archify/schemas/common.schema.json +0 -115
  941. package/template/.hermes/skills/archify/schemas/dataflow.schema.json +0 -243
  942. package/template/.hermes/skills/archify/schemas/lifecycle.schema.json +0 -266
  943. package/template/.hermes/skills/archify/schemas/sequence.schema.json +0 -223
  944. package/template/.hermes/skills/archify/schemas/workflow.schema.json +0 -428
  945. package/template/.hermes/skills/archify/scripts/check-render-output.mjs +0 -835
  946. package/template/.hermes/skills/archify/scripts/check-update.mjs +0 -1667
  947. package/template/.hermes/skills/archify/scripts/generate-brand-marks.mjs +0 -141
  948. package/template/.hermes/skills/archify/scripts/generate-validators.mjs +0 -66
  949. package/template/.hermes/skills/archify/scripts/render-examples.mjs +0 -26
  950. package/template/.hermes/skills/archify/scripts/update-contract.mjs +0 -182
  951. package/template/.hermes/skills/archify/scripts/yss-safe-deliver.mjs +0 -214
  952. package/template/.hermes/skills/archify/skill-release.json +0 -10
  953. package/template/.hermes/skills/archify/test/adaptive-reader-layout.test.mjs +0 -86
  954. package/template/.hermes/skills/archify/test/animation.test.mjs +0 -179
  955. package/template/.hermes/skills/archify/test/architecture-delta.test.mjs +0 -518
  956. package/template/.hermes/skills/archify/test/authored-reachability.test.mjs +0 -95
  957. package/template/.hermes/skills/archify/test/authoring-safety-contract.test.mjs +0 -54
  958. package/template/.hermes/skills/archify/test/automatic-port-spread.test.mjs +0 -376
  959. package/template/.hermes/skills/archify/test/base-input-compatibility.test.mjs +0 -42
  960. package/template/.hermes/skills/archify/test/brand-marks.test.mjs +0 -654
  961. package/template/.hermes/skills/archify/test/chapter-delta-preview.test.mjs +0 -157
  962. package/template/.hermes/skills/archify/test/chapter-handoff.test.mjs +0 -97
  963. package/template/.hermes/skills/archify/test/chapter-rail.test.mjs +0 -86
  964. package/template/.hermes/skills/archify/test/checkout-line-endings.test.mjs +0 -72
  965. package/template/.hermes/skills/archify/test/clean-skill-staging.test.mjs +0 -244
  966. package/template/.hermes/skills/archify/test/cli.test.mjs +0 -809
  967. package/template/.hermes/skills/archify/test/community-proof-intake.test.mjs +0 -104
  968. package/template/.hermes/skills/archify/test/cursor-onboarding.test.mjs +0 -74
  969. package/template/.hermes/skills/archify/test/degraded.test.mjs +0 -161
  970. package/template/.hermes/skills/archify/test/delivery-contract.test.mjs +0 -52
  971. package/template/.hermes/skills/archify/test/desktop-reader-browser.test.mjs +0 -58
  972. package/template/.hermes/skills/archify/test/diagram-guide.test.mjs +0 -92
  973. package/template/.hermes/skills/archify/test/engineering-profile.test.mjs +0 -209
  974. package/template/.hermes/skills/archify/test/finder.test.mjs +0 -104
  975. package/template/.hermes/skills/archify/test/fixtures/automatic-routing-node-border-clearance.workflow.json +0 -40
  976. package/template/.hermes/skills/archify/test/fixtures/fail-migration-cleanup.mjs +0 -17
  977. package/template/.hermes/skills/archify/test/fixtures/issue-126/custom-widths.workflow.json +0 -18
  978. package/template/.hermes/skills/archify/test/fixtures/issue-126/explicit-route.workflow.json +0 -28
  979. package/template/.hermes/skills/archify/test/fixtures/issue-126/explicit-viewbox.workflow.json +0 -19
  980. package/template/.hermes/skills/archify/test/fixtures/issue-126/labels.workflow.json +0 -18
  981. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/agent-run.lifecycle.json +0 -64
  982. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/agent-tool-call.workflow.json +0 -88
  983. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/cache-miss-request.sequence.json +0 -75
  984. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/event-stream.dataflow.json +0 -57
  985. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/product-analytics.dataflow.json +0 -70
  986. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/production-deployment.architecture.json +0 -71
  987. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/web-app.architecture.json +0 -41
  988. package/template/.hermes/skills/archify/test/fixtures/v1-workflow-700x400.workflow.json +0 -22
  989. package/template/.hermes/skills/archify/test/fixtures/v1-workflow-explicit-coordinates.workflow.json +0 -47
  990. package/template/.hermes/skills/archify/test/gallery.test.mjs +0 -130
  991. package/template/.hermes/skills/archify/test/generate-validators.test.mjs +0 -59
  992. package/template/.hermes/skills/archify/test/generated-artifact-xml.test.mjs +0 -88
  993. package/template/.hermes/skills/archify/test/geometry.test.mjs +0 -739
  994. package/template/.hermes/skills/archify/test/golden.mjs +0 -207
  995. package/template/.hermes/skills/archify/test/grid.test.mjs +0 -47
  996. package/template/.hermes/skills/archify/test/guide-page.test.mjs +0 -59
  997. package/template/.hermes/skills/archify/test/guide.test.mjs +0 -81
  998. package/template/.hermes/skills/archify/test/guided-views.test.mjs +0 -144
  999. package/template/.hermes/skills/archify/test/helpers/xml.mjs +0 -37
  1000. package/template/.hermes/skills/archify/test/i18n.test.mjs +0 -422
  1001. package/template/.hermes/skills/archify/test/intent-trace.test.mjs +0 -111
  1002. package/template/.hermes/skills/archify/test/landing.test.mjs +0 -97
  1003. package/template/.hermes/skills/archify/test/layout-rules.test.mjs +0 -1449
  1004. package/template/.hermes/skills/archify/test/legend-contract.test.mjs +0 -404
  1005. package/template/.hermes/skills/archify/test/motion-governor.test.mjs +0 -111
  1006. package/template/.hermes/skills/archify/test/open-artifact.test.mjs +0 -118
  1007. package/template/.hermes/skills/archify/test/ordinary-model-floor.test.mjs +0 -1024
  1008. package/template/.hermes/skills/archify/test/output-path.test.mjs +0 -587
  1009. package/template/.hermes/skills/archify/test/presentation.test.mjs +0 -71
  1010. package/template/.hermes/skills/archify/test/preset-tryon.test.mjs +0 -113
  1011. package/template/.hermes/skills/archify/test/preview-contract.test.mjs +0 -40
  1012. package/template/.hermes/skills/archify/test/preview.test.mjs +0 -499
  1013. package/template/.hermes/skills/archify/test/proof-aperture.test.mjs +0 -78
  1014. package/template/.hermes/skills/archify/test/reach-share-card.test.mjs +0 -149
  1015. package/template/.hermes/skills/archify/test/readme-showcase.test.mjs +0 -318
  1016. package/template/.hermes/skills/archify/test/real-repository-proof.test.mjs +0 -209
  1017. package/template/.hermes/skills/archify/test/relationship-direct-explorer.test.mjs +0 -115
  1018. package/template/.hermes/skills/archify/test/relationship-lens.test.mjs +0 -128
  1019. package/template/.hermes/skills/archify/test/relationship-permalink.test.mjs +0 -130
  1020. package/template/.hermes/skills/archify/test/relationship-pulse.test.mjs +0 -122
  1021. package/template/.hermes/skills/archify/test/release-identity.test.mjs +0 -504
  1022. package/template/.hermes/skills/archify/test/release-package-gates.test.mjs +0 -508
  1023. package/template/.hermes/skills/archify/test/render-output-checks.test.mjs +0 -435
  1024. package/template/.hermes/skills/archify/test/repair-receipt.test.mjs +0 -181
  1025. package/template/.hermes/skills/archify/test/repository-evidence.test.mjs +0 -217
  1026. package/template/.hermes/skills/archify/test/repository-language-metadata.test.mjs +0 -52
  1027. package/template/.hermes/skills/archify/test/route-journey.test.mjs +0 -123
  1028. package/template/.hermes/skills/archify/test/route-probe.test.mjs +0 -114
  1029. package/template/.hermes/skills/archify/test/route-share-card.test.mjs +0 -163
  1030. package/template/.hermes/skills/archify/test/semantic-camera.test.mjs +0 -88
  1031. package/template/.hermes/skills/archify/test/semantic-flow.test.mjs +0 -90
  1032. package/template/.hermes/skills/archify/test/semantic-legend-gateway.test.mjs +0 -156
  1033. package/template/.hermes/skills/archify/test/semantic-lens.test.mjs +0 -103
  1034. package/template/.hermes/skills/archify/test/semantic-passport.test.mjs +0 -99
  1035. package/template/.hermes/skills/archify/test/semantic-radar.test.mjs +0 -575
  1036. package/template/.hermes/skills/archify/test/semantic-zoom.test.mjs +0 -86
  1037. package/template/.hermes/skills/archify/test/sequence-column-fit.test.mjs +0 -137
  1038. package/template/.hermes/skills/archify/test/settled-flow.test.mjs +0 -97
  1039. package/template/.hermes/skills/archify/test/share-card-export.test.mjs +0 -132
  1040. package/template/.hermes/skills/archify/test/site-language-continuity.test.mjs +0 -562
  1041. package/template/.hermes/skills/archify/test/site-language-integration.mjs +0 -9
  1042. package/template/.hermes/skills/archify/test/skill-metadata.test.mjs +0 -84
  1043. package/template/.hermes/skills/archify/test/stable-update-manifest.test.mjs +0 -167
  1044. package/template/.hermes/skills/archify/test/start-page.test.mjs +0 -312
  1045. package/template/.hermes/skills/archify/test/story-beat-navigator.test.mjs +0 -103
  1046. package/template/.hermes/skills/archify/test/story-carrier.test.mjs +0 -90
  1047. package/template/.hermes/skills/archify/test/story-director-strip.test.mjs +0 -91
  1048. package/template/.hermes/skills/archify/test/story-follow-camera.test.mjs +0 -103
  1049. package/template/.hermes/skills/archify/test/story-horizon.test.mjs +0 -89
  1050. package/template/.hermes/skills/archify/test/story-moment-link.test.mjs +0 -93
  1051. package/template/.hermes/skills/archify/test/story-shelf.test.mjs +0 -84
  1052. package/template/.hermes/skills/archify/test/story-trail.test.mjs +0 -81
  1053. package/template/.hermes/skills/archify/test/toolbar-polish.test.mjs +0 -57
  1054. package/template/.hermes/skills/archify/test/update-contract.test.mjs +0 -133
  1055. package/template/.hermes/skills/archify/test/update-notifier.test.mjs +0 -3317
  1056. package/template/.hermes/skills/archify/test/v1-compatibility.test.mjs +0 -329
  1057. package/template/.hermes/skills/archify/test/vertical-edge.test.mjs +0 -68
  1058. package/template/.hermes/skills/archify/test/viewer-chrome-layout.test.mjs +0 -872
  1059. package/template/.hermes/skills/archify/test/visual-check.test.mjs +0 -400
  1060. package/template/.hermes/skills/archify/test/webm-artifact.smoke.mjs +0 -1840
  1061. package/template/.hermes/skills/archify/test/workflow-compiler-hard-contract.test.mjs +0 -2135
  1062. package/template/.hermes/skills/archify/test/workflow-compiler.test.mjs +0 -1163
  1063. package/template/.hermes/skills/archify/test/workflow-migration.test.mjs +0 -620
  1064. package/template/.hermes/skills/archify/test/workflow-semantic-contract.test.mjs +0 -131
  1065. package/template/.hermes/skills/archify/test/yss-safe-deliver.test.mjs +0 -153
  1066. package/template/.hermes/skills/ask-matt/PHASE-BOUNDARIES.md +0 -55
  1067. package/template/.hermes/skills/ask-matt/SKILL.md +0 -95
  1068. package/template/.hermes/skills/ask-matt/agents/openai.yaml +0 -5
  1069. package/template/.hermes/skills/code-review/SKILL.md +0 -122
  1070. package/template/.hermes/skills/code-review/agents/openai.yaml +0 -3
  1071. package/template/.hermes/skills/code-review/references/yss-review-standards.md +0 -54
  1072. package/template/.hermes/skills/codebase-design/DEEPENING.md +0 -37
  1073. package/template/.hermes/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
  1074. package/template/.hermes/skills/codebase-design/SKILL.md +0 -118
  1075. package/template/.hermes/skills/codebase-design/agents/openai.yaml +0 -3
  1076. package/template/.hermes/skills/competitive-intelligence/SKILL.md +0 -94
  1077. package/template/.hermes/skills/component-selection-imports/SKILL.md +0 -64
  1078. package/template/.hermes/skills/cross-repo-implementation-routing/SKILL.md +0 -51
  1079. package/template/.hermes/skills/diagnosing-bugs/SKILL.md +0 -138
  1080. package/template/.hermes/skills/diagnosing-bugs/agents/openai.yaml +0 -3
  1081. package/template/.hermes/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -44
  1082. package/template/.hermes/skills/dispatching-parallel-agents/SKILL.md +0 -185
  1083. package/template/.hermes/skills/domain-modeling/ADR-FORMAT.md +0 -47
  1084. package/template/.hermes/skills/domain-modeling/CONTEXT-FORMAT.md +0 -60
  1085. package/template/.hermes/skills/domain-modeling/SKILL.md +0 -74
  1086. package/template/.hermes/skills/domain-modeling/agents/openai.yaml +0 -3
  1087. package/template/.hermes/skills/file-export-download/SKILL.md +0 -160
  1088. package/template/.hermes/skills/file-export-download/agents/openai.yaml +0 -4
  1089. package/template/.hermes/skills/formily-foundation/SKILL.md +0 -176
  1090. package/template/.hermes/skills/formily-foundation/references/examples.md +0 -486
  1091. package/template/.hermes/skills/formily-linkage-effects/SKILL.md +0 -167
  1092. package/template/.hermes/skills/formily-mode-slot-detail/SKILL.md +0 -137
  1093. package/template/.hermes/skills/formily-step-flow/SKILL.md +0 -194
  1094. package/template/.hermes/skills/frontend-commit/SKILL.md +0 -118
  1095. package/template/.hermes/skills/frontend-commit/agents/openai.yaml +0 -7
  1096. package/template/.hermes/skills/frontend-commit/references/scope-and-examples.md +0 -111
  1097. package/template/.hermes/skills/grill-me/SKILL.md +0 -7
  1098. package/template/.hermes/skills/grill-me/agents/openai.yaml +0 -5
  1099. package/template/.hermes/skills/grill-with-docs/SKILL.md +0 -19
  1100. package/template/.hermes/skills/grill-with-docs/agents/openai.yaml +0 -5
  1101. package/template/.hermes/skills/grilling/SKILL.md +0 -28
  1102. package/template/.hermes/skills/grilling/agents/openai.yaml +0 -3
  1103. package/template/.hermes/skills/handoff/SKILL.md +0 -16
  1104. package/template/.hermes/skills/handoff/agents/openai.yaml +0 -5
  1105. package/template/.hermes/skills/implement/SKILL.md +0 -15
  1106. package/template/.hermes/skills/implement/agents/openai.yaml +0 -5
  1107. package/template/.hermes/skills/implementation-repo-onboarding/SKILL.md +0 -50
  1108. package/template/.hermes/skills/improve-codebase-architecture/HTML-REPORT.md +0 -123
  1109. package/template/.hermes/skills/improve-codebase-architecture/SKILL.md +0 -71
  1110. package/template/.hermes/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  1111. package/template/.hermes/skills/java-backend-commit/SKILL.md +0 -121
  1112. package/template/.hermes/skills/java-backend-commit/agents/openai.yaml +0 -7
  1113. package/template/.hermes/skills/java-backend-commit/references/scope-and-examples.md +0 -118
  1114. package/template/.hermes/skills/llm-wiki/SKILL.md +0 -60
  1115. package/template/.hermes/skills/llm-wiki/assets/CLAUDE.md.template +0 -26
  1116. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/CLAUDE.md +0 -3
  1117. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/index.md +0 -5
  1118. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/log.md +0 -1
  1119. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki//345/255/230/345/234/250/347/232/204/351/241/265.md +0 -7
  1120. package/template/.hermes/skills/llm-wiki/references/compile.md +0 -126
  1121. package/template/.hermes/skills/llm-wiki/references/discover.md +0 -42
  1122. package/template/.hermes/skills/llm-wiki/references/ingest.md +0 -36
  1123. package/template/.hermes/skills/llm-wiki/references/lint.md +0 -54
  1124. package/template/.hermes/skills/llm-wiki/references/query.md +0 -21
  1125. package/template/.hermes/skills/llm-wiki/references/schema.md +0 -45
  1126. package/template/.hermes/skills/llm-wiki/references/writing.md +0 -35
  1127. package/template/.hermes/skills/llm-wiki/scripts/advise.mjs +0 -196
  1128. package/template/.hermes/skills/llm-wiki/scripts/advise.test.mjs +0 -91
  1129. package/template/.hermes/skills/llm-wiki/scripts/extract.mjs +0 -55
  1130. package/template/.hermes/skills/llm-wiki/scripts/extract.test.mjs +0 -34
  1131. package/template/.hermes/skills/llm-wiki/scripts/inventory.mjs +0 -174
  1132. package/template/.hermes/skills/llm-wiki/scripts/inventory.test.mjs +0 -98
  1133. package/template/.hermes/skills/llm-wiki/scripts/lint-wikilinks.mjs +0 -177
  1134. package/template/.hermes/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +0 -212
  1135. package/template/.hermes/skills/lombok/SKILL.md +0 -351
  1136. package/template/.hermes/skills/loop-me/SKILL.md +0 -32
  1137. package/template/.hermes/skills/loop-me/agents/openai.yaml +0 -5
  1138. package/template/.hermes/skills/maintaining-skills/SKILL.md +0 -27
  1139. package/template/.hermes/skills/mapstruct/SKILL.md +0 -334
  1140. package/template/.hermes/skills/migrate-to-shoehorn/SKILL.md +0 -118
  1141. package/template/.hermes/skills/migrate-to-shoehorn/agents/openai.yaml +0 -3
  1142. package/template/.hermes/skills/page-form-module/SKILL.md +0 -168
  1143. package/template/.hermes/skills/page-list-module/SKILL.md +0 -137
  1144. package/template/.hermes/skills/page-skeleton/SKILL.md +0 -76
  1145. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/constant.ts +0 -34
  1146. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -75
  1147. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/index.vue +0 -107
  1148. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
  1149. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/style.less +0 -30
  1150. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/constant.ts +0 -28
  1151. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -58
  1152. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/hooks/useRuleActions.ts +0 -65
  1153. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/index.vue +0 -91
  1154. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/style.less +0 -49
  1155. package/template/.hermes/skills/prototype/LOGIC.md +0 -67
  1156. package/template/.hermes/skills/prototype/SKILL.md +0 -26
  1157. package/template/.hermes/skills/prototype/UI.md +0 -112
  1158. package/template/.hermes/skills/prototype/agents/openai.yaml +0 -3
  1159. package/template/.hermes/skills/prototype-page-acceptance/SKILL.md +0 -62
  1160. package/template/.hermes/skills/prototype-review/SKILL.md +0 -74
  1161. package/template/.hermes/skills/prototype-review/agents/openai.yaml +0 -4
  1162. package/template/.hermes/skills/research/SKILL.md +0 -12
  1163. package/template/.hermes/skills/research/agents/openai.yaml +0 -3
  1164. package/template/.hermes/skills/resolving-merge-conflicts/SKILL.md +0 -14
  1165. package/template/.hermes/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
  1166. package/template/.hermes/skills/scaffold-exercises/SKILL.md +0 -106
  1167. package/template/.hermes/skills/scaffold-exercises/agents/openai.yaml +0 -3
  1168. package/template/.hermes/skills/setup-matt-pocock-skills/SKILL.md +0 -125
  1169. package/template/.hermes/skills/setup-matt-pocock-skills/agents/openai.yaml +0 -5
  1170. package/template/.hermes/skills/setup-matt-pocock-skills/domain.md +0 -51
  1171. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-github.md +0 -45
  1172. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +0 -46
  1173. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-local.md +0 -66
  1174. package/template/.hermes/skills/setup-matt-pocock-skills/triage-labels.md +0 -15
  1175. package/template/.hermes/skills/setup-pre-commit/SKILL.md +0 -91
  1176. package/template/.hermes/skills/setup-pre-commit/agents/openai.yaml +0 -3
  1177. package/template/.hermes/skills/setup-ts-deep-modules/SKILL.md +0 -102
  1178. package/template/.hermes/skills/setup-ts-deep-modules/agents/openai.yaml +0 -5
  1179. package/template/.hermes/skills/setup-ts-deep-modules/dependency-cruiser.config.cjs +0 -95
  1180. package/template/.hermes/skills/tdd/SKILL.md +0 -38
  1181. package/template/.hermes/skills/tdd/agents/openai.yaml +0 -3
  1182. package/template/.hermes/skills/tdd/mocking.md +0 -59
  1183. package/template/.hermes/skills/tdd/tests.md +0 -77
  1184. package/template/.hermes/skills/teach/GLOSSARY-FORMAT.md +0 -35
  1185. package/template/.hermes/skills/teach/LEARNING-RECORD-FORMAT.md +0 -46
  1186. package/template/.hermes/skills/teach/MISSION-FORMAT.md +0 -31
  1187. package/template/.hermes/skills/teach/RESOURCES-FORMAT.md +0 -32
  1188. package/template/.hermes/skills/teach/SKILL.md +0 -140
  1189. package/template/.hermes/skills/teach/agents/openai.yaml +0 -5
  1190. package/template/.hermes/skills/theme-token-usage/SKILL.md +0 -123
  1191. package/template/.hermes/skills/theme-token-usage/agents/openai.yaml +0 -4
  1192. package/template/.hermes/skills/to-questionnaire/SKILL.md +0 -53
  1193. package/template/.hermes/skills/to-questionnaire/agents/openai.yaml +0 -5
  1194. package/template/.hermes/skills/to-spec/SKILL.md +0 -75
  1195. package/template/.hermes/skills/to-spec/agents/openai.yaml +0 -5
  1196. package/template/.hermes/skills/to-tickets/SKILL.md +0 -105
  1197. package/template/.hermes/skills/to-tickets/agents/openai.yaml +0 -5
  1198. package/template/.hermes/skills/triage/AGENT-BRIEF.md +0 -207
  1199. package/template/.hermes/skills/triage/OUT-OF-SCOPE.md +0 -105
  1200. package/template/.hermes/skills/triage/SKILL.md +0 -112
  1201. package/template/.hermes/skills/triage/agents/openai.yaml +0 -5
  1202. package/template/.hermes/skills/using-git-worktrees/SKILL.md +0 -202
  1203. package/template/.hermes/skills/vue3-best-practices/SKILL.md +0 -68
  1204. package/template/.hermes/skills/wait-what/SKILL.md +0 -7
  1205. package/template/.hermes/skills/wait-what/agents/openai.yaml +0 -5
  1206. package/template/.hermes/skills/wayfinder/SKILL.md +0 -128
  1207. package/template/.hermes/skills/wayfinder/agents/openai.yaml +0 -5
  1208. package/template/.hermes/skills/writing-beats/SKILL.md +0 -67
  1209. package/template/.hermes/skills/writing-beats/agents/openai.yaml +0 -5
  1210. package/template/.hermes/skills/writing-for-agents/SKILL-MECHANICS.md +0 -22
  1211. package/template/.hermes/skills/writing-for-agents/SKILL.md +0 -81
  1212. package/template/.hermes/skills/writing-for-agents/agents/openai.yaml +0 -3
  1213. package/template/.hermes/skills/writing-fragments/SKILL.md +0 -79
  1214. package/template/.hermes/skills/writing-fragments/agents/openai.yaml +0 -5
  1215. package/template/.hermes/skills/writing-shape/SKILL.md +0 -79
  1216. package/template/.hermes/skills/writing-shape/agents/openai.yaml +0 -5
  1217. package/template/.hermes/skills/yedit-table-usage/SKILL.md +0 -227
  1218. package/template/.hermes/skills/yss-antd-design/SKILL.md +0 -34
  1219. package/template/.hermes/skills/yss-antd-design/references/boundaries.md +0 -30
  1220. package/template/.hermes/skills/yss-antd-design/references/cli-matrix.md +0 -38
  1221. package/template/.hermes/skills/yss-antd-design/references/evidence.md +0 -30
  1222. package/template/.hermes/skills/yss-antd-design/scripts/validate-frontmatter.mjs +0 -46
  1223. package/template/.hermes/skills/yss-antd-design/scripts/validate-frontmatter.test.mjs +0 -27
  1224. package/template/.hermes/skills/yss-api-integration/SKILL.md +0 -185
  1225. package/template/.hermes/skills/yss-api-integration/agents/openai.yaml +0 -5
  1226. package/template/.hermes/skills/yss-application/SKILL.md +0 -54
  1227. package/template/.hermes/skills/yss-application/agents/openai.yaml +0 -5
  1228. package/template/.hermes/skills/yss-application/references/application-layer-guide.md +0 -87
  1229. package/template/.hermes/skills/yss-audit-log/SKILL.md +0 -67
  1230. package/template/.hermes/skills/yss-audit-log/agents/openai.yaml +0 -5
  1231. package/template/.hermes/skills/yss-audit-log/assets/AuditLogAspect.java +0 -262
  1232. package/template/.hermes/skills/yss-audit-log/assets/YssAuditLogPrintSubscriberImpl.java +0 -37
  1233. package/template/.hermes/skills/yss-audit-log/assets/YssAuditLogSysManagerSubscriberImpl.java +0 -18
  1234. package/template/.hermes/skills/yss-audit-log/assets/YssAuditPublishService.java +0 -69
  1235. package/template/.hermes/skills/yss-audit-log/references/README.md +0 -69
  1236. package/template/.hermes/skills/yss-audit-log/references/source-index.md +0 -43
  1237. package/template/.hermes/skills/yss-cache/SKILL.md +0 -44
  1238. package/template/.hermes/skills/yss-cache/agents/openai.yaml +0 -5
  1239. package/template/.hermes/skills/yss-cache/references/annotations.md +0 -39
  1240. package/template/.hermes/skills/yss-cache/references/architecture.md +0 -25
  1241. package/template/.hermes/skills/yss-cache/references/configuration.md +0 -34
  1242. package/template/.hermes/skills/yss-cache/references/redis-fallback.md +0 -23
  1243. package/template/.hermes/skills/yss-cache/references/redis-topology.md +0 -62
  1244. package/template/.hermes/skills/yss-cache/references/serialization.md +0 -17
  1245. package/template/.hermes/skills/yss-cache/references/source-index.md +0 -67
  1246. package/template/.hermes/skills/yss-cache/references/troubleshooting.md +0 -33
  1247. package/template/.hermes/skills/yss-cache/references/verification.md +0 -34
  1248. package/template/.hermes/skills/yss-cache/scripts/check-skill-freshness.sh +0 -84
  1249. package/template/.hermes/skills/yss-cache/scripts/inspect-cache-usage.sh +0 -96
  1250. package/template/.hermes/skills/yss-cache/scripts/verify-cache-component.sh +0 -101
  1251. package/template/.hermes/skills/yss-components/SKILL.md +0 -386
  1252. package/template/.hermes/skills/yss-components/agents/openai.yaml +0 -5
  1253. package/template/.hermes/skills/yss-components/references/frontend-docs.md +0 -9
  1254. package/template/.hermes/skills/yss-ddd-scaffold-generator/SKILL.md +0 -110
  1255. package/template/.hermes/skills/yss-ddd-scaffold-generator/agents/openai.yaml +0 -5
  1256. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/application-local.yml.template +0 -8
  1257. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/application.yml.template +0 -36
  1258. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/logback-spring.xml.template +0 -15
  1259. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +0 -36
  1260. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/java/bootstrap-application.java.template +0 -15
  1261. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/adapter-pom.xml.template +0 -19
  1262. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +0 -52
  1263. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +0 -70
  1264. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/domain-pom.xml.template +0 -22
  1265. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/infrastructure-pom.xml.template +0 -54
  1266. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +0 -155
  1267. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +0 -58
  1268. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/jvm.config +0 -1
  1269. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/maven.config +0 -2
  1270. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/settings.xml +0 -41
  1271. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/MavenWrapperDownloader.java +0 -117
  1272. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/maven-wrapper.jar +0 -0
  1273. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/maven-wrapper.properties +0 -20
  1274. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/mvnw +0 -316
  1275. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/mvnw.cmd +0 -188
  1276. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +0 -116
  1277. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +0 -114
  1278. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-adapter/SKILL.md +0 -31
  1279. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-adapter/agents/openai.yaml +0 -5
  1280. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-application/SKILL.md +0 -8
  1281. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-application/agents/openai.yaml +0 -5
  1282. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-domain/SKILL.md +0 -8
  1283. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-domain/agents/openai.yaml +0 -5
  1284. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-infrastructure/SKILL.md +0 -8
  1285. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-infrastructure/agents/openai.yaml +0 -5
  1286. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +0 -57
  1287. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/agents/openai.yaml +0 -5
  1288. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-web/SKILL.md +0 -8
  1289. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-web/agents/openai.yaml +0 -5
  1290. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +0 -191
  1291. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +0 -126
  1292. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +0 -349
  1293. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +0 -112
  1294. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +0 -388
  1295. package/template/.hermes/skills/yss-design-system/SKILL.md +0 -105
  1296. package/template/.hermes/skills/yss-design-system/agents/openai.yaml +0 -5
  1297. package/template/.hermes/skills/yss-design-system/references/design-qa-theme.md +0 -64
  1298. package/template/.hermes/skills/yss-design-system/references/design-system.md +0 -245
  1299. package/template/.hermes/skills/yss-design-system/scripts/validate-frontmatter.mjs +0 -36
  1300. package/template/.hermes/skills/yss-design-system/scripts/validate-frontmatter.test.mjs +0 -12
  1301. package/template/.hermes/skills/yss-distributed-id/SKILL.md +0 -69
  1302. package/template/.hermes/skills/yss-distributed-id/agents/openai.yaml +0 -5
  1303. package/template/.hermes/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
  1304. package/template/.hermes/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
  1305. package/template/.hermes/skills/yss-distributed-id/assets/LeafConf.java +0 -36
  1306. package/template/.hermes/skills/yss-distributed-id/references/README.md +0 -51
  1307. package/template/.hermes/skills/yss-distributed-id/references/source-index.md +0 -86
  1308. package/template/.hermes/skills/yss-domain/SKILL.md +0 -69
  1309. package/template/.hermes/skills/yss-domain/agents/openai.yaml +0 -5
  1310. package/template/.hermes/skills/yss-domain/references/domain-layer-guide.md +0 -99
  1311. package/template/.hermes/skills/yss-dto/SKILL.md +0 -86
  1312. package/template/.hermes/skills/yss-dto/agents/openai.yaml +0 -5
  1313. package/template/.hermes/skills/yss-dto/assets/CommandDTO.java +0 -8
  1314. package/template/.hermes/skills/yss-dto/assets/PageQuery.java +0 -133
  1315. package/template/.hermes/skills/yss-dto/assets/QueryDTO.java +0 -6
  1316. package/template/.hermes/skills/yss-dto/assets/Result.java +0 -105
  1317. package/template/.hermes/skills/yss-dto/references/README.md +0 -67
  1318. package/template/.hermes/skills/yss-dto/references/openapi-wire-profile.yaml +0 -181
  1319. package/template/.hermes/skills/yss-dto/references/source-index.md +0 -55
  1320. package/template/.hermes/skills/yss-excel-mvc/SKILL.md +0 -66
  1321. package/template/.hermes/skills/yss-excel-mvc/agents/openai.yaml +0 -5
  1322. package/template/.hermes/skills/yss-excel-mvc/assets/ExcelDynamicData.java +0 -66
  1323. package/template/.hermes/skills/yss-excel-mvc/assets/RequestExcel.java +0 -33
  1324. package/template/.hermes/skills/yss-excel-mvc/assets/RequestFastExcelArgumentResolver.java +0 -272
  1325. package/template/.hermes/skills/yss-excel-mvc/assets/ResponseExcel.java +0 -46
  1326. package/template/.hermes/skills/yss-excel-mvc/assets/ResponseFastExcelReturnValueHandler.java +0 -154
  1327. package/template/.hermes/skills/yss-excel-mvc/references/README.md +0 -5
  1328. package/template/.hermes/skills/yss-excel-mvc/references/source-index.md +0 -74
  1329. package/template/.hermes/skills/yss-exception/SKILL.md +0 -57
  1330. package/template/.hermes/skills/yss-exception/agents/openai.yaml +0 -4
  1331. package/template/.hermes/skills/yss-exception/references/source-index.md +0 -35
  1332. package/template/.hermes/skills/yss-formily/SKILL.md +0 -272
  1333. package/template/.hermes/skills/yss-formily-schema-generator/SKILL.md +0 -189
  1334. package/template/.hermes/skills/yss-formily-schema-generator/agents/openai.yaml +0 -5
  1335. package/template/.hermes/skills/yss-formily-schema-generator/assets/java-sdk/ExampleUsage.java +0 -111
  1336. package/template/.hermes/skills/yss-formily-schema-generator/assets/java-sdk/YssFormilyDsl.java +0 -1219
  1337. package/template/.hermes/skills/yss-formily-schema-generator/references/workflow.md +0 -240
  1338. package/template/.hermes/skills/yss-frontend-scaffold-generator/SKILL.md +0 -76
  1339. package/template/.hermes/skills/yss-hook/SKILL.md +0 -200
  1340. package/template/.hermes/skills/yss-hook/agents/openai.yaml +0 -5
  1341. package/template/.hermes/skills/yss-hook/references/frontend-docs.md +0 -10
  1342. package/template/.hermes/skills/yss-microapp-commit/SKILL.md +0 -201
  1343. package/template/.hermes/skills/yss-microapp-commit/agents/openai.yaml +0 -5
  1344. package/template/.hermes/skills/yss-microapp-commit/references/scope-mapping.md +0 -204
  1345. package/template/.hermes/skills/yss-mybatis/SKILL.md +0 -77
  1346. package/template/.hermes/skills/yss-mybatis/agents/openai.yaml +0 -5
  1347. package/template/.hermes/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  1348. package/template/.hermes/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  1349. package/template/.hermes/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  1350. package/template/.hermes/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  1351. package/template/.hermes/skills/yss-mybatis/references/README.md +0 -57
  1352. package/template/.hermes/skills/yss-mybatis/references/source-index.md +0 -93
  1353. package/template/.hermes/skills/yss-openapi-draft-review/SKILL.md +0 -82
  1354. package/template/.hermes/skills/yss-openapi-governance/SKILL.md +0 -130
  1355. package/template/.hermes/skills/yss-openapi-governance/agents/openai.yaml +0 -4
  1356. package/template/.hermes/skills/yss-page-module-development/SKILL.md +0 -165
  1357. package/template/.hermes/skills/yss-page-module-development/agents/openai.yaml +0 -5
  1358. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/constant.ts +0 -32
  1359. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -158
  1360. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/index.vue +0 -188
  1361. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
  1362. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/style.less +0 -76
  1363. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/constant.ts +0 -128
  1364. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -144
  1365. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/hooks/useRuleActions.ts +0 -141
  1366. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/index.vue +0 -175
  1367. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/style.less +0 -83
  1368. package/template/.hermes/skills/yss-page-module-development/references/component-map.md +0 -34
  1369. package/template/.hermes/skills/yss-page-module-development/references/frontend-docs.md +0 -11
  1370. package/template/.hermes/skills/yss-page-module-development/references/hooks-map.md +0 -11
  1371. package/template/.hermes/skills/yss-page-module-development/references/utils-map.md +0 -14
  1372. package/template/.hermes/skills/yss-product-lifecycle/SKILL.md +0 -52
  1373. package/template/.hermes/skills/yss-product-lifecycle/agents/openai.yaml +0 -4
  1374. package/template/.hermes/skills/yss-product-lifecycle/references/artifact-dependencies.md +0 -36
  1375. package/template/.hermes/skills/yss-product-lifecycle/references/matt-yss-adapter.md +0 -122
  1376. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration-contract.yaml +0 -863
  1377. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +0 -116
  1378. package/template/.hermes/skills/yss-product-lifecycle/references/state-model.md +0 -112
  1379. package/template/.hermes/skills/yss-prototype-stage/SKILL.md +0 -47
  1380. package/template/.hermes/skills/yss-prototype-stage/references/product-design-adapter.md +0 -54
  1381. package/template/.hermes/skills/yss-prototype-stage/scripts/prototype-contract.mjs +0 -207
  1382. package/template/.hermes/skills/yss-prototype-stage/tests/run-scenarios.mjs +0 -154
  1383. package/template/.hermes/skills/yss-repository/SKILL.md +0 -78
  1384. package/template/.hermes/skills/yss-repository/agents/openai.yaml +0 -5
  1385. package/template/.hermes/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  1386. package/template/.hermes/skills/yss-resilience4j/SKILL.md +0 -46
  1387. package/template/.hermes/skills/yss-resilience4j/agents/openai.yaml +0 -4
  1388. package/template/.hermes/skills/yss-resilience4j/references/source-index.md +0 -33
  1389. package/template/.hermes/skills/yss-router/SKILL.md +0 -51
  1390. package/template/.hermes/skills/yss-router/agents/openai.yaml +0 -4
  1391. package/template/.hermes/skills/yss-router/references/boundaries.md +0 -50
  1392. package/template/.hermes/skills/yss-router/references/router-contract.yaml +0 -345
  1393. package/template/.hermes/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1394. package/template/.hermes/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1395. package/template/.hermes/skills/yss-security-algorithm/SKILL.md +0 -52
  1396. package/template/.hermes/skills/yss-security-algorithm/agents/openai.yaml +0 -4
  1397. package/template/.hermes/skills/yss-security-algorithm/references/source-index.md +0 -35
  1398. package/template/.hermes/skills/yss-source-index/SKILL.md +0 -63
  1399. package/template/.hermes/skills/yss-source-index/agents/openai.yaml +0 -4
  1400. package/template/.hermes/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1401. package/template/.hermes/skills/yss-stage-decision/SKILL.md +0 -50
  1402. package/template/.hermes/skills/yss-stage-decision/references/domain-strategy-contract.md +0 -26
  1403. package/template/.hermes/skills/yss-stage-decision/references/domain-strategy.schema.json +0 -128
  1404. package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package-contract.md +0 -19
  1405. package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package.schema.json +0 -86
  1406. package/template/.hermes/skills/yss-stage-decision/references/validation-rules.md +0 -18
  1407. package/template/.hermes/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +0 -169
  1408. package/template/.hermes/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +0 -100
  1409. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +0 -15
  1410. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +0 -58
  1411. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +0 -44
  1412. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +0 -15
  1413. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +0 -49
  1414. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +0 -108
  1415. package/template/.hermes/skills/yss-stage-decision/tests/run-scenarios.mjs +0 -68
  1416. package/template/.hermes/skills/yss-tactical-design/SKILL.md +0 -35
  1417. package/template/.hermes/skills/yss-tactical-design/agents/openai.yaml +0 -4
  1418. package/template/.hermes/skills/yss-tactical-design/references/output-template.md +0 -28
  1419. package/template/.hermes/skills/yss-tactical-design/references/tactical-design-contract.md +0 -41
  1420. package/template/.hermes/skills/yss-tactical-design/references/tactical-design.schema.json +0 -31
  1421. package/template/.hermes/skills/yss-tactical-design/references/validation-rules.md +0 -13
  1422. package/template/.hermes/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +0 -213
  1423. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +0 -21
  1424. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +0 -21
  1425. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +0 -21
  1426. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +0 -21
  1427. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +0 -21
  1428. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +0 -80
  1429. package/template/.hermes/skills/yss-tactical-design/tests/run-scenarios.mjs +0 -47
  1430. package/template/.hermes/skills/yss-ui/SKILL.md +0 -170
  1431. package/template/.hermes/skills/yss-ui/agents/openai.yaml +0 -5
  1432. package/template/.hermes/skills/yss-ui/assets/demos/button/auth-basic.vue +0 -24
  1433. package/template/.hermes/skills/yss-ui/assets/demos/button/auth-dropdown.vue +0 -29
  1434. package/template/.hermes/skills/yss-ui/assets/demos/button/permission.vue +0 -10
  1435. package/template/.hermes/skills/yss-ui/assets/demos/button/size.vue +0 -12
  1436. package/template/.hermes/skills/yss-ui/assets/demos/button/states.vue +0 -12
  1437. package/template/.hermes/skills/yss-ui/assets/demos/card/basic/index.vue +0 -54
  1438. package/template/.hermes/skills/yss-ui/assets/demos/card/basic/style.less +0 -41
  1439. package/template/.hermes/skills/yss-ui/assets/demos/card/cover/index.vue +0 -25
  1440. package/template/.hermes/skills/yss-ui/assets/demos/card/flexible-layout/index.vue +0 -122
  1441. package/template/.hermes/skills/yss-ui/assets/demos/card/flexible-layout/style.less +0 -191
  1442. package/template/.hermes/skills/yss-ui/assets/demos/card/header-extra/index.vue +0 -260
  1443. package/template/.hermes/skills/yss-ui/assets/demos/card/header-extra/style.less +0 -124
  1444. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/constants.ts +0 -61
  1445. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/index.vue +0 -72
  1446. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/metrics-cards.vue +0 -70
  1447. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/style.less +0 -232
  1448. package/template/.hermes/skills/yss-ui/assets/demos/card/loading/index.vue +0 -19
  1449. package/template/.hermes/skills/yss-ui/assets/demos/card/meta/index.vue +0 -19
  1450. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/constants.ts +0 -44
  1451. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/hooks/useConditionBasic.ts +0 -18
  1452. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/index.vue +0 -100
  1453. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/constants.ts +0 -51
  1454. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/hooks/useConditionLinked.ts +0 -29
  1455. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/index.vue +0 -23
  1456. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/constants.ts +0 -35
  1457. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/hooks/useConditionNesting.ts +0 -25
  1458. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/index.vue +0 -24
  1459. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/constants.ts +0 -7
  1460. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/hooks/useConditionRemote.ts +0 -75
  1461. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/index.vue +0 -23
  1462. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/constants.ts +0 -42
  1463. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/hooks/useConditionBasic.ts +0 -22
  1464. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/index.vue +0 -138
  1465. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/constants.ts +0 -42
  1466. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/hooks/useConditionBasic.ts +0 -22
  1467. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/index.vue +0 -134
  1468. package/template/.hermes/skills/yss-ui/assets/demos/cron/basic/index.vue +0 -54
  1469. package/template/.hermes/skills/yss-ui/assets/demos/cron/select/index.vue +0 -145
  1470. package/template/.hermes/skills/yss-ui/assets/demos/echarts/basic.vue +0 -30
  1471. package/template/.hermes/skills/yss-ui/assets/demos/echarts/calendar-heatmap.vue +0 -49
  1472. package/template/.hermes/skills/yss-ui/assets/demos/echarts/candlestick.vue +0 -30
  1473. package/template/.hermes/skills/yss-ui/assets/demos/echarts/dark.vue +0 -40
  1474. package/template/.hermes/skills/yss-ui/assets/demos/echarts/expose-methods.vue +0 -97
  1475. package/template/.hermes/skills/yss-ui/assets/demos/echarts/funnel.vue +0 -42
  1476. package/template/.hermes/skills/yss-ui/assets/demos/echarts/gauge.vue +0 -25
  1477. package/template/.hermes/skills/yss-ui/assets/demos/echarts/line.vue +0 -24
  1478. package/template/.hermes/skills/yss-ui/assets/demos/echarts/mix.vue +0 -33
  1479. package/template/.hermes/skills/yss-ui/assets/demos/echarts/pie.vue +0 -34
  1480. package/template/.hermes/skills/yss-ui/assets/demos/echarts/radar.vue +0 -38
  1481. package/template/.hermes/skills/yss-ui/assets/demos/echarts/ring-segment.vue +0 -77
  1482. package/template/.hermes/skills/yss-ui/assets/demos/echarts/scatter.vue +0 -23
  1483. package/template/.hermes/skills/yss-ui/assets/demos/echarts/stacked-area.vue +0 -55
  1484. package/template/.hermes/skills/yss-ui/assets/demos/echarts/stacked-bar.vue +0 -52
  1485. package/template/.hermes/skills/yss-ui/assets/demos/echarts/treemap.vue +0 -30
  1486. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/all-types/hooks/useAllTypes.ts +0 -259
  1487. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/all-types/index.vue +0 -33
  1488. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/async-remote/hooks/useAsyncRemote.ts +0 -201
  1489. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/async-remote/index.vue +0 -45
  1490. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/basic/index.vue +0 -104
  1491. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/big-data/index.vue +0 -82
  1492. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/custom-rule/index.vue +0 -114
  1493. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/direct-editable/index.vue +0 -94
  1494. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/drag/index.vue +0 -125
  1495. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/filter/index.vue +0 -107
  1496. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/linkage/index.vue +0 -181
  1497. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/pagination-action/index.vue +0 -131
  1498. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/required/index.vue +0 -95
  1499. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/row-edit/index.vue +0 -220
  1500. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/row-options/index.vue +0 -165
  1501. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/slot-linkage/index.vue +0 -184
  1502. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/submit-view/index.vue +0 -107
  1503. package/template/.hermes/skills/yss-ui/assets/demos/file-import/basic.vue +0 -54
  1504. package/template/.hermes/skills/yss-ui/assets/demos/file-import/formily.vue +0 -42
  1505. package/template/.hermes/skills/yss-ui/assets/demos/file-import/multiple.vue +0 -33
  1506. package/template/.hermes/skills/yss-ui/assets/demos/file-import/result-slot.vue +0 -30
  1507. package/template/.hermes/skills/yss-ui/assets/demos/formily/actions.vue +0 -61
  1508. package/template/.hermes/skills/yss-ui/assets/demos/formily/align.vue +0 -56
  1509. package/template/.hermes/skills/yss-ui/assets/demos/formily/async-validate.vue +0 -69
  1510. package/template/.hermes/skills/yss-ui/assets/demos/formily/basic.vue +0 -317
  1511. package/template/.hermes/skills/yss-ui/assets/demos/formily/collapse.vue +0 -104
  1512. package/template/.hermes/skills/yss-ui/assets/demos/formily/custom-component.vue +0 -75
  1513. package/template/.hermes/skills/yss-ui/assets/demos/formily/disabled.vue +0 -71
  1514. package/template/.hermes/skills/yss-ui/assets/demos/formily/dynamic-array.vue +0 -74
  1515. package/template/.hermes/skills/yss-ui/assets/demos/formily/dynamic.vue +0 -59
  1516. package/template/.hermes/skills/yss-ui/assets/demos/formily/effects.vue +0 -117
  1517. package/template/.hermes/skills/yss-ui/assets/demos/formily/events-inline.vue +0 -82
  1518. package/template/.hermes/skills/yss-ui/assets/demos/formily/events-scope.vue +0 -101
  1519. package/template/.hermes/skills/yss-ui/assets/demos/formily/grid.vue +0 -71
  1520. package/template/.hermes/skills/yss-ui/assets/demos/formily/group.vue +0 -102
  1521. package/template/.hermes/skills/yss-ui/assets/demos/formily/helper-slot.vue +0 -50
  1522. package/template/.hermes/skills/yss-ui/assets/demos/formily/label-tip.vue +0 -42
  1523. package/template/.hermes/skills/yss-ui/assets/demos/formily/linkage-multi.vue +0 -127
  1524. package/template/.hermes/skills/yss-ui/assets/demos/formily/linkage.vue +0 -78
  1525. package/template/.hermes/skills/yss-ui/assets/demos/formily/modes.vue +0 -72
  1526. package/template/.hermes/skills/yss-ui/assets/demos/formily/remote-enum.vue +0 -88
  1527. package/template/.hermes/skills/yss-ui/assets/demos/formily/slot.vue +0 -54
  1528. package/template/.hermes/skills/yss-ui/assets/demos/formily/steps.vue +0 -135
  1529. package/template/.hermes/skills/yss-ui/assets/demos/formily/submit-failed.vue +0 -72
  1530. package/template/.hermes/skills/yss-ui/assets/demos/monaco/basic.vue +0 -40
  1531. package/template/.hermes/skills/yss-ui/assets/demos/monaco/diff-basic.vue +0 -143
  1532. package/template/.hermes/skills/yss-ui/assets/demos/monaco/fullscreen.vue +0 -84
  1533. package/template/.hermes/skills/yss-ui/assets/demos/monaco/history-log-viewer.vue +0 -586
  1534. package/template/.hermes/skills/yss-ui/assets/demos/monaco/log-viewer.vue +0 -260
  1535. package/template/.hermes/skills/yss-ui/assets/demos/monaco/mock/mockLogApi.ts +0 -199
  1536. package/template/.hermes/skills/yss-ui/assets/demos/monaco/monaco.vue +0 -66
  1537. package/template/.hermes/skills/yss-ui/assets/demos/monaco/readonly.vue +0 -20
  1538. package/template/.hermes/skills/yss-ui/assets/demos/monaco/resizable.vue +0 -22
  1539. package/template/.hermes/skills/yss-ui/assets/demos/monaco/sql-schema.vue +0 -20
  1540. package/template/.hermes/skills/yss-ui/assets/demos/monaco/switch-lang-theme.vue +0 -48
  1541. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/constant.ts +0 -18
  1542. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/index.vue +0 -60
  1543. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/style.less +0 -39
  1544. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/hooks/useTreeTable.ts +0 -158
  1545. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/index.vue +0 -88
  1546. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/style.less +0 -73
  1547. package/template/.hermes/skills/yss-ui/assets/demos/table/action/index.vue +0 -83
  1548. package/template/.hermes/skills/yss-ui/assets/demos/table/action/style.less +0 -3
  1549. package/template/.hermes/skills/yss-ui/assets/demos/table/action-auth/index.vue +0 -41
  1550. package/template/.hermes/skills/yss-ui/assets/demos/table/action-slot/index.vue +0 -62
  1551. package/template/.hermes/skills/yss-ui/assets/demos/table/action-slot/style.less +0 -36
  1552. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/hooks/useBasicTable.ts +0 -205
  1553. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/index.vue +0 -89
  1554. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/style.less +0 -28
  1555. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/hooks/useColumnCustomSearch.ts +0 -236
  1556. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/index.vue +0 -141
  1557. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/style.less +0 -48
  1558. package/template/.hermes/skills/yss-ui/assets/demos/table/drag/index.vue +0 -42
  1559. package/template/.hermes/skills/yss-ui/assets/demos/table/expand/action-expand.vue +0 -142
  1560. package/template/.hermes/skills/yss-ui/assets/demos/table/expand/index.vue +0 -35
  1561. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-async/index.vue +0 -93
  1562. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-auto/index.vue +0 -99
  1563. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-custom/index.vue +0 -72
  1564. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-local/index.vue +0 -112
  1565. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-mixed/index.vue +0 -142
  1566. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/constant.ts +0 -129
  1567. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/hooks/useTableData.ts +0 -186
  1568. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/index.vue +0 -73
  1569. package/template/.hermes/skills/yss-ui/assets/demos/table/group-header/index.vue +0 -86
  1570. package/template/.hermes/skills/yss-ui/assets/demos/table/group-header/style.less +0 -3
  1571. package/template/.hermes/skills/yss-ui/assets/demos/table/header-height/index.vue +0 -29
  1572. package/template/.hermes/skills/yss-ui/assets/demos/table/header-height/style.less +0 -9
  1573. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/hooks/useMergeCells.ts +0 -183
  1574. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/index.vue +0 -17
  1575. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/style.less +0 -3
  1576. package/template/.hermes/skills/yss-ui/assets/demos/table/pagination-remote/hooks/usePaginationRemote.ts +0 -106
  1577. package/template/.hermes/skills/yss-ui/assets/demos/table/pagination-remote/index.vue +0 -24
  1578. package/template/.hermes/skills/yss-ui/assets/demos/table/renderers/index.vue +0 -64
  1579. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-checkbox/index.vue +0 -88
  1580. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-checkbox/style.less +0 -9
  1581. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-radio/index.vue +0 -57
  1582. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-radio/style.less +0 -9
  1583. package/template/.hermes/skills/yss-ui/assets/demos/table/transform/index.vue +0 -32
  1584. package/template/.hermes/skills/yss-ui/assets/demos/tree/basic-left/index.vue +0 -124
  1585. package/template/.hermes/skills/yss-ui/assets/demos/tree/datasource-group/index.vue +0 -31
  1586. package/template/.hermes/skills/yss-ui/assets/demos/tree/multi-checkable/index.vue +0 -86
  1587. package/template/.hermes/skills/yss-ui/assets/demos/tree/multiple-select/index.vue +0 -76
  1588. package/template/.hermes/skills/yss-ui/assets/demos/tree/resource-list/index.vue +0 -41
  1589. package/template/.hermes/skills/yss-ui/assets/demos/tree/search-toolbar/index.vue +0 -156
  1590. package/template/.hermes/skills/yss-ui/assets/demos/tree/slot-icon/index.vue +0 -36
  1591. package/template/.hermes/skills/yss-ui/assets/demos/tree/slot-switcher-icon/index.vue +0 -34
  1592. package/template/.hermes/skills/yss-ui/assets/docs/components/button.md +0 -79
  1593. package/template/.hermes/skills/yss-ui/assets/docs/components/card.md +0 -162
  1594. package/template/.hermes/skills/yss-ui/assets/docs/components/conditionBuilder.md +0 -114
  1595. package/template/.hermes/skills/yss-ui/assets/docs/components/cron.md +0 -77
  1596. package/template/.hermes/skills/yss-ui/assets/docs/components/echarts.md +0 -110
  1597. package/template/.hermes/skills/yss-ui/assets/docs/components/editTable.md +0 -212
  1598. package/template/.hermes/skills/yss-ui/assets/docs/components/fileImport.md +0 -85
  1599. package/template/.hermes/skills/yss-ui/assets/docs/components/formily.md +0 -370
  1600. package/template/.hermes/skills/yss-ui/assets/docs/components/monaco.md +0 -369
  1601. package/template/.hermes/skills/yss-ui/assets/docs/components/sheet.md +0 -169
  1602. package/template/.hermes/skills/yss-ui/assets/docs/components/split-pane.md +0 -45
  1603. package/template/.hermes/skills/yss-ui/assets/docs/components/table.md +0 -398
  1604. package/template/.hermes/skills/yss-ui/assets/docs/components/tree.md +0 -102
  1605. package/template/.hermes/skills/yss-ui/assets/docs/guide/gitlab-ci-integration.md +0 -166
  1606. package/template/.hermes/skills/yss-ui/assets/docs/guide/index.md +0 -132
  1607. package/template/.hermes/skills/yss-ui/assets/docs/guide/installation.md +0 -97
  1608. package/template/.hermes/skills/yss-ui/assets/docs/guide/jsp.md +0 -227
  1609. package/template/.hermes/skills/yss-ui/assets/docs/guide/llms.md +0 -295
  1610. package/template/.hermes/skills/yss-ui/assets/docs/guide/monorepo-versioning.md +0 -149
  1611. package/template/.hermes/skills/yss-ui/assets/docs/guide/release-workflow.md +0 -251
  1612. package/template/.hermes/skills/yss-ui/assets/docs/hooks/use-table-height.md +0 -101
  1613. package/template/.hermes/skills/yss-ui/assets/docs/hooks/use-tree-height.md +0 -115
  1614. package/template/.hermes/skills/yss-ui/assets/docs/hooks/useFullscreen.md +0 -130
  1615. package/template/.hermes/skills/yss-ui/assets/docs/hooks/useLoading.md +0 -97
  1616. package/template/.hermes/skills/yss-ui/assets/docs/root/resources.md +0 -18
  1617. package/template/.hermes/skills/yss-ui/assets/keyword-index.json +0 -53
  1618. package/template/.hermes/skills/yss-ui/assets/keyword-index.md +0 -47
  1619. package/template/.hermes/skills/yss-ui/assets/reference-index.json +0 -55
  1620. package/template/.hermes/skills/yss-ui/assets/reference-index.md +0 -74
  1621. package/template/.hermes/skills/yss-ui/assets/scenario-index.json +0 -50
  1622. package/template/.hermes/skills/yss-ui/assets/scenario-index.md +0 -41
  1623. package/template/.hermes/skills/yss-ui/references/accessibility.md +0 -23
  1624. package/template/.hermes/skills/yss-ui/references/antdv-compatibility.md +0 -39
  1625. package/template/.hermes/skills/yss-ui/references/checklist.md +0 -110
  1626. package/template/.hermes/skills/yss-ui/references/component-routing.md +0 -31
  1627. package/template/.hermes/skills/yss-ui/references/frontend-docs.md +0 -11
  1628. package/template/.hermes/skills/yss-ui/references/migration-antdv-to-yss.md +0 -20
  1629. package/template/.hermes/skills/yss-ui/references/quick-recipes.md +0 -280
  1630. package/template/.hermes/skills/yss-ui/references/theme-locale-overlay.md +0 -25
  1631. package/template/.hermes/skills/yss-ui/references/verification.md +0 -20
  1632. package/template/.hermes/skills/yss-ui-business-page-generation/SKILL.md +0 -159
  1633. package/template/.hermes/skills/yss-ui-business-page-generation/references/component-map.md +0 -39
  1634. package/template/.hermes/skills/yss-ui-business-page-generation/references/hooks-map.md +0 -70
  1635. package/template/.hermes/skills/yss-ui-business-page-generation/references/utils-map.md +0 -51
  1636. package/template/.hermes/skills/yss-up-springboot3/SKILL.md +0 -40
  1637. package/template/.hermes/skills/yss-up-springboot3/agents/openai.yaml +0 -4
  1638. package/template/.hermes/skills/yss-use-table-height/SKILL.md +0 -130
  1639. package/template/.hermes/skills/yss-use-table-height/agents/openai.yaml +0 -5
  1640. package/template/.hermes/skills/yss-use-table-height/references/frontend-docs.md +0 -9
  1641. package/template/.hermes/skills/yss-use-tree-height/SKILL.md +0 -98
  1642. package/template/.hermes/skills/yss-use-tree-height/agents/openai.yaml +0 -5
  1643. package/template/.hermes/skills/yss-use-tree-height/references/frontend-docs.md +0 -9
  1644. package/template/.hermes/skills/yss-userinfo/SKILL.md +0 -49
  1645. package/template/.hermes/skills/yss-userinfo/agents/openai.yaml +0 -4
  1646. package/template/.hermes/skills/yss-userinfo/references/source-index.md +0 -34
  1647. package/template/.hermes/skills/yss-validation/SKILL.md +0 -56
  1648. package/template/.hermes/skills/yss-validation/agents/openai.yaml +0 -4
  1649. package/template/.hermes/skills/yss-validation/references/source-index.md +0 -33
  1650. package/template/.hermes/skills/yss-web-controller/SKILL.md +0 -79
  1651. package/template/.hermes/skills/yss-web-controller/agents/openai.yaml +0 -5
  1652. package/template/.hermes/skills/yss-web-controller/assets/templates/Controller.java.template +0 -72
  1653. package/template/.hermes/skills/yss-web-controller/assets/templates/WebConvertor.java.template +0 -33
  1654. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +0 -19
  1655. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +0 -24
  1656. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/Response.java.template +0 -18
  1657. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +0 -19
  1658. package/template/.hermes/skills/yss-web-controller/references/web-adapter-layer-guide.md +0 -84
  1659. package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.mjs +0 -105
  1660. package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.test.mjs +0 -60
  1661. package/template/.hermes/skills/ytable-usage/SKILL.md +0 -251
  1662. package/template/.hermes/skills/ytree-usage/SKILL.md +0 -132
  1663. package/template/.kiro/settings/mcp.json +0 -8
  1664. package/template/.pi/skills/research/SKILL.md +0 -12
  1665. package/template/.pi/skills/research/agents/openai.yaml +0 -3
  1666. package/template/.pi/skills/yss-router/SKILL.md +0 -51
  1667. package/template/.pi/skills/yss-router/agents/openai.yaml +0 -4
  1668. package/template/.pi/skills/yss-router/references/boundaries.md +0 -50
  1669. package/template/.pi/skills/yss-router/references/router-contract.yaml +0 -345
  1670. package/template/.pi/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1671. package/template/.pi/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1672. package/template/.pi/skills/yss-source-index/SKILL.md +0 -63
  1673. package/template/.pi/skills/yss-source-index/agents/openai.yaml +0 -4
  1674. package/template/.pi/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1675. package/template/.qoder/skills/research/SKILL.md +0 -12
  1676. package/template/.qoder/skills/research/agents/openai.yaml +0 -3
  1677. package/template/.qoder/skills/yss-router/SKILL.md +0 -51
  1678. package/template/.qoder/skills/yss-router/agents/openai.yaml +0 -4
  1679. package/template/.qoder/skills/yss-router/references/boundaries.md +0 -50
  1680. package/template/.qoder/skills/yss-router/references/router-contract.yaml +0 -345
  1681. package/template/.qoder/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1682. package/template/.qoder/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1683. package/template/.qoder/skills/yss-source-index/SKILL.md +0 -63
  1684. package/template/.qoder/skills/yss-source-index/agents/openai.yaml +0 -4
  1685. package/template/.qoder/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1686. package/template/.trae/skills/research/SKILL.md +0 -12
  1687. package/template/.trae/skills/research/agents/openai.yaml +0 -3
  1688. package/template/.trae/skills/yss-router/SKILL.md +0 -51
  1689. package/template/.trae/skills/yss-router/agents/openai.yaml +0 -4
  1690. package/template/.trae/skills/yss-router/references/boundaries.md +0 -50
  1691. package/template/.trae/skills/yss-router/references/router-contract.yaml +0 -345
  1692. package/template/.trae/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1693. package/template/.trae/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1694. package/template/.trae/skills/yss-source-index/SKILL.md +0 -63
  1695. package/template/.trae/skills/yss-source-index/agents/openai.yaml +0 -4
  1696. package/template/.trae/skills/yss-source-index/references/source-location.md +0 -18
  1697. package/template/.trae/skills/yss-source-index/references/source-map-config.md +0 -30
  1698. package/template/.trae/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1699. package/template/.trae/skills/yss-source-index/scripts/refresh-yss-skill-index.test.mjs +0 -15
  1700. package/template/scripts/verify-yss-router-scenarios +0 -3
  1701. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1702. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1703. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1704. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1705. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1706. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1707. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1708. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1709. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1710. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1711. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1712. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1713. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1714. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1715. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1716. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1717. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1718. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1719. /package/template/{.qoder/skills/yss-source-index → .trae/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1720. /package/template/{.qoder/skills/yss-source-index → .trae/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1721. /package/template/{.qoder/skills/yss-source-index → .trae/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
@@ -1,4400 +0,0 @@
1
- import { esc, renderDefinitions, renderSemanticSigil, textUnits } from '../shared/utils.mjs';
2
- import { animateAttr, focusEdgeAttrs, focusNodeAttrs, focusNodeTitle, svgAccessibleText, svgRootAttrs } from '../shared/cli.mjs';
3
- import {
4
- throwDiagnosticError,
5
- throwDiagnosticProblems,
6
- withDiagnosticRecordingSuppressed,
7
- } from '../shared/diagnostics.mjs';
8
- import { validateSchema } from '../shared/validator.mjs';
9
- import {
10
- legendFootprint,
11
- measureLegend,
12
- relationshipLegendObstacles,
13
- resolveLegend,
14
- renderLegend as renderResolvedLegend,
15
- } from '../shared/legend.mjs';
16
- import { availableNodeTextWidth, fittedNodeFontSize, minimumNodeTextWidth } from '../shared/text-fit.mjs';
17
- import { brandLabelFitWidth, brandMetadataFor, brandTopRailProblem, renderBrandMark } from '../shared/brand-marks.mjs';
18
- import { translateMessage as i18nText } from '../shared/i18n.mjs';
19
- import {
20
- createMappedWorkflowCandidate,
21
- intrinsicWorkflow,
22
- planningWorkflow,
23
- } from './workflow-migration-geometry.mjs';
24
- import {
25
- asArray,
26
- isFinitePoint,
27
- rectsOverlap,
28
- segmentIntersectsRect,
29
- segmentRectClearance,
30
- cleanEndpointSideProblems,
31
- cleanFlowProblems,
32
- cleanCrossingProblems,
33
- cleanAmbiguousCorridorProblems,
34
- cleanBorderRunProblems,
35
- cleanRouteRhythmProblems,
36
- cleanLabelRouteClearanceProblems,
37
- collectAmbiguousCorridors,
38
- collectLabelRouteClearance,
39
- collectBorderRuns,
40
- forwardCollinearAnalysisSegments,
41
- sourceSegmentIndexAtPoint,
42
- suggestLabelObstacleFix,
43
- suggestLabelPairFix,
44
- anchor,
45
- automaticPortSpread,
46
- defaultFromSide,
47
- defaultToSide,
48
- chosenSide,
49
- normalizeRoutePoints,
50
- routeHonorsEndpointSides,
51
- polylinePath,
52
- routePointsValue,
53
- labelPoint,
54
- componentFill,
55
- componentText,
56
- arrowClassMap,
57
- variantAccent
58
- } from '../shared/geometry.mjs';
59
-
60
- const LEGACY_COLUMN_CENTERS = Object.freeze([88, 220, 300, 430, 500, 625]);
61
- const READABLE_CANDIDATE_COST_PRIORITY = Object.freeze([
62
- 'automaticForwardReversePx',
63
- 'properCrossingCount',
64
- 'sharedCorridorPx',
65
- 'labelRouteClearanceDeficit',
66
- 'interiorPreferred28Deficit',
67
- 'bendCount',
68
- 'stretchMilli',
69
- 'canvasGrowthPx',
70
- 'portDisplacementMilli',
71
- 'legacyCoordinateDisplacement',
72
- 'stableCandidateOrdinal',
73
- ]);
74
- const MAX_READABLE_LAYOUT_FEEDBACK_ROUNDS = 3;
75
- const GROUP_FRAME_TOP_INSET = 8;
76
- const GROUP_FRAME_BOTTOM_INSET = 4;
77
- const GROUP_LABEL_BASELINE_OFFSET = -2;
78
- const GROUP_LABEL_MASK_ASCENT = 10;
79
- const GROUP_LABEL_MASK_H = 14;
80
- const GROUP_NODE_INSET = 4;
81
-
82
- class WorkflowLayoutFeedback extends Error {
83
- constructor(request) {
84
- super(`Workflow layout requires ${request.kind} feedback.`);
85
- this.name = 'WorkflowLayoutFeedback';
86
- this.request = request;
87
- }
88
- }
89
-
90
- function createLegacyLayout() {
91
- return {
92
- contract: 'fixed-v1',
93
- laneX: 40,
94
- laneY: 52,
95
- laneW: 640,
96
- laneH: 104,
97
- laneGap: 20,
98
- laneTitleH: 30,
99
- colXs: [...LEGACY_COLUMN_CENTERS],
100
- nodeW: 92,
101
- nodeH: 52,
102
- defaultViewBoxWidth: 720,
103
- };
104
- }
105
-
106
- function authoredNodeWidth(node) {
107
- return Number.isFinite(node?.width) ? node.width : 92;
108
- }
109
-
110
- function nodeWidthContributor(node) {
111
- return `node ${node.id} width ${authoredNodeWidth(node)}px`;
112
- }
113
-
114
- function authoredNodeHeight(node) {
115
- if (Number.isFinite(node?.height)) return node.height;
116
- return node?.tag ? 68 : 52;
117
- }
118
-
119
- function workflowLabelWidth(label) {
120
- return Math.max(30, textUnits(label) * 4.8 + 10);
121
- }
122
-
123
- function readableGroupBounds(workflow, group, colXs) {
124
- if (!Number.isInteger(group.fromCol) || !Number.isInteger(group.toCol)
125
- || group.fromCol < 0 || group.fromCol > group.toCol || group.toCol >= colXs.length) {
126
- return { x: 0, width: 0, cx: 0 };
127
- }
128
- const start = colXs[group.fromCol] - 50;
129
- const end = colXs[group.toCol] + 50;
130
- const naturalWidth = end - start;
131
- const minimumWidth = textUnits(group.label) * 5.6 + 20;
132
- let width = Math.max(naturalWidth, minimumWidth);
133
- let left = group.fromCol === group.toCol && width > naturalWidth
134
- ? start
135
- : (start + end - width) / 2;
136
- let right = left + width;
137
- for (const node of asArray(workflow.nodes)) {
138
- if (node.lane !== group.lane
139
- || !Number.isInteger(node.col)
140
- || node.col < group.fromCol
141
- || node.col > group.toCol
142
- || node.col < 0
143
- || node.col >= colXs.length) continue;
144
- const halfWidth = authoredNodeWidth(node) / 2;
145
- left = Math.min(left, colXs[node.col] - halfWidth - GROUP_NODE_INSET);
146
- right = Math.max(right, colXs[node.col] + halfWidth + GROUP_NODE_INSET);
147
- }
148
- width = right - left;
149
- return { x: left, width, cx: left + width / 2 };
150
- }
151
-
152
- function verticalIntervalsOverlap(a, b, clearance = 0) {
153
- const aCenter = Number(a?.yOffset) || 0;
154
- const bCenter = Number(b?.yOffset) || 0;
155
- return Math.abs(aCenter - bCenter)
156
- < authoredNodeHeight(a) / 2 + authoredNodeHeight(b) / 2 + clearance;
157
- }
158
-
159
- function createReadableLayout(workflow, layoutFeedback = {}) {
160
- const columnCount = 6;
161
- const baselinePitch = 120;
162
- const columnStart = 94;
163
- const maxLayoutIterations = 3;
164
- const channelDetourBudgetPx = 4 * 28;
165
- const constraints = [];
166
- const feedbackConstraints = [];
167
- const channelLabelEdgeKeys = new Set();
168
- const widthContributors = new Set();
169
- const heightContributors = new Set();
170
- const nodes = asArray(workflow.nodes);
171
- const nodesById = new Map(nodes.map((node) => [node.id, node]));
172
-
173
- for (let col = 0; col < columnCount - 1; col += 1) {
174
- constraints.push({ from: col, to: col + 1, minimum: baselinePitch });
175
- }
176
- for (const [key, minimum] of Object.entries(layoutFeedback.rankGapMinimums || {}).sort()) {
177
- const [from, to] = key.split(':').map(Number);
178
- constraints.push({
179
- from,
180
- to,
181
- minimum,
182
- contributors: layoutFeedback.rankGapContributors?.[key]
183
- || [`rank ${from}→${to} route clearance`],
184
- });
185
- }
186
-
187
- for (let leftIndex = 0; leftIndex < nodes.length; leftIndex += 1) {
188
- for (let rightIndex = leftIndex + 1; rightIndex < nodes.length; rightIndex += 1) {
189
- const leftNode = nodes[leftIndex];
190
- const rightNode = nodes[rightIndex];
191
- if (leftNode.lane !== rightNode.lane || leftNode.col === rightNode.col) continue;
192
- if (!verticalIntervalsOverlap(leftNode, rightNode, 8)) continue;
193
- const fromNode = leftNode.col < rightNode.col ? leftNode : rightNode;
194
- const toNode = fromNode === leftNode ? rightNode : leftNode;
195
- constraints.push({
196
- from: fromNode.col,
197
- to: toNode.col,
198
- minimum: authoredNodeWidth(fromNode) / 2 + 8 + authoredNodeWidth(toNode) / 2,
199
- contributors: [
200
- `rank ${fromNode.col}→${toNode.col} node width clearance`,
201
- nodeWidthContributor(fromNode),
202
- nodeWidthContributor(toNode),
203
- ],
204
- });
205
- }
206
- }
207
-
208
- for (const edge of asArray(workflow.edges)) {
209
- const fromNode = nodesById.get(edge.from);
210
- const toNode = nodesById.get(edge.to);
211
- if (!fromNode || !toNode || fromNode.lane !== toNode.lane || fromNode.col === toNode.col) continue;
212
- if (edge.via || edge.channelX !== undefined || edge.channelY !== undefined
213
- || !['auto', 'straight'].includes(edge.route || 'auto')) continue;
214
- if ((Number(fromNode.yOffset) || 0) !== (Number(toNode.yOffset) || 0)) continue;
215
- const earlier = fromNode.col < toNode.col ? fromNode : toNode;
216
- const later = earlier === fromNode ? toNode : fromNode;
217
- const labeledDirectClearance = edge.label && !edge.labelAt
218
- ? Math.max(28, workflowLabelWidth(edge.label) + 8)
219
- : 28;
220
- const directLabelExpansionCost = Math.max(0, labeledDirectClearance - 28);
221
- const canUseAutomaticLabelChannel = edge.label
222
- && !edge.labelAt
223
- && (edge.route || 'auto') === 'auto'
224
- && !edge.fromSide
225
- && !edge.toSide
226
- && edge.channelX === undefined
227
- && edge.channelY === undefined;
228
- const preferLabelChannel = canUseAutomaticLabelChannel
229
- && directLabelExpansionCost > channelDetourBudgetPx;
230
- if (preferLabelChannel) channelLabelEdgeKeys.add(stableValueKey(edge));
231
- constraints.push({
232
- from: earlier.col,
233
- to: later.col,
234
- minimum: authoredNodeWidth(earlier) / 2 + 28 + authoredNodeWidth(later) / 2,
235
- contributors: [
236
- `rank ${earlier.col}→${later.col} direct clearance`,
237
- `rank ${earlier.col}→${later.col} node width clearance`,
238
- nodeWidthContributor(earlier),
239
- nodeWidthContributor(later),
240
- ],
241
- });
242
- if (!preferLabelChannel && labeledDirectClearance > 28) {
243
- const labelConstraintMinimum = authoredNodeWidth(earlier) / 2
244
- + labeledDirectClearance
245
- + authoredNodeWidth(later) / 2;
246
- feedbackConstraints.push({
247
- from: earlier.col,
248
- to: later.col,
249
- minimum: labelConstraintMinimum,
250
- contributors: [
251
- `rank ${earlier.col}→${later.col} direct clearance`,
252
- `edge ${workflowEdgeName(edge)} label mask`,
253
- nodeWidthContributor(earlier),
254
- nodeWidthContributor(later),
255
- ],
256
- });
257
- }
258
- }
259
-
260
- for (const phase of asArray(workflow.phases)) {
261
- if (!Number.isInteger(phase.fromCol) || !Number.isInteger(phase.toCol)
262
- || phase.fromCol < 0 || phase.fromCol > phase.toCol || phase.toCol >= columnCount) continue;
263
- const minimumWidth = textUnits(phase.label) * 5.6 + 8;
264
- if (phase.fromCol === phase.toCol) {
265
- if (phase.toCol < columnCount - 1) {
266
- constraints.push({
267
- from: phase.toCol,
268
- to: phase.toCol + 1,
269
- minimum: baselinePitch + Math.max(0, minimumWidth - 92),
270
- contributors: [`phase ${phase.id || phase.label} label span`],
271
- });
272
- }
273
- continue;
274
- }
275
- constraints.push({
276
- from: phase.fromCol,
277
- to: phase.toCol,
278
- minimum: Math.max(0, minimumWidth - 92),
279
- contributors: [`phase ${phase.id || phase.label} label span`],
280
- });
281
- }
282
-
283
- for (const group of asArray(workflow.groups)) {
284
- if (!Number.isInteger(group.fromCol) || !Number.isInteger(group.toCol)
285
- || group.fromCol < 0 || group.fromCol > group.toCol || group.toCol >= columnCount) continue;
286
- const minimumWidth = textUnits(group.label) * 5.6 + 20;
287
- if (group.fromCol === group.toCol) {
288
- if (group.toCol < columnCount - 1) {
289
- constraints.push({
290
- from: group.toCol,
291
- to: group.toCol + 1,
292
- minimum: baselinePitch + Math.max(0, minimumWidth - 100),
293
- contributors: [`group ${group.id || group.label} label span`],
294
- });
295
- }
296
- continue;
297
- }
298
- constraints.push({
299
- from: group.fromCol,
300
- to: group.toCol,
301
- minimum: Math.max(0, minimumWidth - 100),
302
- contributors: [`group ${group.id || group.label} label span`],
303
- });
304
- }
305
-
306
- let activeConstraints = [...constraints];
307
- let colXs;
308
- let colProvenance;
309
- for (let iteration = 0; iteration < maxLayoutIterations; iteration += 1) {
310
- colXs = Array.from({ length: columnCount }, (_, col) => columnStart + col * baselinePitch);
311
- colProvenance = Array.from({ length: columnCount }, () => new Set());
312
- const orderedConstraints = activeConstraints
313
- .filter(({ from, to, minimum }) => (
314
- Number.isInteger(from) && Number.isInteger(to)
315
- && from >= 0 && from < to && to < columnCount
316
- && Number.isFinite(minimum)
317
- ))
318
- .sort((a, b) => a.to - b.to || a.from - b.from || a.minimum - b.minimum);
319
- for (let to = 1; to < columnCount; to += 1) {
320
- for (const constraint of orderedConstraints) {
321
- if (constraint.to !== to) continue;
322
- const candidate = colXs[constraint.from] + constraint.minimum;
323
- const candidateProvenance = new Set([
324
- ...colProvenance[constraint.from],
325
- ...asArray(constraint.contributors),
326
- ]);
327
- if (candidate > colXs[to] + 0.0001) {
328
- colXs[to] = candidate;
329
- colProvenance[to] = candidateProvenance;
330
- } else if (Math.abs(candidate - colXs[to]) <= 0.0001
331
- && candidate > columnStart + to * baselinePitch + 0.0001) {
332
- for (const contributor of candidateProvenance) colProvenance[to].add(contributor);
333
- }
334
- }
335
- }
336
- if (iteration > 0 || !feedbackConstraints.length) break;
337
- activeConstraints = [...activeConstraints, ...feedbackConstraints];
338
- }
339
-
340
- const firstRankNodes = nodes.filter((node) => node.col === 0);
341
- const firstExtent = firstRankNodes.reduce(
342
- (maximum, node) => Math.max(maximum, authoredNodeWidth(node) / 2),
343
- 46,
344
- );
345
- const leftInset = 8;
346
- const leftShift = Math.max(0, 40 + leftInset + firstExtent - colXs[0]);
347
- if (leftShift) {
348
- for (let col = 0; col < colXs.length; col += 1) colXs[col] += leftShift;
349
- for (const node of firstRankNodes) {
350
- if (Math.abs(authoredNodeWidth(node) / 2 - firstExtent) > 0.0001) continue;
351
- for (const provenance of colProvenance) provenance.add(nodeWidthContributor(node));
352
- }
353
- }
354
-
355
- const unpinnedTopEndpointIds = new Set();
356
- for (const edge of asArray(workflow.edges)) {
357
- const preservesHorizontalPins = Array.isArray(edge.via) || edge.channelX !== undefined;
358
- if (preservesHorizontalPins) continue;
359
- if (edge.fromSide === 'top') unpinnedTopEndpointIds.add(edge.from);
360
- if (edge.toSide === 'top') unpinnedTopEndpointIds.add(edge.to);
361
- }
362
- const laneOrder = new Map(asArray(workflow.lanes).map((lane, index) => [lane.id, index]));
363
- let laneHeaderShift = 0;
364
- const laneHeaderShiftContributors = new Set();
365
- for (const nodeId of unpinnedTopEndpointIds) {
366
- const node = nodesById.get(nodeId);
367
- if (!node || !Number.isInteger(node.col) || node.col < 0 || node.col >= columnCount) continue;
368
- const lanePosition = laneOrder.get(node.lane);
369
- const lane = asArray(workflow.lanes)[lanePosition];
370
- if (!lane) continue;
371
- const prefix = lane.variant === 'exception'
372
- ? 'EX'
373
- : String(lanePosition + 1).padStart(2, '0');
374
- const laneHeaderRight = 40 + 14 + textUnits(`${prefix} / ${lane.label}`) * 6.2;
375
- const requiredShift = laneHeaderRight + 2 - colXs[node.col];
376
- if (requiredShift > laneHeaderShift + 0.0001) {
377
- laneHeaderShift = requiredShift;
378
- laneHeaderShiftContributors.clear();
379
- laneHeaderShiftContributors.add(`lane ${lane.id} label width`);
380
- } else if (requiredShift > 0 && Math.abs(requiredShift - laneHeaderShift) <= 0.0001) {
381
- laneHeaderShiftContributors.add(`lane ${lane.id} label width`);
382
- }
383
- }
384
- if (laneHeaderShift > 0) {
385
- for (let col = 0; col < colXs.length; col += 1) colXs[col] += laneHeaderShift;
386
- for (const provenance of colProvenance) {
387
- for (const contributor of laneHeaderShiftContributors) provenance.add(contributor);
388
- }
389
- }
390
-
391
- let measuredContentLeftShift = asArray(workflow.edges).reduce((maximum, edge) => {
392
- if (!channelLabelEdgeKeys.has(stableValueKey(edge))) return maximum;
393
- const fromNode = nodesById.get(edge.from);
394
- const toNode = nodesById.get(edge.to);
395
- if (!fromNode || !toNode) return maximum;
396
- const labelCenter = (colXs[fromNode.col] + colXs[toNode.col]) / 2;
397
- const labelLeft = labelCenter - workflowLabelWidth(edge.label) / 2;
398
- return Math.max(maximum, 16 - labelLeft);
399
- }, 0);
400
- for (const phase of asArray(workflow.phases)) {
401
- if (!Number.isInteger(phase.fromCol) || !Number.isInteger(phase.toCol)) continue;
402
- const width = Math.max(
403
- colXs[phase.toCol] - colXs[phase.fromCol] + 92,
404
- textUnits(phase.label) * 5.6 + 8,
405
- );
406
- const left = phase.fromCol === phase.toCol
407
- ? colXs[phase.fromCol] - 46
408
- : (colXs[phase.fromCol] + colXs[phase.toCol] - width) / 2;
409
- measuredContentLeftShift = Math.max(measuredContentLeftShift, 16 - left);
410
- }
411
- for (const group of asArray(workflow.groups)) {
412
- if (!Number.isInteger(group.fromCol) || !Number.isInteger(group.toCol)) continue;
413
- const bounds = readableGroupBounds(workflow, group, colXs);
414
- measuredContentLeftShift = Math.max(measuredContentLeftShift, 44 - bounds.x);
415
- }
416
- if (measuredContentLeftShift > 0) {
417
- for (let col = 0; col < colXs.length; col += 1) colXs[col] += measuredContentLeftShift;
418
- }
419
-
420
- let rightmost = colXs.at(-1) + 50;
421
- let rightmostContributors = new Set(colProvenance.at(-1));
422
- for (const node of nodes) {
423
- if (!Number.isInteger(node.col) || node.col < 0 || node.col >= columnCount) continue;
424
- const nodeRight = colXs[node.col] + authoredNodeWidth(node) / 2;
425
- const nodeContributors = new Set([
426
- ...colProvenance[node.col],
427
- nodeWidthContributor(node),
428
- ]);
429
- if (nodeRight > rightmost + 0.0001) {
430
- rightmost = nodeRight;
431
- rightmostContributors = nodeContributors;
432
- } else if (Math.abs(nodeRight - rightmost) <= 0.0001) {
433
- for (const contributor of nodeContributors) rightmostContributors.add(contributor);
434
- }
435
- }
436
- for (const group of asArray(workflow.groups)) {
437
- if (!Number.isInteger(group.fromCol) || !Number.isInteger(group.toCol)) continue;
438
- const bounds = readableGroupBounds(workflow, group, colXs);
439
- const groupRight = bounds.x + bounds.width;
440
- const groupContributors = new Set([
441
- ...colProvenance[group.fromCol],
442
- ...colProvenance[group.toCol],
443
- `group ${group.id || group.label} label span`,
444
- ...nodes
445
- .filter((node) => node.lane === group.lane
446
- && node.col >= group.fromCol && node.col <= group.toCol)
447
- .map(nodeWidthContributor),
448
- ]);
449
- if (groupRight > rightmost + 0.0001) {
450
- rightmost = groupRight;
451
- rightmostContributors = groupContributors;
452
- } else if (Math.abs(groupRight - rightmost) <= 0.0001) {
453
- for (const contributor of groupContributors) rightmostContributors.add(contributor);
454
- }
455
- }
456
- const widestLaneLabel = asArray(workflow.lanes).reduce((widest, lane, index) => {
457
- const width = textUnits(`${String(index + 1).padStart(2, '0')} / ${lane.label}`) * 6.2 + 30;
458
- return width > widest.width ? { width, lane } : widest;
459
- }, { width: 0, lane: null });
460
- const laneLabelWidth = widestLaneLabel.width;
461
- const rightmostLaneWidth = Math.ceil(rightmost - 40 + 8);
462
- const laneW = Math.max(
463
- 640,
464
- rightmostLaneWidth,
465
- Math.ceil(laneLabelWidth),
466
- );
467
- if (laneW > 640) {
468
- if (rightmostLaneWidth === laneW) {
469
- for (const contributor of rightmostContributors) widthContributors.add(contributor);
470
- }
471
- if (Math.ceil(laneLabelWidth) === laneW && widestLaneLabel.lane) {
472
- widthContributors.add(`lane ${widestLaneLabel.lane.id || widestLaneLabel.lane.label} label width`);
473
- }
474
- }
475
- let maxVerticalExtent = 0;
476
- const verticalExtentContributors = new Set();
477
- for (const node of nodes) {
478
- const yOffset = Number(node.yOffset) || 0;
479
- const extent = authoredNodeHeight(node) / 2 + Math.abs(yOffset);
480
- const contributor = `node ${node.id} height ${authoredNodeHeight(node)}px${yOffset ? ` with yOffset ${yOffset}px` : ''}`;
481
- if (extent > maxVerticalExtent + 0.0001) {
482
- maxVerticalExtent = extent;
483
- verticalExtentContributors.clear();
484
- verticalExtentContributors.add(contributor);
485
- } else if (Math.abs(extent - maxVerticalExtent) <= 0.0001) {
486
- verticalExtentContributors.add(contributor);
487
- }
488
- }
489
- const baseContentH = Math.max(74, Math.ceil(maxVerticalExtent * 2 + 8));
490
- const laneH = 30 + baseContentH;
491
- const groupsByLane = new Map();
492
- for (const group of asArray(workflow.groups)) {
493
- groupsByLane.set(group.lane, [...(groupsByLane.get(group.lane) || []), group]);
494
- }
495
- const groupLaneReserves = asArray(workflow.lanes).map((lane) => {
496
- let header = 0;
497
- let footer = 0;
498
- for (const group of groupsByLane.get(lane.id) || []) {
499
- const bounds = readableGroupBounds(workflow, group, colXs);
500
- const labelLeft = bounds.x + 10;
501
- const labelRight = labelLeft + textUnits(group.label) * 5.6;
502
- for (const node of nodes) {
503
- if (node.lane !== group.lane
504
- || !Number.isInteger(node.col)
505
- || node.col < group.fromCol
506
- || node.col > group.toCol
507
- || node.col < 0
508
- || node.col >= colXs.length) continue;
509
- const halfWidth = authoredNodeWidth(node) / 2;
510
- const nodeLeft = colXs[node.col] - halfWidth;
511
- const nodeRight = colXs[node.col] + halfWidth;
512
- const overlapsLabel = nodeRight > labelLeft && nodeLeft < labelRight;
513
- const topOffset = (baseContentH - authoredNodeHeight(node)) / 2
514
- + (Number(node.yOffset) || 0);
515
- const minimumTopOffset = overlapsLabel ? 11 : 9;
516
- header = Math.max(header, Math.ceil(minimumTopOffset - topOffset));
517
- const bottomMargin = baseContentH - GROUP_FRAME_BOTTOM_INSET
518
- - topOffset - authoredNodeHeight(node);
519
- footer = Math.max(footer, Math.ceil(1 - bottomMargin));
520
- }
521
- }
522
- return { header: Math.max(0, header), footer: Math.max(0, footer) };
523
- });
524
- const groupHeaderHeights = groupLaneReserves.map(({ header }) => header);
525
- const groupFooterHeights = groupLaneReserves.map(({ footer }) => footer);
526
- const laneHeights = groupLaneReserves.map(({ header, footer }) => laneH + header + footer);
527
- const laneGap = Math.max(20, Math.ceil(layoutFeedback.laneGapMin || 0));
528
- for (const [index, reserve] of groupHeaderHeights.entries()) {
529
- if (!reserve) continue;
530
- const lane = asArray(workflow.lanes)[index];
531
- heightContributors.add(`lane ${lane.id || lane.label} group label clearance ${reserve}px`);
532
- }
533
- for (const [index, reserve] of groupFooterHeights.entries()) {
534
- if (!reserve) continue;
535
- const lane = asArray(workflow.lanes)[index];
536
- heightContributors.add(`lane ${lane.id || lane.label} group frame containment ${reserve}px`);
537
- }
538
- if (laneH > 104) {
539
- for (const contributor of verticalExtentContributors) heightContributors.add(contributor);
540
- }
541
- if (laneGap > 20) {
542
- for (const contributor of asArray(layoutFeedback.laneGapContributors)) {
543
- heightContributors.add(contributor);
544
- }
545
- }
546
- const requiredWidth = 40 + laneW + 16;
547
-
548
- return {
549
- contract: 'readable-v2',
550
- laneX: 40,
551
- laneY: 52,
552
- laneW,
553
- laneH,
554
- laneHeights,
555
- laneGap,
556
- laneTitleH: 30,
557
- groupHeaderHeights,
558
- groupFooterHeights,
559
- colXs,
560
- nodeW: 92,
561
- nodeH: 52,
562
- defaultViewBoxWidth: requiredWidth,
563
- channelLabelEdgeKeys,
564
- widthContributors: [...widthContributors].sort(stableCompare),
565
- heightContributors: [...heightContributors].sort(stableCompare),
566
- };
567
- }
568
-
569
- function compilerFailure(contract, diagnostics, error = diagnostics.map(({ message }) => message).join('\n')) {
570
- return {
571
- ok: false,
572
- error,
573
- diagnostics,
574
- receipt: { contract, diagnostics },
575
- };
576
- }
577
-
578
- function workflowEdgeName(edge) {
579
- return edge.id || `${edge.from}->${edge.to}`;
580
- }
581
-
582
- function stableText(value) {
583
- return value == null ? '' : String(value);
584
- }
585
-
586
- function stableCompare(left, right) {
587
- const a = stableText(left);
588
- const b = stableText(right);
589
- return a < b ? -1 : a > b ? 1 : 0;
590
- }
591
-
592
- function stableValueKey(value) {
593
- if (Array.isArray(value)) return `[${value.map(stableValueKey).join(',')}]`;
594
- if (value && typeof value === 'object') {
595
- return `{${Object.keys(value).sort(stableCompare).map((key) => `${JSON.stringify(key)}:${stableValueKey(value[key])}`).join(',')}}`;
596
- }
597
- return JSON.stringify(value);
598
- }
599
-
600
- function cloneWorkflow(value) {
601
- return JSON.parse(JSON.stringify(value));
602
- }
603
-
604
- function canonicalReadableWorkflow(workflow) {
605
- if (workflow.schema_version !== 2) return workflow;
606
- const laneOrder = new Map(asArray(workflow.lanes).map((lane, index) => [lane.id, index]));
607
- const nodes = [...asArray(workflow.nodes)].sort((left, right) => (
608
- (laneOrder.get(left.lane) ?? Number.MAX_SAFE_INTEGER) - (laneOrder.get(right.lane) ?? Number.MAX_SAFE_INTEGER)
609
- || left.col - right.col
610
- || stableCompare(left.id, right.id)
611
- ));
612
- const edges = [...asArray(workflow.edges)].sort((left, right) => (
613
- stableCompare(left.id, right.id)
614
- || stableCompare(left.from, right.from)
615
- || stableCompare(left.to, right.to)
616
- || stableCompare(left.label, right.label)
617
- || stableCompare(left.route, right.route)
618
- || stableCompare(stableValueKey(left), stableValueKey(right))
619
- ));
620
- const phases = workflow.phases === undefined ? undefined : [...asArray(workflow.phases)].sort((left, right) => (
621
- left.fromCol - right.fromCol || left.toCol - right.toCol
622
- || stableCompare(left.id, right.id)
623
- ));
624
- const groups = workflow.groups === undefined ? undefined : [...asArray(workflow.groups)].sort((left, right) => (
625
- (laneOrder.get(left.lane) ?? Number.MAX_SAFE_INTEGER) - (laneOrder.get(right.lane) ?? Number.MAX_SAFE_INTEGER)
626
- || left.fromCol - right.fromCol || left.toCol - right.toCol
627
- || stableCompare(left.id, right.id)
628
- ));
629
- return {
630
- ...workflow,
631
- nodes,
632
- edges,
633
- ...(phases ? { phases } : {}),
634
- ...(groups ? { groups } : {}),
635
- };
636
- }
637
-
638
- function semanticContractDiagnostics(workflow) {
639
- const checks = workflow.semanticChecks;
640
- if (!checks) return [];
641
-
642
- const nodeIds = new Set(asArray(workflow.nodes).map((node) => node.id));
643
- const incoming = new Map([...nodeIds].map((id) => [id, 0]));
644
- const outgoing = new Map([...nodeIds].map((id) => [id, 0]));
645
- const adjacency = new Map([...nodeIds].map((id) => [id, new Set()]));
646
- for (const edge of asArray(workflow.edges)) {
647
- if (!nodeIds.has(edge.from) || !nodeIds.has(edge.to)) continue;
648
- outgoing.set(edge.from, outgoing.get(edge.from) + 1);
649
- incoming.set(edge.to, incoming.get(edge.to) + 1);
650
- adjacency.get(edge.from).add(edge.to);
651
- }
652
-
653
- const diagnostics = [];
654
- const diagnostic = (code, message, subject, evidence, supportedFixes) => ({
655
- code,
656
- severity: 'error',
657
- message,
658
- subject: { diagramType: 'workflow', ...subject },
659
- evidence,
660
- supportedFixes,
661
- });
662
- const referencedNodes = [
663
- ...asArray(checks.allowedRoots).map((id, index) => ({ id, path: `/semanticChecks/allowedRoots/${index}` })),
664
- ...asArray(checks.allowedTerminals).map((id, index) => ({ id, path: `/semanticChecks/allowedTerminals/${index}` })),
665
- ...asArray(checks.requiredEdges).flatMap((relation, index) => [
666
- { id: relation.from, path: `/semanticChecks/requiredEdges/${index}/from` },
667
- { id: relation.to, path: `/semanticChecks/requiredEdges/${index}/to` },
668
- ]),
669
- ...asArray(checks.requiredPaths).flatMap((relation, index) => [
670
- { id: relation.from, path: `/semanticChecks/requiredPaths/${index}/from` },
671
- { id: relation.to, path: `/semanticChecks/requiredPaths/${index}/to` },
672
- ]),
673
- ];
674
- for (const { id, path } of referencedNodes) {
675
- if (nodeIds.has(id)) continue;
676
- diagnostics.push(diagnostic(
677
- 'workflow/semantic-node-reference',
678
- `Workflow semantic contract references unknown node "${id}" at ${path}.`,
679
- { node: id, path },
680
- { knownNodes: [...nodeIds] },
681
- [`replace "${id}" with an existing node id`, 'add the missing node before compiling'],
682
- ));
683
- }
684
- if (diagnostics.length) return diagnostics;
685
-
686
- if (checks.allowedRoots !== undefined) {
687
- const allowed = new Set(checks.allowedRoots);
688
- for (const [node, count] of incoming) {
689
- if (count > 0 || allowed.has(node)) continue;
690
- diagnostics.push(diagnostic(
691
- 'workflow/unexpected-root',
692
- `Workflow node "${node}" has no incoming edge and is not declared in semanticChecks.allowedRoots.`,
693
- { node, path: '/semanticChecks/allowedRoots' },
694
- { incomingEdges: 0, allowedRoots: [...allowed] },
695
- [`add the missing incoming edge to "${node}"`, `declare "${node}" in semanticChecks.allowedRoots if it is an intentional source`],
696
- ));
697
- }
698
- }
699
-
700
- if (checks.allowedTerminals !== undefined) {
701
- const allowed = new Set(checks.allowedTerminals);
702
- for (const [node, count] of outgoing) {
703
- if (count > 0 || allowed.has(node)) continue;
704
- diagnostics.push(diagnostic(
705
- 'workflow/unexpected-terminal',
706
- `Workflow node "${node}" has no outgoing edge and is not declared in semanticChecks.allowedTerminals.`,
707
- { node, path: '/semanticChecks/allowedTerminals' },
708
- { outgoingEdges: 0, allowedTerminals: [...allowed] },
709
- [`add the missing outgoing edge from "${node}"`, `declare "${node}" in semanticChecks.allowedTerminals if it is an intentional sink`],
710
- ));
711
- }
712
- }
713
-
714
- const authoredEdges = new Set(asArray(workflow.edges).map((edge) => `${edge.from}\u0000${edge.to}`));
715
- for (const [index, relation] of asArray(checks.requiredEdges).entries()) {
716
- if (authoredEdges.has(`${relation.from}\u0000${relation.to}`)) continue;
717
- diagnostics.push(diagnostic(
718
- 'workflow/required-edge',
719
- `Workflow semantic contract requires edge "${relation.from}" -> "${relation.to}", but no authored edge matches it.`,
720
- { from: relation.from, to: relation.to, path: `/semanticChecks/requiredEdges/${index}` },
721
- { authoredEdgeCount: asArray(workflow.edges).length },
722
- [`add an edge from "${relation.from}" to "${relation.to}" without deleting the semantic requirement`],
723
- ));
724
- }
725
-
726
- function reachable(from, to) {
727
- const visited = new Set([from]);
728
- const pending = [from];
729
- while (pending.length) {
730
- const current = pending.shift();
731
- if (current === to) return true;
732
- for (const next of adjacency.get(current) || []) {
733
- if (visited.has(next)) continue;
734
- visited.add(next);
735
- pending.push(next);
736
- }
737
- }
738
- return false;
739
- }
740
-
741
- for (const [index, relation] of asArray(checks.requiredPaths).entries()) {
742
- if (reachable(relation.from, relation.to)) continue;
743
- diagnostics.push(diagnostic(
744
- 'workflow/required-path',
745
- `Workflow semantic contract requires a directed path from "${relation.from}" to "${relation.to}", but none exists.`,
746
- { from: relation.from, to: relation.to, path: `/semanticChecks/requiredPaths/${index}` },
747
- { reachableNodes: [...new Set([relation.from, ...(adjacency.get(relation.from) || [])])] },
748
- [`restore a directed path from "${relation.from}" to "${relation.to}" without weakening the semantic requirement`],
749
- ));
750
- }
751
-
752
- return diagnostics;
753
- }
754
-
755
- function compileWorkflowInternal({
756
- workflow: inputWorkflow,
757
- qualityProfile,
758
- discoverFixes = true,
759
- layoutFeedback = {},
760
- } = {}) {
761
- if (!inputWorkflow || typeof inputWorkflow !== 'object' || Array.isArray(inputWorkflow)) {
762
- const diagnostics = [{
763
- code: 'workflow/input-contract',
764
- severity: 'error',
765
- message: 'compileWorkflow requires one parsed workflow document object.',
766
- subject: { diagramType: 'workflow', path: '/' },
767
- evidence: {},
768
- supportedFixes: [],
769
- }];
770
- return compilerFailure('fixed-v1', diagnostics, diagnostics[0].message);
771
- }
772
- const resolvedQualityProfile = qualityProfile || inputWorkflow.meta?.quality_profile;
773
- const authoredQualityProfile = inputWorkflow.meta?.quality_profile;
774
- const qualityResolvedWorkflow = resolvedQualityProfile && resolvedQualityProfile !== inputWorkflow.meta?.quality_profile
775
- ? { ...inputWorkflow, meta: { ...inputWorkflow.meta, quality_profile: resolvedQualityProfile } }
776
- : inputWorkflow;
777
- let inputDiagnostics = [];
778
- try {
779
- validateSchema('workflow', qualityResolvedWorkflow);
780
- } catch (error) {
781
- inputDiagnostics = Array.isArray(error?.archifyDiagnostics)
782
- ? error.archifyDiagnostics.map((diagnostic) => ({
783
- ...diagnostic,
784
- supportedFixes: [],
785
- }))
786
- : [{
787
- code: 'workflow/input-contract',
788
- severity: 'error',
789
- message: 'Workflow schema validation failed unexpectedly.',
790
- subject: { diagramType: 'workflow', path: '/' },
791
- evidence: { reason: error?.message || String(error) },
792
- supportedFixes: [],
793
- }];
794
- }
795
- if (inputDiagnostics.length) {
796
- return compilerFailure(
797
- inputWorkflow.schema_version === 2 ? 'readable-v2' : 'fixed-v1',
798
- inputDiagnostics,
799
- );
800
- }
801
- const workflow = canonicalReadableWorkflow(qualityResolvedWorkflow);
802
- const semanticDiagnostics = semanticContractDiagnostics(workflow);
803
- if (semanticDiagnostics.length) {
804
- return compilerFailure(
805
- workflow.schema_version === 2 ? 'readable-v2' : 'fixed-v1',
806
- semanticDiagnostics,
807
- );
808
- }
809
- const sourceIndexes = {
810
- lanes: new Map(asArray(qualityResolvedWorkflow.lanes).map((lane, index) => [lane, index])),
811
- nodes: new Map(asArray(qualityResolvedWorkflow.nodes).map((node, index) => [node, index])),
812
- edges: new Map(asArray(qualityResolvedWorkflow.edges).map((edge, index) => [edge, index])),
813
- };
814
- const layout = workflow.schema_version === 2
815
- ? createReadableLayout(workflow, layoutFeedback)
816
- : createLegacyLayout();
817
-
818
- const LEGEND_CATALOG = [
819
- 'frontend',
820
- 'backend',
821
- 'security',
822
- 'messagebus',
823
- 'database',
824
- 'cloud',
825
- 'external',
826
- ].map((kind) => ({ kind, label: i18nText(workflow.meta.locale, `legend.workflow.${kind}`) }));
827
- const presentLegendKinds = new Set(asArray(workflow.nodes).map((node) => node.type));
828
- const workflowLegendEntries = resolveLegend(
829
- workflow.meta?.legend,
830
- LEGEND_CATALOG,
831
- presentLegendKinds,
832
- );
833
- const legendFootprintOptions = { fontSize: 7, itemGap: 7 };
834
- const oneRowLegendFootprint = legendFootprint(workflowLegendEntries, {
835
- ...legendFootprintOptions,
836
- width: Number.MAX_SAFE_INTEGER,
837
- });
838
- const minimumCanvasWidth = workflow.schema_version === 2
839
- ? Math.max(layout.defaultViewBoxWidth, oneRowLegendFootprint.minWidth + 40)
840
- : layout.defaultViewBoxWidth;
841
- const legendPackingWidth = Math.max(
842
- 1,
843
- (workflow.schema_version === 2
844
- ? minimumCanvasWidth
845
- : (workflow.meta?.viewBox?.[0] ?? minimumCanvasWidth)) - 40,
846
- );
847
- const packedLegendFootprint = legendFootprint(workflowLegendEntries, {
848
- ...legendFootprintOptions,
849
- width: legendPackingWidth,
850
- });
851
- const legendExtraHeight = workflow.schema_version === 2
852
- ? packedLegendFootprint.extraHeight
853
- : 0;
854
-
855
- // Content is 680px wide (laneX + laneW); auto height fits the lanes plus legend.
856
- const autoHeight = layout.laneY
857
- + (layout.laneHeights?.reduce((total, height) => total + height, 0)
858
- ?? (workflow.lanes?.length || 1) * layout.laneH)
859
- + ((workflow.lanes?.length || 1) - 1) * layout.laneGap
860
- + 124
861
- + legendExtraHeight;
862
- let viewBox = workflow.meta?.viewBox || [minimumCanvasWidth, autoHeight];
863
- let requiredViewBox = [...viewBox];
864
-
865
- const laneIndex = new Map(asArray(workflow.lanes).map((lane, index) => [lane.id, index]));
866
- const laneLabels = new Map(asArray(workflow.lanes).map((lane) => [lane.id, lane.label]));
867
-
868
- function nodeContext(node) {
869
- const group = asArray(workflow.groups).find((candidate) => (
870
- candidate.lane === node.lane && node.col >= candidate.fromCol && node.col <= candidate.toCol
871
- ));
872
- const phase = asArray(workflow.phases).find((candidate) => (
873
- node.col >= candidate.fromCol && node.col <= candidate.toCol
874
- ));
875
- return [laneLabels.get(node.lane), group?.label, phase?.label].filter(Boolean).join(' › ')
876
- || i18nText(workflow.meta.locale, 'node.context.workflow');
877
- }
878
-
879
- function laneHeight(idOrIndex) {
880
- const index = typeof idOrIndex === 'number' ? idOrIndex : laneIndex.get(idOrIndex);
881
- return layout.laneHeights?.[index] ?? layout.laneH;
882
- }
883
-
884
- function laneGroupHeaderH(idOrIndex) {
885
- const index = typeof idOrIndex === 'number' ? idOrIndex : laneIndex.get(idOrIndex);
886
- return layout.groupHeaderHeights?.[index] ?? 0;
887
- }
888
-
889
- function laneGroupFooterH(idOrIndex) {
890
- const index = typeof idOrIndex === 'number' ? idOrIndex : laneIndex.get(idOrIndex);
891
- return layout.groupFooterHeights?.[index] ?? 0;
892
- }
893
-
894
- function laneTop(id) {
895
- const index = laneIndex.get(id);
896
- const precedingHeight = asArray(workflow.lanes).slice(0, index)
897
- .reduce((total, _lane, lanePosition) => total + laneHeight(lanePosition), 0);
898
- return layout.laneY + precedingHeight + index * layout.laneGap;
899
- }
900
-
901
- function lastLaneBottom() {
902
- return layout.laneY
903
- + asArray(workflow.lanes).reduce((total, _lane, index) => total + laneHeight(index), 0)
904
- + (workflow.lanes.length - 1) * layout.laneGap;
905
- }
906
-
907
- function legendY() {
908
- return lastLaneBottom() + 44 + legendExtraHeight;
909
- }
910
-
911
- function workflowLegendLayout(obstacles = []) {
912
- return {
913
- x: 20,
914
- baselineY: legendY(),
915
- width: workflow.schema_version === 2 ? legendPackingWidth : viewBox[0] - 40,
916
- fontSize: 7,
917
- itemGap: 7,
918
- minTitleY: lastLaneBottom() + 8,
919
- obstacles,
920
- unfit: workflow.meta?.legend === undefined ? 'hide' : 'error',
921
- diagramType: 'workflow',
922
- };
923
- }
924
-
925
- function workflowLegendRects() {
926
- if (!workflowLegendEntries.length) return [];
927
- const measured = measureLegend(workflowLegendEntries, workflowLegendLayout());
928
- if (!measured) return [];
929
- return [
930
- { kind: 'title', x: 20, y: measured.titleY - 10, width: 48, height: 14 },
931
- ...measured.entries.map((entry) => ({
932
- kind: entry.kind,
933
- x: entry.x,
934
- y: entry.baseline - 10,
935
- width: entry.width,
936
- height: 14,
937
- })),
938
- ];
939
- }
940
-
941
- function measureNode(node) {
942
- const width = node.width || layout.nodeW;
943
- const height = node.height || (node.tag ? 68 : layout.nodeH);
944
- const cx = layout.colXs[node.col];
945
- const groupHeaderH = laneGroupHeaderH(node.lane);
946
- const contentH = laneHeight(node.lane) - layout.laneTitleH
947
- - groupHeaderH - laneGroupFooterH(node.lane);
948
- const y = laneTop(node.lane) + layout.laneTitleH + groupHeaderH
949
- + (contentH - height) / 2 + (node.yOffset || 0);
950
- return {
951
- ...node,
952
- width,
953
- height,
954
- x: cx - width / 2,
955
- y,
956
- cx,
957
- cy: y + height / 2
958
- };
959
- }
960
-
961
- // Font sizes for this renderer's node text; the fitting geometry is shared.
962
- const nodeTextFit = {
963
- labelPreferred: 11,
964
- labelMinimum: 9,
965
- sublabelPreferred: 8,
966
- sublabelMinimum: 6,
967
- tagPreferred: 7,
968
- tagMinimum: 6,
969
- };
970
-
971
- const nodes = new Map(asArray(workflow.nodes).map((node) => [node.id, measureNode(node)]));
972
-
973
- function workflowCompositionFrames() {
974
- const frames = [];
975
- for (const [index, lane] of asArray(workflow.lanes).entries()) {
976
- const y = laneTop(lane.id);
977
- const height = laneHeight(index);
978
- frames.push({ id: `lane-${index}`, label: lane.label, kind: 'lane', x: layout.laneX, y, width: layout.laneW, height, radius: 10 });
979
- if (lane.variant === 'exception') {
980
- frames.push({ id: `lane-${index}-exception`, label: `${lane.label} exception`, kind: 'exception-lane', x: layout.laneX + 6, y: y + 6, width: layout.laneW - 12, height: height - 12, radius: 8 });
981
- }
982
- }
983
- for (const [index, group] of asArray(workflow.groups).entries()) {
984
- const span = groupSpan(group);
985
- frames.push({
986
- id: `group-${index}`,
987
- label: group.label,
988
- kind: 'group',
989
- x: span.x,
990
- y: laneTop(group.lane) + layout.laneTitleH + GROUP_FRAME_TOP_INSET,
991
- width: span.width,
992
- height: workflow.schema_version === 2
993
- ? laneHeight(group.lane) - layout.laneTitleH
994
- - GROUP_FRAME_TOP_INSET - GROUP_FRAME_BOTTOM_INSET
995
- : layout.laneH - layout.laneTitleH - 16,
996
- radius: 9,
997
- });
998
- }
999
- return frames;
1000
- }
1001
-
1002
- function workflowSceneLabelObstacles() {
1003
- const obstacles = [];
1004
- for (const [index, lane] of asArray(workflow.lanes).entries()) {
1005
- const prefix = lane.variant === 'exception' ? 'EX' : String(index + 1).padStart(2, '0');
1006
- const label = `${prefix} / ${lane.label}`;
1007
- obstacles.push({
1008
- kind: 'lane-header',
1009
- id: lane.id,
1010
- x: layout.laneX + 14,
1011
- y: laneTop(lane.id) + 12,
1012
- width: textUnits(label) * 6.2,
1013
- height: 14,
1014
- });
1015
- }
1016
- for (const phase of asArray(workflow.phases)) {
1017
- if (!Number.isInteger(phase.fromCol) || !Number.isInteger(phase.toCol)
1018
- || phase.fromCol < 0 || phase.toCol >= layout.colXs.length || phase.fromCol > phase.toCol) continue;
1019
- const span = phaseSpan(phase);
1020
- obstacles.push({
1021
- kind: 'phase-header',
1022
- id: phase.id ?? null,
1023
- x: span.x,
1024
- y: 27,
1025
- width: span.width,
1026
- height: 16,
1027
- });
1028
- }
1029
- for (const group of asArray(workflow.groups)) {
1030
- if (!laneIndex.has(group.lane)
1031
- || !Number.isInteger(group.fromCol) || !Number.isInteger(group.toCol)
1032
- || group.fromCol < 0 || group.toCol >= layout.colXs.length || group.fromCol > group.toCol) continue;
1033
- const span = groupSpan(group);
1034
- const frameY = laneTop(group.lane) + layout.laneTitleH + GROUP_FRAME_TOP_INSET;
1035
- const labelBaseline = frameY + GROUP_LABEL_BASELINE_OFFSET;
1036
- obstacles.push({
1037
- kind: 'group-label',
1038
- id: group.id ?? null,
1039
- x: span.x + 10,
1040
- y: labelBaseline - GROUP_LABEL_MASK_ASCENT,
1041
- width: textUnits(group.label) * 5.6,
1042
- height: GROUP_LABEL_MASK_H,
1043
- });
1044
- }
1045
- return obstacles;
1046
- }
1047
-
1048
- const mainPathSteps = new Map(asArray(workflow.mainPath).map((id, index) => [id, index]));
1049
- const edgeSteps = new Map(asArray(workflow.edges).map((edge, index) => {
1050
- const fromStep = mainPathSteps.get(edge.from);
1051
- const toStep = mainPathSteps.get(edge.to);
1052
- const mainStep = Number.isInteger(fromStep) && toStep === fromStep + 1 ? fromStep : null;
1053
- return [edge, mainStep ?? asArray(workflow.mainPath).length + index];
1054
- }));
1055
-
1056
- function nodeStep(node) {
1057
- return mainPathSteps.get(node.id) ?? asArray(workflow.mainPath).length + asArray(workflow.nodes).findIndex((item) => item.id === node.id);
1058
- }
1059
-
1060
- function acceptsFix(mutator) {
1061
- if (!discoverFixes) return false;
1062
- const candidate = cloneWorkflow(workflow);
1063
- mutator(candidate);
1064
- return withDiagnosticRecordingSuppressed(() => compileWorkflowWithFeedback({
1065
- workflow: candidate,
1066
- qualityProfile: resolvedQualityProfile,
1067
- discoverFixes: false,
1068
- }).ok);
1069
- }
1070
-
1071
- function verifiedLegacyAlternative(edge, from, to, requiredClearance) {
1072
- const occupied = [...nodes.values()].filter((node) => node.lane === to.lane && node.id !== to.id);
1073
- const candidates = layout.colXs.map((center, col) => ({ center, col }))
1074
- .filter(({ col }) => col !== to.col)
1075
- .sort((a, b) => Math.abs(a.col - to.col) - Math.abs(b.col - to.col) || a.col - b.col);
1076
- for (const candidate of candidates) {
1077
- const candidateRect = { ...to, col: candidate.col, cx: candidate.center, x: candidate.center - to.width / 2 };
1078
- if (occupied.some((node) => rectsOverlap(candidateRect, node, 8))) continue;
1079
- const centerDistance = Math.abs(candidate.center - from.cx);
1080
- const signedClearance = centerDistance - from.width / 2 - to.width / 2;
1081
- if (signedClearance < requiredClearance) continue;
1082
- if (acceptsFix((document) => {
1083
- document.nodes.find((node) => node.id === to.id).col = candidate.col;
1084
- })) return candidate.col;
1085
- }
1086
- return null;
1087
- }
1088
-
1089
- function readableMigrationProvidesCapacity(from, to, requiredClearance) {
1090
- const readable = createReadableLayout({ ...workflow, schema_version: 2 });
1091
- const centerDistance = Math.abs(readable.colXs[to.col] - readable.colXs[from.col]);
1092
- if (centerDistance - from.width / 2 - to.width / 2 < requiredClearance) return false;
1093
- if (!discoverFixes) return false;
1094
-
1095
- return withDiagnosticRecordingSuppressed(() => {
1096
- const migrationQualityProfile = authoredQualityProfile;
1097
- let planned = compileWorkflowWithFeedback({
1098
- workflow: intrinsicWorkflow(workflow),
1099
- qualityProfile: migrationQualityProfile,
1100
- discoverFixes: false,
1101
- });
1102
- if (!planned.ok) {
1103
- planned = compileWorkflowWithFeedback({
1104
- workflow: planningWorkflow(workflow),
1105
- qualityProfile: migrationQualityProfile,
1106
- discoverFixes: false,
1107
- });
1108
- }
1109
- if (!planned.ok || !Array.isArray(planned.receipt?.columns)) return false;
1110
-
1111
- let candidate;
1112
- try {
1113
- candidate = createMappedWorkflowCandidate(
1114
- workflow,
1115
- LEGACY_COLUMN_CENTERS,
1116
- planned.receipt.columns,
1117
- ).document;
1118
- } catch {
1119
- return false;
1120
- }
1121
- let compiled = compileWorkflowWithFeedback({
1122
- workflow: candidate,
1123
- qualityProfile: migrationQualityProfile,
1124
- discoverFixes: false,
1125
- });
1126
- const requiredViewBox = compiled.diagnostics?.length
1127
- && compiled.diagnostics.every(({ code }) => code === 'workflow/viewbox-capacity')
1128
- ? compiled.diagnostics.find(({ evidence }) => Array.isArray(evidence?.requiredViewBox))
1129
- ?.evidence.requiredViewBox
1130
- : null;
1131
- if (!compiled.ok && Array.isArray(candidate.meta?.viewBox) && requiredViewBox) {
1132
- candidate.meta.viewBox = [
1133
- Math.max(candidate.meta.viewBox[0], requiredViewBox[0]),
1134
- Math.max(candidate.meta.viewBox[1], requiredViewBox[1]),
1135
- ];
1136
- compiled = compileWorkflowWithFeedback({
1137
- workflow: candidate,
1138
- qualityProfile: migrationQualityProfile,
1139
- discoverFixes: false,
1140
- });
1141
- }
1142
- return compiled.ok;
1143
- });
1144
- }
1145
-
1146
- function verifiedReducedWidths(from, to, requiredClearance) {
1147
- const widthBudget = 2 * (Math.abs(to.cx - from.cx) - requiredClearance);
1148
- if (widthBudget < 64) return null;
1149
- const widths = [from.width, to.width];
1150
- let excess = widths[0] + widths[1] - widthBudget;
1151
- for (const index of widths[0] >= widths[1] ? [0, 1] : [1, 0]) {
1152
- const reduction = Math.min(excess, widths[index] - 32);
1153
- widths[index] -= reduction;
1154
- excess -= reduction;
1155
- }
1156
- if (excess > 0.0001) return null;
1157
- const candidates = [from, to];
1158
- const labelsFit = candidates.every((node, index) => (
1159
- textUnits(node.label) * 6.8 <= widths[index] + 6
1160
- && (!node.sublabel || minimumNodeTextWidth(node.sublabel, nodeTextFit.sublabelMinimum) <= availableNodeTextWidth(widths[index]))
1161
- && (!node.tag || minimumNodeTextWidth(node.tag, nodeTextFit.tagMinimum) <= availableNodeTextWidth(widths[index]))
1162
- ));
1163
- if (!labelsFit) return null;
1164
- const serializedWidths = widths.map((width) => Math.floor((width + 1e-9) * 100) / 100);
1165
- const signedClearance = Math.abs(to.cx - from.cx)
1166
- - serializedWidths[0] / 2 - serializedWidths[1] / 2;
1167
- if (signedClearance + 0.0001 < requiredClearance) return null;
1168
- const accepted = acceptsFix((document) => {
1169
- document.nodes.find((node) => node.id === from.id).width = serializedWidths[0];
1170
- document.nodes.find((node) => node.id === to.id).width = serializedWidths[1];
1171
- });
1172
- return accepted ? serializedWidths : null;
1173
- }
1174
-
1175
- function enforceLegacyColumnCapacity() {
1176
- if (workflow.schema_version !== 1) return;
1177
- for (const edge of workflow.edges) {
1178
- const from = nodes.get(edge.from);
1179
- const to = nodes.get(edge.to);
1180
- if (!from || !to || from.lane !== to.lane || from.col === to.col) continue;
1181
- if (!verticalIntervalsOverlap(from, to, 8)) continue;
1182
- const centerDistance = Math.abs(to.cx - from.cx);
1183
- const actualSignedClearance = centerDistance - from.width / 2 - to.width / 2;
1184
- const direct = !edge.via && ['auto', 'straight'].includes(edge.route || 'auto')
1185
- && Math.abs(from.cy - to.cy) < 0.0001;
1186
- const requiredDirectClearance = direct ? 28 : 8;
1187
- if (actualSignedClearance >= requiredDirectClearance) continue;
1188
- const alternative = verifiedLegacyAlternative(edge, from, to, requiredDirectClearance);
1189
- const reducedWidths = verifiedReducedWidths(from, to, requiredDirectClearance);
1190
- const capacity = actualSignedClearance < 0
1191
- ? `overlap by ${Math.abs(Math.round(actualSignedClearance))}px`
1192
- : `leave only ${Math.round(actualSignedClearance)}px of direct clearance`;
1193
- const message = `Workflow columns ${from.col}→${to.col} place nodes "${from.id}" and "${to.id}" so they ${capacity} under the fixed-v1 layout.`;
1194
- const supportedFixes = [];
1195
- if (readableMigrationProvidesCapacity(from, to, requiredDirectClearance)) {
1196
- supportedFixes.push('migrate this workflow to schema_version 2');
1197
- }
1198
- if (alternative !== null) supportedFixes.push(`move node "${to.id}" to verified free column ${alternative}`);
1199
- if (reducedWidths) {
1200
- supportedFixes.push(`set node widths "${from.id}"=${Math.round(reducedWidths[0] * 100) / 100}px and "${to.id}"=${Math.round(reducedWidths[1] * 100) / 100}px`);
1201
- }
1202
- throwDiagnosticError(message, [{
1203
- code: 'workflow/column-capacity',
1204
- severity: 'error',
1205
- message,
1206
- subject: {
1207
- diagramType: 'workflow',
1208
- edge: edge.id ?? null,
1209
- from: edge.from,
1210
- to: edge.to,
1211
- fromCol: from.col,
1212
- toCol: to.col,
1213
- },
1214
- evidence: {
1215
- centerDistancePx: centerDistance,
1216
- nodeWidthsPx: [from.width, to.width],
1217
- actualSignedClearancePx: actualSignedClearance,
1218
- requiredDirectClearancePx: requiredDirectClearance,
1219
- },
1220
- supportedFixes,
1221
- suppresses: [
1222
- 'workflow/short-edge',
1223
- 'clean-flow/endpoint-side-direction',
1224
- 'workflow/label-node-overlap',
1225
- ],
1226
- }]);
1227
- }
1228
- }
1229
-
1230
- function verifiedEdgeFix(edge, message, mutator) {
1231
- const edgeIndex = workflow.edges.indexOf(edge);
1232
- if (edgeIndex < 0) return null;
1233
- const accepted = acceptsFix((document) => mutator(document.edges[edgeIndex], document));
1234
- return accepted ? message : null;
1235
- }
1236
-
1237
- function verifiedAutomaticRouteFix(edge, { clearSides = false } = {}) {
1238
- const edgeName = workflowEdgeName(edge);
1239
- return verifiedEdgeFix(
1240
- edge,
1241
- clearSides
1242
- ? `remove explicit route geometry and endpoint sides from edge "${edgeName}" so readable-v2 can use its verified automatic candidate`
1243
- : `remove explicit route geometry from edge "${edgeName}" so readable-v2 can use its verified automatic candidate`,
1244
- (candidate) => {
1245
- delete candidate.via;
1246
- delete candidate.channelX;
1247
- delete candidate.channelY;
1248
- delete candidate.route;
1249
- if (clearSides) {
1250
- delete candidate.fromSide;
1251
- delete candidate.toSide;
1252
- }
1253
- },
1254
- );
1255
- }
1256
-
1257
- function authoredPinEvidence(edge, field) {
1258
- const authoredEdgeIndex = sourceIndexes.edges.get(edge);
1259
- const value = Array.isArray(edge[field])
1260
- ? edge[field].map((item) => (Array.isArray(item) ? [...item] : item))
1261
- : edge[field];
1262
- return {
1263
- edge: workflowEdgeName(edge),
1264
- field,
1265
- ...(Number.isInteger(authoredEdgeIndex) ? { path: `/edges/${authoredEdgeIndex}/${field}` } : {}),
1266
- value,
1267
- };
1268
- }
1269
-
1270
- function combinations(values, size, start = 0, prefix = [], output = []) {
1271
- if (prefix.length === size) {
1272
- output.push([...prefix]);
1273
- return output;
1274
- }
1275
- for (let index = start; index <= values.length - (size - prefix.length); index += 1) {
1276
- prefix.push(values[index]);
1277
- combinations(values, size, index + 1, prefix, output);
1278
- prefix.pop();
1279
- }
1280
- return output;
1281
- }
1282
-
1283
- function verifiedPinRemovalAlternatives(edge, fields, reason) {
1284
- if (!discoverFixes) return { removalSets: [], supportedFixes: [] };
1285
- const edgeIndex = workflow.edges.indexOf(edge);
1286
- if (edgeIndex < 0) return { removalSets: [], supportedFixes: [] };
1287
- const uniqueFields = [...new Set(fields.filter((field) => edge[field] !== undefined))];
1288
- for (let size = 1; size <= uniqueFields.length; size += 1) {
1289
- const removalSets = combinations(uniqueFields, size).filter((fieldSet) => (
1290
- acceptsFix((document) => {
1291
- for (const field of fieldSet) delete document.edges[edgeIndex][field];
1292
- })
1293
- ));
1294
- if (!removalSets.length) continue;
1295
- const edgeName = workflowEdgeName(edge);
1296
- return {
1297
- removalSets,
1298
- supportedFixes: removalSets.map((fieldSet) => (
1299
- `remove ${fieldSet.join(' and ')} from edge "${edgeName}" ${reason}`
1300
- )),
1301
- };
1302
- }
1303
- return { removalSets: [], supportedFixes: [] };
1304
- }
1305
-
1306
- function conflictPinsFromRemovalSets(edge, removalSets, fallbackFields = []) {
1307
- const fields = removalSets.length
1308
- ? [...new Set(removalSets.flat())]
1309
- : [...new Set(fallbackFields)];
1310
- return fields.map((field) => authoredPinEvidence(edge, field));
1311
- }
1312
-
1313
- function authoredRouteAssertionFields(edge) {
1314
- return [
1315
- ...(Array.isArray(edge?.via) ? ['via'] : []),
1316
- ...(edge?.channelX !== undefined ? ['channelX'] : []),
1317
- ...(edge?.channelY !== undefined ? ['channelY'] : []),
1318
- ...(edge?.route && edge.route !== 'auto' ? ['route'] : []),
1319
- ...(edge?.fromSide && edge.fromSide !== 'auto' ? ['fromSide'] : []),
1320
- ...(edge?.toSide && edge.toSide !== 'auto' ? ['toSide'] : []),
1321
- ];
1322
- }
1323
-
1324
- function hasAuthoredRouteAssertions(edge) {
1325
- return authoredRouteAssertionFields(edge).length > 0;
1326
- }
1327
-
1328
- function verifiedPinReferenceAlternatives(candidateRefs, reason) {
1329
- const seenRefs = new Set();
1330
- const refs = candidateRefs.filter(({ edge, edgeIndex, field }) => {
1331
- if (edgeIndex < 0 || edge?.[field] === undefined) return false;
1332
- const key = `${edgeIndex}:${field}`;
1333
- if (seenRefs.has(key)) return false;
1334
- seenRefs.add(key);
1335
- return true;
1336
- });
1337
- const fallbackPins = refs.map(({ edge, field }) => authoredPinEvidence(edge, field));
1338
- if (!discoverFixes) {
1339
- return {
1340
- removalSets: [], conflictingRefs: refs, conflictingPins: fallbackPins, repairs: [], supportedFixes: [],
1341
- };
1342
- }
1343
-
1344
- for (let size = 1; size <= refs.length; size += 1) {
1345
- const removalSets = combinations(refs, size).filter((removalSet) => (
1346
- acceptsFix((document) => {
1347
- for (const { edgeIndex, field } of removalSet) delete document.edges[edgeIndex][field];
1348
- })
1349
- ));
1350
- if (!removalSets.length) continue;
1351
- const conflictingRefs = [];
1352
- const conflictingPins = [];
1353
- const seenPins = new Set();
1354
- for (const removalSet of removalSets) {
1355
- for (const { edge, field } of removalSet) {
1356
- const key = `${workflow.edges.indexOf(edge)}:${field}`;
1357
- if (seenPins.has(key)) continue;
1358
- seenPins.add(key);
1359
- conflictingRefs.push({ edge, edgeIndex: workflow.edges.indexOf(edge), field });
1360
- conflictingPins.push(authoredPinEvidence(edge, field));
1361
- }
1362
- }
1363
- const repairs = removalSets.map((removalSet) => {
1364
- const grouped = [];
1365
- for (const ref of removalSet) {
1366
- let group = grouped.find(({ edge }) => edge === ref.edge);
1367
- if (!group) {
1368
- group = { edge: ref.edge, fields: [] };
1369
- grouped.push(group);
1370
- }
1371
- group.fields.push(ref.field);
1372
- }
1373
- const removals = grouped.map(({ edge, fields }) => (
1374
- `remove ${fields.join(' and ')} from edge "${workflowEdgeName(edge)}"`
1375
- ));
1376
- return { removalSet, message: `${removals.join(' and ')} ${reason}` };
1377
- });
1378
- return {
1379
- removalSets,
1380
- conflictingRefs,
1381
- conflictingPins,
1382
- repairs,
1383
- supportedFixes: repairs.map(({ message }) => message),
1384
- };
1385
- }
1386
- return {
1387
- removalSets: [], conflictingRefs: refs, conflictingPins: fallbackPins, repairs: [], supportedFixes: [],
1388
- };
1389
- }
1390
-
1391
- function verifiedRoutePairPinAlternatives(leftEdge, rightEdge, reason) {
1392
- const refs = [leftEdge, rightEdge].flatMap((edge) => {
1393
- const edgeIndex = workflow.edges.indexOf(edge);
1394
- return authoredRouteAssertionFields(edge).map((field) => ({ edge, edgeIndex, field }));
1395
- });
1396
- return verifiedPinReferenceAlternatives(refs, reason);
1397
- }
1398
-
1399
- function verifiedLabelRoutePinAlternatives(labelEdge, routeEdge) {
1400
- const refs = [];
1401
- const labelEdgeIndex = workflow.edges.indexOf(labelEdge);
1402
- if (Array.isArray(labelEdge?.labelAt)) {
1403
- refs.push({ edge: labelEdge, edgeIndex: labelEdgeIndex, field: 'labelAt' });
1404
- }
1405
- const routeEdgeIndex = workflow.edges.indexOf(routeEdge);
1406
- for (const field of authoredRouteAssertionFields(routeEdge)) {
1407
- refs.push({ edge: routeEdge, edgeIndex: routeEdgeIndex, field });
1408
- }
1409
- return verifiedPinReferenceAlternatives(
1410
- refs,
1411
- Array.isArray(labelEdge?.labelAt)
1412
- ? 'so readable-v2 can replan the remaining authored label-route pins'
1413
- : 'so readable-v2 can replan the remaining authored route assertions',
1414
- );
1415
- }
1416
-
1417
- function verifiedLabelPairPinAlternatives(leftEdge, rightEdge) {
1418
- return verifiedPinReferenceAlternatives(
1419
- [leftEdge, rightEdge].flatMap((edge) => (
1420
- Array.isArray(edge?.labelAt)
1421
- ? [{ edge, edgeIndex: workflow.edges.indexOf(edge), field: 'labelAt' }]
1422
- : []
1423
- )),
1424
- 'so readable-v2 can replan the remaining authored label pins',
1425
- );
1426
- }
1427
-
1428
- function verifiedRepairsWithLabelNudges(alternatives) {
1429
- return alternatives.repairs.flatMap(({ removalSet, message }) => {
1430
- if (removalSet.length !== 1 || removalSet[0].field !== 'labelAt') return [message];
1431
- const nudges = verifiedLabelAtAlternatives(removalSet[0].edge);
1432
- return nudges.length ? nudges : [message];
1433
- });
1434
- }
1435
-
1436
- function throwExplicitPinConflict(edge, invariant, evidence, supportedFixes = []) {
1437
- const message = `Workflow edge "${workflowEdgeName(edge)}" has explicit geometry that violates ${invariant}.`;
1438
- const [onlyPin] = asArray(evidence?.conflictingPins);
1439
- const authoredEdgeIndex = sourceIndexes.edges.get(edge);
1440
- const pinPath = asArray(evidence?.conflictingPins).length === 1
1441
- && Number.isInteger(authoredEdgeIndex)
1442
- && onlyPin?.field
1443
- ? onlyPin.path || `/edges/${authoredEdgeIndex}/${onlyPin.field}`
1444
- : null;
1445
- throwDiagnosticError(message, [{
1446
- code: 'workflow/explicit-pin-conflict',
1447
- severity: 'error',
1448
- message,
1449
- subject: {
1450
- diagramType: 'workflow',
1451
- edge: edge.id ?? null,
1452
- from: edge.from,
1453
- to: edge.to,
1454
- ...(pinPath ? { path: pinPath } : {}),
1455
- },
1456
- evidence: { invariant, ...evidence },
1457
- supportedFixes: supportedFixes.filter(Boolean),
1458
- }]);
1459
- }
1460
-
1461
- function hasAbsoluteRoutePins(edge) {
1462
- return Array.isArray(edge?.via)
1463
- || edge?.channelX !== undefined
1464
- || edge?.channelY !== undefined;
1465
- }
1466
-
1467
- function presentRouteGeometryFields(edge) {
1468
- return [
1469
- ...(Array.isArray(edge?.via) ? ['via'] : []),
1470
- ...(edge?.channelX !== undefined ? ['channelX'] : []),
1471
- ...(edge?.channelY !== undefined ? ['channelY'] : []),
1472
- ];
1473
- }
1474
-
1475
- function verifiedRouteGeometryPinAlternatives(
1476
- edge,
1477
- reason = 'so readable-v2 can replan the remaining explicit route assertions',
1478
- ) {
1479
- const edgeIndex = workflow.edges.indexOf(edge);
1480
- return verifiedPinReferenceAlternatives(
1481
- authoredRouteAssertionFields(edge).map((field) => ({ edge, edgeIndex, field })),
1482
- reason,
1483
- );
1484
- }
1485
-
1486
- function properOrthogonalIntersection(leftStart, leftEnd, rightStart, rightEnd) {
1487
- const leftOrientation = segmentOrientation(leftStart, leftEnd);
1488
- const rightOrientation = segmentOrientation(rightStart, rightEnd);
1489
- if (leftOrientation === rightOrientation
1490
- || leftOrientation === 'diagonal'
1491
- || rightOrientation === 'diagonal') return null;
1492
- const horizontalStart = leftOrientation === 'horizontal' ? leftStart : rightStart;
1493
- const horizontalEnd = leftOrientation === 'horizontal' ? leftEnd : rightEnd;
1494
- const verticalStart = leftOrientation === 'vertical' ? leftStart : rightStart;
1495
- const verticalEnd = leftOrientation === 'vertical' ? leftEnd : rightEnd;
1496
- const point = [verticalStart[0], horizontalStart[1]];
1497
- const epsilon = 0.0001;
1498
- const insideHorizontal = point[0] > Math.min(horizontalStart[0], horizontalEnd[0]) + epsilon
1499
- && point[0] < Math.max(horizontalStart[0], horizontalEnd[0]) - epsilon;
1500
- const insideVertical = point[1] > Math.min(verticalStart[1], verticalEnd[1]) + epsilon
1501
- && point[1] < Math.max(verticalStart[1], verticalEnd[1]) - epsilon;
1502
- return insideHorizontal && insideVertical ? point : null;
1503
- }
1504
-
1505
- function verifiedLabelAtAlternatives(edge) {
1506
- if (!Array.isArray(edge.labelAt)) return [];
1507
- const [x, y] = edge.labelAt;
1508
- return [
1509
- [0, 24], [0, -24], [24, 0], [-24, 0],
1510
- [0, 48], [0, -48], [48, 0], [-48, 0],
1511
- ].map(([dx, dy]) => {
1512
- const next = [x + dx, y + dy];
1513
- return verifiedEdgeFix(
1514
- edge,
1515
- `set labelAt on edge "${workflowEdgeName(edge)}" to [${next[0]}, ${next[1]}]`,
1516
- (candidate) => { candidate.labelAt = next; },
1517
- );
1518
- }).filter(Boolean);
1519
- }
1520
-
1521
- function verifiedLabelAtNudge(edge) {
1522
- const [alternative] = verifiedLabelAtAlternatives(edge);
1523
- if (alternative) return alternative;
1524
- return verifiedEdgeFix(
1525
- edge,
1526
- `remove labelAt from edge "${workflowEdgeName(edge)}" so readable-v2 can use verified automatic label placement`,
1527
- (candidate) => { delete candidate.labelAt; },
1528
- );
1529
- }
1530
-
1531
- function throwReadableLabelRoutePinConflict(hit, routePoints = null) {
1532
- const labelEdge = hit.labelRelation;
1533
- const routeEdge = hit.otherRelation;
1534
- const labelPinned = Array.isArray(labelEdge?.labelAt);
1535
- const routePinned = hasAuthoredRouteAssertions(routeEdge);
1536
- if (!labelPinned && !routePinned) return false;
1537
- const alternatives = verifiedLabelRoutePinAlternatives(labelEdge, routeEdge);
1538
- const actualRoutePoints = routePoints
1539
- || pathCache.get(routeEdge)?.points
1540
- || pathFor(routeEdge).points;
1541
- const diagnosticEdge = alternatives.conflictingRefs[0]?.edge
1542
- || (labelPinned ? labelEdge : routeEdge);
1543
- throwExplicitPinConflict(diagnosticEdge, 'explicit label-route clearance', {
1544
- conflictingPins: alternatives.conflictingPins,
1545
- ...(labelPinned ? { labelAt: [...labelEdge.labelAt] } : {}),
1546
- labelRect: {
1547
- x: hit.rect.x,
1548
- y: hit.rect.y,
1549
- width: hit.rect.width,
1550
- height: hit.rect.height,
1551
- },
1552
- collidedRoute: {
1553
- edge: routeEdge.id || `${routeEdge.from}->${routeEdge.to}`,
1554
- from: routeEdge.from,
1555
- to: routeEdge.to,
1556
- points: actualRoutePoints.map((point) => [...point]),
1557
- },
1558
- routeSegmentIndex: hit.segmentIndex,
1559
- routeSegment: { from: [...hit.start], to: [...hit.end] },
1560
- clearancePx: Math.round(hit.clearance * 10) / 10,
1561
- minimumPx: hit.threshold,
1562
- }, [
1563
- ...verifiedRepairsWithLabelNudges(alternatives),
1564
- ]);
1565
- return true;
1566
- }
1567
-
1568
- function throwReadableLabelLabelPinConflict(left, right) {
1569
- const leftEdge = left.relation;
1570
- const rightEdge = right.relation;
1571
- const pinnedEdges = [leftEdge, rightEdge].filter((edge) => Array.isArray(edge?.labelAt));
1572
- if (!pinnedEdges.length) return false;
1573
- const alternatives = verifiedLabelPairPinAlternatives(leftEdge, rightEdge);
1574
- const causalLabelEdges = [...new Set(alternatives.conflictingRefs.map(({ edge }) => edge))];
1575
- const diagnosticEdge = causalLabelEdges[0] || pinnedEdges[0];
1576
- throwExplicitPinConflict(diagnosticEdge, 'explicit label-label clearance', {
1577
- conflictingPins: alternatives.conflictingPins,
1578
- labelRects: [left, right].map((rect) => ({
1579
- edge: rect.relation.id ?? null,
1580
- x: rect.x,
1581
- y: rect.y,
1582
- width: rect.width,
1583
- height: rect.height,
1584
- })),
1585
- minimumGapPx: -2,
1586
- }, verifiedRepairsWithLabelNudges(alternatives));
1587
- return true;
1588
- }
1589
-
1590
- function classifyFailedAutomaticCandidatePins(edge, rawCandidates) {
1591
- const relationIndex = workflow.edges.indexOf(edge);
1592
- const priorRoutes = [...pathCache.entries()]
1593
- .filter(([otherEdge]) => otherEdge !== edge)
1594
- .map(([relation, routed]) => ({
1595
- relation,
1596
- relationIndex: workflow.edges.indexOf(relation),
1597
- points: routed.points,
1598
- }));
1599
- if (!priorRoutes.length) return;
1600
- const priorLabels = priorRoutes.map(({ relation, relationIndex }) => (
1601
- labelRectFor(relation, relationIndex)
1602
- )).filter(Boolean);
1603
-
1604
- for (const { points } of rawCandidates) {
1605
- const candidateRect = candidateLabelRect(edge, points);
1606
- const candidateLabel = candidateRect
1607
- ? { ...candidateRect, relation: edge, relationIndex, label: edge.label }
1608
- : null;
1609
- if (candidateLabel) {
1610
- const priorLabel = priorLabels.find((otherLabel) => (
1611
- rectsOverlap(candidateLabel, otherLabel, -2)
1612
- && (Array.isArray(edge.labelAt) || Array.isArray(otherLabel.relation?.labelAt))
1613
- ));
1614
- if (priorLabel) throwReadableLabelLabelPinConflict(candidateLabel, priorLabel);
1615
-
1616
- const labelRouteHit = collectLabelRouteClearance({
1617
- labels: [candidateLabel],
1618
- routedRelations: priorRoutes,
1619
- threshold: 4,
1620
- }).find((hit) => (
1621
- Array.isArray(edge.labelAt) || hasAbsoluteRoutePins(hit.otherRelation)
1622
- ));
1623
- if (labelRouteHit) {
1624
- const collidedRoute = priorRoutes.find(({ relation }) => relation === labelRouteHit.otherRelation);
1625
- throwReadableLabelRoutePinConflict(labelRouteHit, collidedRoute?.points);
1626
- }
1627
- }
1628
-
1629
- const reverseHit = collectLabelRouteClearance({
1630
- labels: priorLabels,
1631
- routedRelations: [{ relation: edge, relationIndex, points }],
1632
- threshold: 4,
1633
- }).find((hit) => Array.isArray(hit.labelRelation?.labelAt));
1634
- if (reverseHit) throwReadableLabelRoutePinConflict(reverseHit, points);
1635
- }
1636
- }
1637
-
1638
- function validateReadablePairwisePinConflicts() {
1639
- const labels = workflow.edges.map((edge, relationIndex) => (
1640
- labelRectFor(edge, relationIndex)
1641
- )).filter(Boolean);
1642
- const routedRelations = workflow.edges.map((edge, relationIndex) => (
1643
- nodes.has(edge.from) && nodes.has(edge.to)
1644
- ? { relation: edge, relationIndex, points: pathFor(edge).points }
1645
- : null
1646
- )).filter(Boolean);
1647
-
1648
- const labelRouteHit = collectLabelRouteClearance({
1649
- labels,
1650
- routedRelations,
1651
- threshold: 4,
1652
- }).find((hit) => (
1653
- Array.isArray(hit.labelRelation?.labelAt) || hasAuthoredRouteAssertions(hit.otherRelation)
1654
- ));
1655
- if (labelRouteHit) throwReadableLabelRoutePinConflict(labelRouteHit);
1656
-
1657
- for (let leftIndex = 0; leftIndex < labels.length; leftIndex += 1) {
1658
- for (let rightIndex = leftIndex + 1; rightIndex < labels.length; rightIndex += 1) {
1659
- const left = labels[leftIndex];
1660
- const right = labels[rightIndex];
1661
- if (!rectsOverlap(left, right, -2)) continue;
1662
- throwReadableLabelLabelPinConflict(left, right);
1663
- }
1664
- }
1665
-
1666
- const requestedProfile = workflow.meta?.quality_profile;
1667
- if (requestedProfile !== 'showcase') return;
1668
- for (let leftIndex = 0; leftIndex < routedRelations.length; leftIndex += 1) {
1669
- const left = routedRelations[leftIndex];
1670
- for (let rightIndex = leftIndex + 1; rightIndex < routedRelations.length; rightIndex += 1) {
1671
- const right = routedRelations[rightIndex];
1672
- const leftPinned = hasAuthoredRouteAssertions(left.relation);
1673
- const rightPinned = hasAuthoredRouteAssertions(right.relation);
1674
- if (!leftPinned && !rightPinned) continue;
1675
- if ([left.relation.from, left.relation.to].some((id) => (
1676
- id === right.relation.from || id === right.relation.to
1677
- ))) continue;
1678
- const leftAnalysis = forwardCollinearAnalysisSegments(left.points);
1679
- const rightAnalysis = forwardCollinearAnalysisSegments(right.points);
1680
- for (const leftSegment of leftAnalysis) {
1681
- for (const rightSegment of rightAnalysis) {
1682
- const point = properOrthogonalIntersection(
1683
- leftSegment.start,
1684
- leftSegment.end,
1685
- rightSegment.start,
1686
- rightSegment.end,
1687
- );
1688
- if (!point) continue;
1689
- const leftSourceIndex = sourceSegmentIndexAtPoint(leftSegment, point);
1690
- const rightSourceIndex = sourceSegmentIndexAtPoint(rightSegment, point);
1691
- const leftSource = {
1692
- from: left.points[leftSourceIndex],
1693
- to: left.points[leftSourceIndex + 1],
1694
- };
1695
- const rightSource = {
1696
- from: right.points[rightSourceIndex],
1697
- to: right.points[rightSourceIndex + 1],
1698
- };
1699
- const alternatives = verifiedRoutePairPinAlternatives(
1700
- left.relation,
1701
- right.relation,
1702
- 'so readable-v2 can replan the remaining authored route assertions',
1703
- );
1704
- const diagnosticEdge = leftPinned ? left.relation : right.relation;
1705
- throwExplicitPinConflict(diagnosticEdge, 'explicit route-route crossing', {
1706
- conflictingPins: alternatives.conflictingPins,
1707
- point,
1708
- segmentIndex: leftSourceIndex,
1709
- otherSegmentIndex: rightSourceIndex,
1710
- routeSegments: [
1711
- { edge: left.relation.id ?? null, from: [...leftSegment.start], to: [...leftSegment.end] },
1712
- { edge: right.relation.id ?? null, from: [...rightSegment.start], to: [...rightSegment.end] },
1713
- ],
1714
- sourceRouteSegments: [
1715
- { edge: left.relation.id ?? null, from: [...leftSource.from], to: [...leftSource.to] },
1716
- { edge: right.relation.id ?? null, from: [...rightSource.from], to: [...rightSource.to] },
1717
- ],
1718
- }, alternatives.supportedFixes);
1719
- }
1720
- }
1721
- }
1722
- }
1723
-
1724
- const corridorHit = collectAmbiguousCorridors({
1725
- routedRelations,
1726
- minOverlapPx: 8,
1727
- }).find((hit) => (
1728
- hasAuthoredRouteAssertions(hit.left.relation)
1729
- || hasAuthoredRouteAssertions(hit.right.relation)
1730
- ));
1731
- if (corridorHit) {
1732
- const leftSegment = {
1733
- from: corridorHit.left.points[corridorHit.leftSegment],
1734
- to: corridorHit.left.points[corridorHit.leftSegment + 1],
1735
- };
1736
- const rightSegment = {
1737
- from: corridorHit.right.points[corridorHit.rightSegment],
1738
- to: corridorHit.right.points[corridorHit.rightSegment + 1],
1739
- };
1740
- const leftPinned = hasAuthoredRouteAssertions(corridorHit.left.relation);
1741
- const alternatives = verifiedRoutePairPinAlternatives(
1742
- corridorHit.left.relation,
1743
- corridorHit.right.relation,
1744
- 'so readable-v2 can replan the remaining authored route assertions',
1745
- );
1746
- const diagnosticEdge = leftPinned ? corridorHit.left.relation : corridorHit.right.relation;
1747
- throwExplicitPinConflict(diagnosticEdge, 'explicit route-route corridor clearance', {
1748
- conflictingPins: alternatives.conflictingPins,
1749
- segmentIndex: corridorHit.leftSegment,
1750
- otherSegmentIndex: corridorHit.rightSegment,
1751
- routeSegments: [
1752
- {
1753
- edge: corridorHit.left.relation.id ?? null,
1754
- from: [...leftSegment.from],
1755
- to: [...leftSegment.to],
1756
- },
1757
- {
1758
- edge: corridorHit.right.relation.id ?? null,
1759
- from: [...rightSegment.from],
1760
- to: [...rightSegment.to],
1761
- },
1762
- ],
1763
- overlapStart: [...corridorHit.overlapStart],
1764
- overlapEnd: [...corridorHit.overlapEnd],
1765
- overlapLengthPx: corridorHit.overlapLength,
1766
- minimumClearancePx: 8,
1767
- }, alternatives.supportedFixes);
1768
- }
1769
- }
1770
-
1771
- const READABLE_PRESET_PIN_FIELDS = Object.freeze({
1772
- straight: [],
1773
- drop: ['channelY'],
1774
- 'outside-right': ['channelX'],
1775
- 'return-left': ['channelX'],
1776
- 'bottom-channel': ['channelY'],
1777
- 'up-channel': ['channelY'],
1778
- });
1779
-
1780
- function presentChannelPins(edge) {
1781
- return ['channelX', 'channelY'].filter((field) => edge[field] !== undefined);
1782
- }
1783
-
1784
- function validateReadableRouteControls(edge) {
1785
- const channelPins = presentChannelPins(edge);
1786
- const preset = edge.route || 'auto';
1787
- if (preset === 'auto') return;
1788
- const allowedPins = new Set(READABLE_PRESET_PIN_FIELDS[preset] || []);
1789
- const conflictingPins = channelPins.filter((field) => !allowedPins.has(field));
1790
- if (!conflictingPins.length) return;
1791
- const edgeIndex = workflow.edges.indexOf(edge);
1792
- const alternatives = verifiedPinReferenceAlternatives([
1793
- { edge, edgeIndex, field: 'route' },
1794
- ...conflictingPins.map((field) => ({ edge, edgeIndex, field })),
1795
- ], 'and keep the remaining verified route assertions');
1796
- throwExplicitPinConflict(edge, 'route preset compatibility', {
1797
- route: preset,
1798
- allowedPins: [...allowedPins],
1799
- conflictingPins: alternatives.conflictingPins,
1800
- }, alternatives.supportedFixes);
1801
- }
1802
-
1803
- function segmentOrientation(start, end) {
1804
- if (Math.abs(start[0] - end[0]) <= 0.0001) return 'vertical';
1805
- if (Math.abs(start[1] - end[1]) <= 0.0001) return 'horizontal';
1806
- return 'diagonal';
1807
- }
1808
-
1809
- function routeSegments(points) {
1810
- return points.slice(0, -1).map((start, index) => ({
1811
- start,
1812
- end: points[index + 1],
1813
- orientation: segmentOrientation(start, points[index + 1]),
1814
- }));
1815
- }
1816
-
1817
- function endpointSideIsHonored(points, side, endpoint) {
1818
- if (!side || side === 'auto' || points.length < 2) return true;
1819
- const source = endpoint === 'source';
1820
- const from = source ? points[0] : points.at(-2);
1821
- const to = source ? points[1] : points.at(-1);
1822
- const dx = to[0] - from[0];
1823
- const dy = to[1] - from[1];
1824
- if (source) {
1825
- if (side === 'right') return dx > 0 && Math.abs(dy) <= 0.0001;
1826
- if (side === 'left') return dx < 0 && Math.abs(dy) <= 0.0001;
1827
- if (side === 'bottom') return dy > 0 && Math.abs(dx) <= 0.0001;
1828
- if (side === 'top') return dy < 0 && Math.abs(dx) <= 0.0001;
1829
- return false;
1830
- }
1831
- if (side === 'right') return dx < 0 && Math.abs(dy) <= 0.0001;
1832
- if (side === 'left') return dx > 0 && Math.abs(dy) <= 0.0001;
1833
- if (side === 'bottom') return dy < 0 && Math.abs(dx) <= 0.0001;
1834
- if (side === 'top') return dy > 0 && Math.abs(dx) <= 0.0001;
1835
- return false;
1836
- }
1837
-
1838
- function corridorTopologyMatches(points, axis, coordinate) {
1839
- const collapsed = normalizeRoutePoints(points.map((point) => [...point]));
1840
- const start = collapsed[0];
1841
- const end = collapsed.at(-1);
1842
- const via = axis === 'x'
1843
- ? [[coordinate, start[1]], [coordinate, end[1]]]
1844
- : [[start[0], coordinate], [end[0], coordinate]];
1845
- const expected = normalizeRoutePoints([start, ...via, end]);
1846
- const actualPattern = routeSegments(collapsed).map(({ orientation }) => orientation);
1847
- const expectedPattern = routeSegments(expected).map(({ orientation }) => orientation);
1848
- return actualPattern.length === expectedPattern.length
1849
- && actualPattern.every((orientation, index) => orientation === expectedPattern[index])
1850
- && routeContainsChannelPin(
1851
- collapsed,
1852
- axis === 'x' ? 'channelX' : 'channelY',
1853
- coordinate,
1854
- );
1855
- }
1856
-
1857
- function routeMatchesPresetFamily(preset, points, from, to) {
1858
- const collapsed = normalizeRoutePoints(points.map((point) => [...point]));
1859
- const segments = routeSegments(collapsed);
1860
- if (preset === 'straight') return collapsed.length === 2;
1861
- if (preset === 'drop') {
1862
- if (from.lane === to.lane) return false;
1863
- if (collapsed.length === 2 && segments[0]?.orientation === 'vertical') return true;
1864
- const upper = from.cy <= to.cy ? from : to;
1865
- const lower = upper === from ? to : from;
1866
- return segments.some(({ start, orientation }) => (
1867
- orientation === 'horizontal'
1868
- && start[1] >= upper.y + upper.height - 0.0001
1869
- && start[1] <= lower.y + 0.0001
1870
- && corridorTopologyMatches(points, 'y', start[1])
1871
- ));
1872
- }
1873
- if (preset === 'outside-right' || preset === 'return-left') {
1874
- const boundary = preset === 'outside-right'
1875
- ? Math.max(from.x + from.width, to.x + to.width)
1876
- : Math.min(from.x, to.x);
1877
- return segments.some(({ start, orientation }) => (
1878
- orientation === 'vertical'
1879
- && (preset === 'outside-right'
1880
- ? start[0] > boundary + 0.0001
1881
- : start[0] < boundary - 0.0001)
1882
- && corridorTopologyMatches(points, 'x', start[0])
1883
- ));
1884
- }
1885
- if (preset === 'bottom-channel' || preset === 'up-channel') {
1886
- const boundary = preset === 'bottom-channel'
1887
- ? Math.max(from.y + from.height, to.y + to.height)
1888
- : Math.min(from.y, to.y);
1889
- return segments.some(({ start, orientation }) => (
1890
- orientation === 'horizontal'
1891
- && (preset === 'bottom-channel'
1892
- ? start[1] > boundary + 0.0001
1893
- : start[1] < boundary - 0.0001)
1894
- && corridorTopologyMatches(points, 'y', start[1])
1895
- ));
1896
- }
1897
- return false;
1898
- }
1899
-
1900
- function routeContainsChannelPin(points, field, value) {
1901
- return points.slice(0, -1).some((start, index) => {
1902
- const end = points[index + 1];
1903
- if (field === 'channelX') {
1904
- return start[0] === value
1905
- && end[0] === value
1906
- && Math.abs(end[1] - start[1]) > 0.0001;
1907
- }
1908
- return start[1] === value
1909
- && end[1] === value
1910
- && Math.abs(end[0] - start[0]) > 0.0001;
1911
- });
1912
- }
1913
-
1914
- function validateReadablePinnedGeometry() {
1915
- if (workflow.schema_version !== 2) return;
1916
- for (const edge of workflow.edges) {
1917
- if (!nodes.has(edge.from) || !nodes.has(edge.to)) continue;
1918
- validateReadableRouteControls(edge);
1919
- const edgeName = workflowEdgeName(edge);
1920
- const edgeIndex = sourceIndexes.edges.get(edge);
1921
- if (Array.isArray(edge.labelAt)) {
1922
- const rect = labelRectFor(edge, workflow.edges.indexOf(edge));
1923
- if (rect && (rect.x < 0 || rect.y < 0)) {
1924
- throwExplicitPinConflict(edge, 'viewBox-origin containment', {
1925
- conflictingPins: [{
1926
- edge: edgeName,
1927
- field: 'labelAt',
1928
- path: `/edges/${edgeIndex}/labelAt`,
1929
- value: [...edge.labelAt],
1930
- }],
1931
- offendingRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
1932
- minimumCoordinate: 0,
1933
- }, [verifiedLabelAtNudge(edge)]);
1934
- }
1935
- }
1936
- const negativeViaIndex = asArray(edge.via).findIndex(([x, y]) => x < 0 || y < 0);
1937
- const negativeRoutePin = negativeViaIndex >= 0
1938
- ? {
1939
- field: 'via',
1940
- path: `/edges/${edgeIndex}/via/${negativeViaIndex}`,
1941
- value: [...edge.via[negativeViaIndex]],
1942
- }
1943
- : edge.channelX < 0
1944
- ? { field: 'channelX', path: `/edges/${edgeIndex}/channelX`, value: edge.channelX }
1945
- : edge.channelY < 0
1946
- ? { field: 'channelY', path: `/edges/${edgeIndex}/channelY`, value: edge.channelY }
1947
- : null;
1948
- if (negativeRoutePin) {
1949
- throwExplicitPinConflict(edge, 'viewBox-origin containment', {
1950
- conflictingPins: [{ edge: edgeName, ...negativeRoutePin }],
1951
- minimumCoordinate: 0,
1952
- }, [
1953
- verifiedAutomaticRouteFix(edge),
1954
- verifiedAutomaticRouteFix(edge, { clearSides: true }),
1955
- ]);
1956
- }
1957
- const hasPinnedRoute = Array.isArray(edge.via)
1958
- || edge.channelX !== undefined
1959
- || edge.channelY !== undefined;
1960
- const points = pathFor(edge).points;
1961
- if (hasPinnedRoute) {
1962
- const invalidPointIndex = points.findIndex((point) => (
1963
- !Array.isArray(point) || point.length !== 2 || !isFinitePoint(...point)
1964
- ));
1965
- if (invalidPointIndex !== -1) {
1966
- const alternatives = verifiedRouteGeometryPinAlternatives(edge);
1967
- throwExplicitPinConflict(edge, 'finite route coordinates', {
1968
- conflictingPins: alternatives.conflictingPins,
1969
- pointIndex: invalidPointIndex,
1970
- point: points[invalidPointIndex],
1971
- }, alternatives.supportedFixes);
1972
- }
1973
- for (let segmentIndex = 0; segmentIndex < points.length - 1; segmentIndex += 1) {
1974
- const start = points[segmentIndex];
1975
- const end = points[segmentIndex + 1];
1976
- const dx = Math.abs(end[0] - start[0]);
1977
- const dy = Math.abs(end[1] - start[1]);
1978
- if (dx <= 0.0001 && dy <= 0.0001) {
1979
- const duplicateFix = Array.isArray(edge.via) && edge.via.length
1980
- ? verifiedEdgeFix(
1981
- edge,
1982
- `remove duplicate via[${Math.min(segmentIndex, edge.via.length - 1)}] and keep the remaining authored pins unchanged`,
1983
- (candidate) => candidate.via.splice(Math.min(segmentIndex, candidate.via.length - 1), 1),
1984
- )
1985
- : verifiedAutomaticRouteFix(edge);
1986
- const alternatives = Array.isArray(edge.via)
1987
- ? null
1988
- : verifiedRouteGeometryPinAlternatives(edge);
1989
- throwExplicitPinConflict(edge, 'non-zero route segments', {
1990
- conflictingPins: alternatives?.conflictingPins
1991
- || [authoredPinEvidence(edge, 'via')],
1992
- segmentIndex,
1993
- from: start,
1994
- to: end,
1995
- }, alternatives?.supportedFixes || [duplicateFix]);
1996
- }
1997
- if (dx > 0.0001 && dy > 0.0001) {
1998
- const alternatives = verifiedRouteGeometryPinAlternatives(edge);
1999
- throwExplicitPinConflict(edge, 'orthogonal route segments', {
2000
- conflictingPins: alternatives.conflictingPins,
2001
- segmentIndex,
2002
- from: start,
2003
- to: end,
2004
- }, alternatives.supportedFixes);
2005
- }
2006
- const endpoint = segmentIndex === 0 || segmentIndex === points.length - 2;
2007
- const minimumPx = points.length === 2 ? 28 : endpoint ? 8 : 16;
2008
- const lengthPx = dx + dy;
2009
- if (lengthPx + 0.0001 < minimumPx) {
2010
- const alternatives = verifiedRouteGeometryPinAlternatives(edge);
2011
- throwExplicitPinConflict(edge, endpoint ? '8px endpoint stub clearance' : '16px interior turn clearance', {
2012
- conflictingPins: alternatives.conflictingPins,
2013
- segmentIndex,
2014
- position: segmentIndex === 0 ? 'source-stub' : segmentIndex === points.length - 2 ? 'target-stub' : 'interior',
2015
- from: start,
2016
- to: end,
2017
- lengthPx,
2018
- minimumPx,
2019
- }, alternatives.supportedFixes);
2020
- }
2021
- }
2022
- const { fromSide, toSide } = edgeSides(edge);
2023
- if (Array.isArray(edge.via)) {
2024
- const missingChannelPins = presentChannelPins(edge).filter((field) => (
2025
- !routeContainsChannelPin(points, field, edge[field])
2026
- ));
2027
- if (missingChannelPins.length) {
2028
- const candidateFields = ['via', ...missingChannelPins];
2029
- const alternatives = verifiedPinRemovalAlternatives(
2030
- edge,
2031
- candidateFields,
2032
- 'and replan the remaining explicit route assertions',
2033
- );
2034
- throwExplicitPinConflict(edge, 'channel pin preservation', {
2035
- route: edge.route || 'auto',
2036
- conflictingPins: conflictPinsFromRemovalSets(
2037
- edge,
2038
- alternatives.removalSets,
2039
- candidateFields,
2040
- ),
2041
- points: points.map((point) => [...point]),
2042
- }, alternatives.supportedFixes);
2043
- }
2044
- }
2045
- if (edge.route
2046
- && edge.route !== 'auto'
2047
- && !routeMatchesPresetFamily(
2048
- edge.route,
2049
- points,
2050
- nodes.get(edge.from),
2051
- nodes.get(edge.to),
2052
- )) {
2053
- const authoredEdgeIndex = workflow.edges.indexOf(edge);
2054
- const alternatives = verifiedPinReferenceAlternatives([
2055
- { edge, edgeIndex: authoredEdgeIndex, field: 'route' },
2056
- ...presentRouteGeometryFields(edge)
2057
- .map((field) => ({ edge, edgeIndex: authoredEdgeIndex, field })),
2058
- ], 'and keep the remaining verified route assertions');
2059
- throwExplicitPinConflict(edge, 'route preset compatibility', {
2060
- route: edge.route,
2061
- conflictingPins: alternatives.conflictingPins,
2062
- points: points.map((point) => [...point]),
2063
- }, alternatives.supportedFixes);
2064
- }
2065
- if (!routeHonorsEndpointSides(points, fromSide, toSide)) {
2066
- const mismatchedSideFields = [
2067
- ...(edge.fromSide && edge.fromSide !== 'auto'
2068
- && !endpointSideIsHonored(points, fromSide, 'source') ? ['fromSide'] : []),
2069
- ...(edge.toSide && edge.toSide !== 'auto'
2070
- && !endpointSideIsHonored(points, toSide, 'target') ? ['toSide'] : []),
2071
- ];
2072
- const candidateFields = [
2073
- ...mismatchedSideFields,
2074
- ...presentRouteGeometryFields(edge),
2075
- ];
2076
- const alternatives = verifiedPinRemovalAlternatives(
2077
- edge,
2078
- candidateFields,
2079
- 'and replan the remaining explicit pins',
2080
- );
2081
- throwExplicitPinConflict(edge, 'perpendicular endpoint-side direction', {
2082
- conflictingPins: conflictPinsFromRemovalSets(
2083
- edge,
2084
- alternatives.removalSets,
2085
- candidateFields,
2086
- ),
2087
- points: points.map((point) => [...point]),
2088
- fromSide,
2089
- toSide,
2090
- }, alternatives.supportedFixes);
2091
- }
2092
- const nodeCollision = firstRouteNodeCollision(edge, points);
2093
- if (nodeCollision) {
2094
- const alternatives = verifiedRouteGeometryPinAlternatives(edge);
2095
- throwExplicitPinConflict(edge, 'node clearance', {
2096
- conflictingPins: alternatives.conflictingPins,
2097
- ...nodeCollision,
2098
- }, alternatives.supportedFixes);
2099
- }
2100
- const legendObstacle = workflowLegendRects().find((rect) => points.slice(0, -1).some((point, index) => (
2101
- segmentIntersectsRect({ start: point, end: points[index + 1] }, rect)
2102
- )));
2103
- if (legendObstacle) {
2104
- const alternatives = verifiedRouteGeometryPinAlternatives(edge);
2105
- throwExplicitPinConflict(edge, 'legend clearance', {
2106
- conflictingPins: alternatives.conflictingPins,
2107
- points: points.map((point) => [...point]),
2108
- legendObstacle,
2109
- }, alternatives.supportedFixes);
2110
- }
2111
- const compositionObstacle = workflowSceneLabelObstacles().find((rect) => (
2112
- points.slice(0, -1).some((point, index) => (
2113
- segmentIntersectsRect({ start: point, end: points[index + 1] }, rect)
2114
- ))
2115
- ));
2116
- if (compositionObstacle) {
2117
- const alternatives = verifiedRouteGeometryPinAlternatives(edge);
2118
- throwExplicitPinConflict(edge, 'lane/phase/group label clearance', {
2119
- conflictingPins: alternatives.conflictingPins,
2120
- points: points.map((point) => [...point]),
2121
- compositionObstacle,
2122
- }, alternatives.supportedFixes);
2123
- }
2124
- const [frameRun] = collectBorderRuns({
2125
- routedRelations: [{ points }],
2126
- frames: workflowCompositionFrames(),
2127
- });
2128
- if (frameRun) {
2129
- const alternatives = verifiedRouteGeometryPinAlternatives(edge);
2130
- throwExplicitPinConflict(edge, 'structural-frame border clearance', {
2131
- conflictingPins: alternatives.conflictingPins,
2132
- points: points.map((point) => [...point]),
2133
- frame: frameRun.frame?.id ?? frameRun.frameIndex,
2134
- side: frameRun.side,
2135
- overlapLengthPx: frameRun.overlapLength,
2136
- }, alternatives.supportedFixes);
2137
- }
2138
- }
2139
-
2140
- if (edge.labelAt) {
2141
- const rect = labelRectFor(edge, workflow.edges.indexOf(edge));
2142
- const obstacle = rect && [...nodes.values()].find((node) => rectsOverlap(rect, node, -2));
2143
- if (obstacle) {
2144
- throwExplicitPinConflict(edge, 'edge-label node clearance', {
2145
- conflictingPins: [authoredPinEvidence(edge, 'labelAt')],
2146
- labelAt: [...edge.labelAt],
2147
- labelRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
2148
- obstacleNode: obstacle.id,
2149
- }, [verifiedEdgeFix(
2150
- edge,
2151
- 'remove labelAt so readable-v2 can use its verified automatic label placement',
2152
- (candidate) => { delete candidate.labelAt; },
2153
- )]);
2154
- }
2155
- const legendObstacle = rect && workflowLegendRects().find((legendRect) => (
2156
- rectsOverlap(rect, legendRect)
2157
- ));
2158
- if (legendObstacle) {
2159
- throwExplicitPinConflict(edge, 'edge-label legend clearance', {
2160
- conflictingPins: [authoredPinEvidence(edge, 'labelAt')],
2161
- labelAt: [...edge.labelAt],
2162
- labelRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
2163
- legendObstacle,
2164
- }, [verifiedEdgeFix(
2165
- edge,
2166
- 'remove labelAt so readable-v2 can use its verified automatic label placement',
2167
- (candidate) => { delete candidate.labelAt; },
2168
- )]);
2169
- }
2170
- const compositionObstacle = rect && workflowSceneLabelObstacles().find((candidate) => (
2171
- rectsOverlap(rect, candidate)
2172
- ));
2173
- if (compositionObstacle) {
2174
- throwExplicitPinConflict(edge, 'edge-label lane/phase/group clearance', {
2175
- conflictingPins: [authoredPinEvidence(edge, 'labelAt')],
2176
- labelAt: [...edge.labelAt],
2177
- labelRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
2178
- compositionObstacle,
2179
- }, [verifiedEdgeFix(
2180
- edge,
2181
- `remove labelAt from edge "${workflowEdgeName(edge)}" so readable-v2 can use its verified automatic label placement`,
2182
- (candidate) => { delete candidate.labelAt; },
2183
- )]);
2184
- }
2185
- }
2186
- }
2187
- validateReadablePairwisePinConflicts();
2188
- }
2189
-
2190
- function validateWorkflow() {
2191
- const problems = [];
2192
- if (workflow.schema_version !== 1 && workflow.schema_version !== 2) {
2193
- problems.push('Workflow files must set "schema_version" to 1 or 2.');
2194
- }
2195
- if (workflow.diagram_type !== 'workflow') {
2196
- problems.push(`Unsupported diagram_type "${workflow.diagram_type}". Expected "workflow".`);
2197
- }
2198
- if (!workflow.meta || !workflow.meta.title) {
2199
- problems.push('Workflow files must include meta.title.');
2200
- }
2201
- if (!Array.isArray(workflow.lanes) || !workflow.lanes.length) {
2202
- problems.push('Workflow files must include at least one lane.');
2203
- }
2204
- if (!Array.isArray(workflow.nodes)) {
2205
- problems.push('Workflow files must include a nodes array.');
2206
- }
2207
- if (!Array.isArray(workflow.edges)) {
2208
- problems.push('Workflow files must include an edges array.');
2209
- }
2210
- if (workflow.phases !== undefined && !Array.isArray(workflow.phases)) {
2211
- problems.push('Workflow "phases" must be an array.');
2212
- }
2213
- if (workflow.groups !== undefined && !Array.isArray(workflow.groups)) {
2214
- problems.push('Workflow "groups" must be an array.');
2215
- }
2216
- if (workflow.mainPath !== undefined && !Array.isArray(workflow.mainPath)) {
2217
- problems.push('Workflow "mainPath" must be an array of node ids.');
2218
- }
2219
- if (workflow.cards !== undefined && !Array.isArray(workflow.cards)) {
2220
- problems.push('Workflow "cards" must be an array.');
2221
- }
2222
- if (problems.length) {
2223
- throwDiagnosticProblems('Workflow layout validation failed', problems, {
2224
- subject: { diagramType: 'workflow' },
2225
- });
2226
- }
2227
-
2228
- enforceLegacyColumnCapacity();
2229
-
2230
- const laneIds = new Set(workflow.lanes.map((lane) => lane.id));
2231
- if (laneIds.size !== workflow.lanes.length) {
2232
- problems.push('Lane ids must be unique.');
2233
- }
2234
- if (nodes.size !== workflow.nodes.length) {
2235
- problems.push('Node ids must be unique.');
2236
- }
2237
- const phaseIds = new Set(asArray(workflow.phases).map((phase) => phase.id));
2238
- if (phaseIds.size !== asArray(workflow.phases).length) {
2239
- problems.push('Phase ids must be unique.');
2240
- }
2241
- const groupIds = new Set(asArray(workflow.groups).map((group) => group.id));
2242
- if (groupIds.size !== asArray(workflow.groups).length) {
2243
- problems.push('Group ids must be unique.');
2244
- }
2245
-
2246
- for (const node of nodes.values()) {
2247
- if (!laneIds.has(node.lane)) {
2248
- problems.push(`Node "${node.id}" uses unknown lane "${node.lane}".`);
2249
- continue;
2250
- }
2251
- if (!Number.isInteger(node.col) || node.col < 0 || node.col >= layout.colXs.length) {
2252
- problems.push(`Node "${node.id}" uses column ${node.col}, but valid columns are integers 0..${layout.colXs.length - 1}.`);
2253
- continue;
2254
- }
2255
- if (!isFinitePoint(node.x, node.y, node.cx, node.cy)) {
2256
- problems.push(`Node "${node.id}" produced non-finite coordinates — check col, width, height, and yOffset are numbers.`);
2257
- continue;
2258
- }
2259
- const estLabelW = textUnits(node.label) * 6.8;
2260
- if (estLabelW > node.width + 6) {
2261
- problems.push(`Label "${node.label}" (~${Math.round(estLabelW)}px) is wider than node "${node.id}" (${node.width}px) — shorten the label or increase node.width.`);
2262
- }
2263
- const brandRailProblem = brandTopRailProblem(node, node.width, nodeTextFit.labelMinimum);
2264
- if (brandRailProblem) problems.push(brandRailProblem);
2265
- const availableTextW = availableNodeTextWidth(node.width);
2266
- for (const [field, value, minimum] of [
2267
- ['Sublabel', node.sublabel, nodeTextFit.sublabelMinimum],
2268
- ['Tag', node.tag, nodeTextFit.tagMinimum],
2269
- ]) {
2270
- if (!value) continue;
2271
- const minimumW = minimumNodeTextWidth(value, minimum);
2272
- if (minimumW > availableTextW) {
2273
- problems.push(`${field} "${value}" needs ~${Math.ceil(minimumW)}px at the ${minimum}px legible minimum, but node "${node.id}" provides ${availableTextW}px — shorten the ${field.toLowerCase()} or increase node.width.`);
2274
- }
2275
- }
2276
-
2277
- const top = laneTop(node.lane);
2278
- const contentTop = top + layout.laneTitleH + laneGroupHeaderH(node.lane);
2279
- const laneRight = layout.laneX + layout.laneW;
2280
- if (node.x < layout.laneX || node.x + node.width > laneRight) {
2281
- problems.push(`Node "${node.id}" exceeds the horizontal bounds of lane "${node.lane}".`);
2282
- }
2283
- if (node.y < contentTop || node.y + node.height > top + laneHeight(node.lane)) {
2284
- problems.push(`Node "${node.id}" collides with the title or boundary of lane "${node.lane}".`);
2285
- }
2286
- }
2287
-
2288
- const phaseRanges = [];
2289
- for (const phase of asArray(workflow.phases)) {
2290
- if (!Number.isInteger(phase.fromCol) || !Number.isInteger(phase.toCol)) {
2291
- problems.push(`Phase "${phase.id}" must use integer fromCol/toCol values.`);
2292
- continue;
2293
- }
2294
- if (phase.fromCol < 0 || phase.toCol >= layout.colXs.length || phase.fromCol > phase.toCol) {
2295
- problems.push(`Phase "${phase.id}" uses invalid columns ${phase.fromCol}..${phase.toCol}; use an ordered range within 0..${layout.colXs.length - 1}.`);
2296
- } else {
2297
- phaseRanges.push(phase);
2298
- }
2299
- const estLabelW = textUnits(phase.label) * 5.6;
2300
- const width = phaseSpan(phase).width;
2301
- if (estLabelW > width + 8) {
2302
- problems.push(`Phase label "${phase.label}" (~${Math.round(estLabelW)}px) is wider than its ${Math.round(width)}px span — shorten the label or widen the phase range.`);
2303
- }
2304
- }
2305
- phaseRanges.sort((a, b) => a.fromCol - b.fromCol || a.toCol - b.toCol);
2306
- for (let i = 0; i < phaseRanges.length; i += 1) {
2307
- for (let j = i + 1; j < phaseRanges.length; j += 1) {
2308
- const earlier = phaseRanges[i];
2309
- const later = phaseRanges[j];
2310
- if (later.fromCol > earlier.toCol) break;
2311
- problems.push(`Phase "${later.id}" (${later.fromCol}..${later.toCol}) overlaps phase "${earlier.id}" (${earlier.fromCol}..${earlier.toCol}) — start at col ${earlier.toCol + 1} or later, or end the earlier phase at col ${later.fromCol - 1}.`);
2312
- }
2313
- }
2314
-
2315
- for (const group of asArray(workflow.groups)) {
2316
- if (!laneIds.has(group.lane)) {
2317
- problems.push(`Group "${group.id}" uses unknown lane "${group.lane}".`);
2318
- continue;
2319
- }
2320
- if (!Number.isInteger(group.fromCol) || !Number.isInteger(group.toCol)) {
2321
- problems.push(`Group "${group.id}" must use integer fromCol/toCol values.`);
2322
- continue;
2323
- }
2324
- if (group.fromCol < 0 || group.toCol >= layout.colXs.length || group.fromCol > group.toCol) {
2325
- problems.push(`Group "${group.id}" uses invalid columns ${group.fromCol}..${group.toCol}; use an ordered range within 0..${layout.colXs.length - 1}.`);
2326
- }
2327
- const contained = [...nodes.values()].some((node) => node.lane === group.lane && node.col >= group.fromCol && node.col <= group.toCol);
2328
- if (!contained) {
2329
- problems.push(`Group "${group.id}" does not contain any nodes — align its lane/columns with the parallel or branch work it frames.`);
2330
- }
2331
- }
2332
-
2333
- const byLane = new Map();
2334
- for (const node of nodes.values()) {
2335
- byLane.set(node.lane, [...(byLane.get(node.lane) || []), node]);
2336
- }
2337
- for (const [lane, laneNodes] of byLane) {
2338
- for (let i = 0; i < laneNodes.length; i += 1) {
2339
- for (let j = i + 1; j < laneNodes.length; j += 1) {
2340
- if (rectsOverlap(laneNodes[i], laneNodes[j], 8)) {
2341
- problems.push(`Nodes "${laneNodes[i].id}" and "${laneNodes[j].id}" are less than 8px apart in lane "${lane}" — move one to another col, adjust yOffset, or reduce width/height.`);
2342
- }
2343
- }
2344
- }
2345
- }
2346
-
2347
- for (const edge of workflow.edges) {
2348
- if (!nodes.has(edge.from)) problems.push(`Edge "${edge.label || edge.from}" references unknown source "${edge.from}".`);
2349
- if (!nodes.has(edge.to)) problems.push(`Edge "${edge.label || edge.to}" references unknown target "${edge.to}".`);
2350
- if (nodes.has(edge.from) && nodes.has(edge.to)) {
2351
- const routed = pathFor(edge);
2352
- if (routed.points.length === 2) {
2353
- const [start, end] = routed.points;
2354
- const segmentLength = Math.hypot(end[0] - start[0], end[1] - start[1]);
2355
- if (segmentLength < 28) {
2356
- problems.push(`Edge "${edge.from}" -> "${edge.to}" is too short (${Math.round(segmentLength)}px; minimum 28px) — move the nodes farther apart or use a verified orthogonal route with readable clearance.`);
2357
- }
2358
- }
2359
- }
2360
- }
2361
-
2362
- problems.push(...cleanEndpointSideProblems({
2363
- relations: workflow.edges,
2364
- endpointIds: new Set(nodes.keys()),
2365
- pathFor,
2366
- diagramType: 'workflow',
2367
- relationCollection: 'edges',
2368
- fromSideFor: (edge) => edgeSides(edge).fromSide,
2369
- toSideFor: (edge) => edgeSides(edge).toSide,
2370
- routeHint: 'keep automatic routing, or choose fromSide/toSide and via points whose first and final segments cross node borders perpendicularly',
2371
- }));
2372
- problems.push(...cleanFlowProblems({
2373
- relations: workflow.edges,
2374
- obstacles: nodes.values(),
2375
- pathFor,
2376
- diagramType: 'workflow',
2377
- relationCollection: 'edges',
2378
- obstacleKind: 'node',
2379
- routeHint: 'adjust fromSide/toSide, set route/via or channel coordinates, or move the node to a clearer lane/column'
2380
- }));
2381
- problems.push(...cleanCrossingProblems({
2382
- relations: workflow.edges,
2383
- endpointIds: new Set(nodes.keys()),
2384
- pathFor,
2385
- diagramType: 'workflow',
2386
- relationCollection: 'edges',
2387
- profile: workflow.meta?.quality_profile,
2388
- profileIsAuthoritative: true,
2389
- mergeForwardCollinearWaypoints: workflow.schema_version === 2,
2390
- routeHint: 'adjust route/via, bias, or channel coordinates so the edges use separate lane corridors'
2391
- }));
2392
- problems.push(...cleanAmbiguousCorridorProblems({
2393
- relations: workflow.edges,
2394
- endpointIds: new Set(nodes.keys()),
2395
- pathFor,
2396
- diagramType: 'workflow',
2397
- relationCollection: 'edges',
2398
- profile: workflow.meta?.quality_profile,
2399
- profileIsAuthoritative: true,
2400
- routeHint: 'adjust route/via, bias, or channel coordinates so unrelated edges do not visually merge'
2401
- }));
2402
- problems.push(...cleanBorderRunProblems({
2403
- relations: workflow.edges,
2404
- endpointIds: new Set(nodes.keys()),
2405
- frames: workflowCompositionFrames(),
2406
- pathFor,
2407
- diagramType: 'workflow',
2408
- relationCollection: 'edges',
2409
- profile: workflow.meta?.quality_profile,
2410
- profileIsAuthoritative: true,
2411
- routeHint: 'adjust route/via, bias, or channel coordinates so the edge crosses the lane or group perpendicularly instead of following its border'
2412
- }));
2413
- problems.push(...cleanRouteRhythmProblems({
2414
- relations: workflow.edges,
2415
- endpointIds: new Set(nodes.keys()),
2416
- pathFor,
2417
- diagramType: 'workflow',
2418
- relationCollection: 'edges',
2419
- profile: workflow.meta?.quality_profile,
2420
- profileIsAuthoritative: true,
2421
- routeHint: 'adjust route/via, bias, or channel coordinates so each turn has a readable run-up'
2422
- }));
2423
-
2424
- if (Array.isArray(workflow.mainPath)) {
2425
- for (const id of workflow.mainPath) {
2426
- if (!nodes.has(id)) {
2427
- problems.push(`mainPath references unknown node "${id}".`);
2428
- }
2429
- }
2430
- for (let i = 0; i < workflow.mainPath.length - 1; i += 1) {
2431
- const fromId = workflow.mainPath[i];
2432
- const toId = workflow.mainPath[i + 1];
2433
- const from = nodes.get(fromId);
2434
- const to = nodes.get(toId);
2435
- if (!from || !to) continue;
2436
- const linked = workflow.edges.some((edge) => edge.from === fromId && edge.to === toId);
2437
- if (!linked) {
2438
- problems.push(`mainPath step "${fromId}" -> "${toId}" has no matching edge — add the edge or remove the pair from mainPath.`);
2439
- }
2440
- if (to.col < from.col) {
2441
- problems.push(`mainPath step "${fromId}" -> "${toId}" moves backward from col ${from.col} to ${to.col} — use a return edge outside mainPath for loops.`);
2442
- }
2443
- }
2444
- }
2445
-
2446
- const labelRects = [];
2447
- for (const [edgeIndex, edge] of workflow.edges.entries()) {
2448
- const labelRect = labelRectFor(edge, edgeIndex);
2449
- if (labelRect) labelRects.push(labelRect);
2450
- }
2451
- for (const rect of labelRects) {
2452
- for (const node of nodes.values()) {
2453
- if (rectsOverlap(rect, node, -2)) {
2454
- problems.push(`Label "${rect.label}" overlaps node "${node.id}" — adjust labelDx/labelDy/labelSegment or set labelAt.\n${suggestLabelObstacleFix(rect, rect.lx, rect.ly, node, 'node')}`);
2455
- }
2456
- }
2457
- }
2458
- for (let i = 0; i < labelRects.length; i += 1) {
2459
- for (let j = i + 1; j < labelRects.length; j += 1) {
2460
- if (rectsOverlap(labelRects[i], labelRects[j], -2)) {
2461
- problems.push(`Labels "${labelRects[i].label}" and "${labelRects[j].label}" overlap — adjust labelDx/labelDy/labelSegment or route one relationship through a separate corridor.\n${suggestLabelPairFix(labelRects[i], labelRects[j])}`);
2462
- }
2463
- }
2464
- }
2465
- problems.push(...cleanLabelRouteClearanceProblems({
2466
- relations: workflow.edges,
2467
- labels: labelRects,
2468
- endpointIds: new Set(nodes.keys()),
2469
- pathFor,
2470
- diagramType: 'workflow',
2471
- relationCollection: 'edges',
2472
- profile: workflow.meta?.quality_profile,
2473
- profileIsAuthoritative: true,
2474
- }));
2475
-
2476
- if (workflow.schema_version === 1) {
2477
- if (viewBox[0] < layout.laneX + layout.laneW + 16) {
2478
- problems.push(`viewBox width ${viewBox[0]} clips the ${layout.laneW}px lanes — set meta.viewBox[0] to at least ${layout.laneX + layout.laneW + 16}.`);
2479
- }
2480
- if (legendY() + 18 > viewBox[1]) {
2481
- problems.push(`Legend exceeds viewBox height ${viewBox[1]} — set meta.viewBox[1] to at least ${legendY() + 18}.`);
2482
- }
2483
- }
2484
-
2485
- if (problems.length) {
2486
- throwDiagnosticProblems('Workflow layout validation failed', problems, {
2487
- subject: { diagramType: 'workflow' },
2488
- });
2489
- }
2490
- }
2491
-
2492
- function validateReadableInputsBeforeRouting() {
2493
- if (workflow.schema_version !== 2) return;
2494
- const fail = (diagnostic) => throwDiagnosticError(diagnostic.message, [diagnostic]);
2495
- const unusedId = (base, used) => {
2496
- for (let suffix = 2; ; suffix += 1) {
2497
- const candidate = `${base}-${suffix}`;
2498
- if (!used.has(candidate)) return candidate;
2499
- }
2500
- };
2501
- const authoredLanes = [...workflow.lanes].sort((left, right) => (
2502
- sourceIndexes.lanes.get(left) - sourceIndexes.lanes.get(right)
2503
- ));
2504
- const authoredNodes = [...workflow.nodes].sort((left, right) => (
2505
- sourceIndexes.nodes.get(left) - sourceIndexes.nodes.get(right)
2506
- ));
2507
- const authoredEdges = [...workflow.edges].sort((left, right) => (
2508
- sourceIndexes.edges.get(left) - sourceIndexes.edges.get(right)
2509
- ));
2510
-
2511
- const firstLaneIndex = new Map();
2512
- for (const lane of authoredLanes) {
2513
- const laneIndex = sourceIndexes.lanes.get(lane);
2514
- if (firstLaneIndex.has(lane.id)) {
2515
- const message = `Workflow lane id "${lane.id}" is duplicated.`;
2516
- const replacement = unusedId(lane.id, new Set(workflow.lanes.map(({ id }) => id)));
2517
- const canonicalLaneIndex = workflow.lanes.indexOf(lane);
2518
- const supportedFixes = acceptsFix((document) => {
2519
- document.lanes[canonicalLaneIndex].id = replacement;
2520
- }) ? [`rename /lanes/${laneIndex}/id to verified unique id "${replacement}"`] : [];
2521
- fail({
2522
- code: 'workflow/duplicate-lane-id',
2523
- severity: 'error',
2524
- message,
2525
- subject: { diagramType: 'workflow', lane: lane.id, path: `/lanes/${laneIndex}/id` },
2526
- evidence: {
2527
- duplicateLaneId: lane.id,
2528
- firstPath: `/lanes/${firstLaneIndex.get(lane.id)}/id`,
2529
- duplicatePath: `/lanes/${laneIndex}/id`,
2530
- },
2531
- supportedFixes,
2532
- });
2533
- }
2534
- firstLaneIndex.set(lane.id, laneIndex);
2535
- }
2536
-
2537
- const firstNodeIndex = new Map();
2538
- for (const node of authoredNodes) {
2539
- const nodeIndex = sourceIndexes.nodes.get(node);
2540
- if (firstNodeIndex.has(node.id)) {
2541
- const message = `Workflow node id "${node.id}" is duplicated.`;
2542
- const replacement = unusedId(node.id, new Set(workflow.nodes.map(({ id }) => id)));
2543
- const canonicalNodeIndex = workflow.nodes.indexOf(node);
2544
- const supportedFixes = acceptsFix((document) => {
2545
- document.nodes[canonicalNodeIndex].id = replacement;
2546
- }) ? [`rename /nodes/${nodeIndex}/id to verified unique id "${replacement}"`] : [];
2547
- fail({
2548
- code: 'workflow/duplicate-node-id',
2549
- severity: 'error',
2550
- message,
2551
- subject: { diagramType: 'workflow', node: node.id, path: `/nodes/${nodeIndex}/id` },
2552
- evidence: {
2553
- duplicateNodeId: node.id,
2554
- firstPath: `/nodes/${firstNodeIndex.get(node.id)}/id`,
2555
- duplicatePath: `/nodes/${nodeIndex}/id`,
2556
- },
2557
- supportedFixes,
2558
- });
2559
- }
2560
- firstNodeIndex.set(node.id, nodeIndex);
2561
- }
2562
-
2563
- const availableNodeIds = [...nodes.keys()].sort(stableCompare);
2564
- for (const edge of authoredEdges) {
2565
- const edgeIndex = sourceIndexes.edges.get(edge);
2566
- for (const [field, endpoint] of [['from', 'source'], ['to', 'target']]) {
2567
- if (nodes.has(edge[field])) continue;
2568
- const message = `Workflow edge "${workflowEdgeName(edge)}" references unknown ${endpoint} "${edge[field]}".`;
2569
- const canonicalEdgeIndex = workflow.edges.indexOf(edge);
2570
- const supportedFixes = availableNodeIds.flatMap((nodeId) => (
2571
- acceptsFix((document) => {
2572
- document.edges[canonicalEdgeIndex][field] = nodeId;
2573
- })
2574
- ? [`set /edges/${edgeIndex}/${field} to verified node id "${nodeId}"`]
2575
- : []
2576
- ));
2577
- fail({
2578
- code: 'workflow/unknown-edge-endpoint',
2579
- severity: 'error',
2580
- message,
2581
- subject: {
2582
- diagramType: 'workflow',
2583
- edge: edge.id ?? null,
2584
- path: `/edges/${edgeIndex}/${field}`,
2585
- from: edge.from,
2586
- to: edge.to,
2587
- },
2588
- evidence: {
2589
- endpoint,
2590
- unknownNodeId: edge[field],
2591
- availableNodeIds,
2592
- },
2593
- supportedFixes,
2594
- });
2595
- }
2596
- }
2597
- const laneIds = new Set(workflow.lanes.map((lane) => lane.id));
2598
- const availableLaneIds = [...laneIds].sort(stableCompare);
2599
- const nodeSourceIndexes = new Map(authoredNodes.map((node) => [
2600
- node.id,
2601
- sourceIndexes.nodes.get(node),
2602
- ]));
2603
-
2604
- const byLane = new Map();
2605
- for (const authoredNode of authoredNodes) {
2606
- const nodeIndex = sourceIndexes.nodes.get(authoredNode);
2607
- const node = nodes.get(authoredNode.id);
2608
- if (!laneIds.has(node.lane)) {
2609
- const message = `Workflow node "${node.id}" uses unknown lane "${node.lane}".`;
2610
- const canonicalNodeIndex = workflow.nodes.findIndex((candidate) => candidate.id === node.id);
2611
- const supportedFixes = availableLaneIds.flatMap((laneId) => (
2612
- acceptsFix((document) => {
2613
- document.nodes[canonicalNodeIndex].lane = laneId;
2614
- })
2615
- ? [`set /nodes/${nodeIndex}/lane to verified lane id "${laneId}"`]
2616
- : []
2617
- ));
2618
- fail({
2619
- code: 'workflow/unknown-node-lane',
2620
- severity: 'error',
2621
- message,
2622
- subject: { diagramType: 'workflow', node: node.id, path: `/nodes/${nodeIndex}/lane` },
2623
- evidence: { unknownLaneId: node.lane, availableLaneIds },
2624
- supportedFixes,
2625
- });
2626
- }
2627
- if (!Number.isInteger(node.col) || node.col < 0 || node.col >= layout.colXs.length) {
2628
- const message = `Workflow node "${node.id}" uses column ${node.col}, but valid columns are integers 0..${layout.colXs.length - 1}.`;
2629
- const canonicalNodeIndex = workflow.nodes.findIndex((candidate) => candidate.id === node.id);
2630
- const supportedFixes = layout.colXs.flatMap((_x, col) => (
2631
- acceptsFix((document) => {
2632
- document.nodes[canonicalNodeIndex].col = col;
2633
- })
2634
- ? [`set /nodes/${nodeIndex}/col to verified column ${col}`]
2635
- : []
2636
- ));
2637
- fail({
2638
- code: 'workflow/invalid-node-column',
2639
- severity: 'error',
2640
- message,
2641
- subject: { diagramType: 'workflow', node: node.id, path: `/nodes/${nodeIndex}/col` },
2642
- evidence: { actualColumn: node.col, minimumColumn: 0, maximumColumn: layout.colXs.length - 1 },
2643
- supportedFixes,
2644
- });
2645
- }
2646
- if (!isFinitePoint(node.x, node.y, node.cx, node.cy)) {
2647
- const message = `Workflow node "${node.id}" produced non-finite coordinates.`;
2648
- fail({
2649
- code: 'workflow/non-finite-node-geometry',
2650
- severity: 'error',
2651
- message,
2652
- subject: { diagramType: 'workflow', node: node.id, path: `/nodes/${nodeIndex}` },
2653
- evidence: {
2654
- measuredRect: { x: node.x, y: node.y, width: node.width, height: node.height },
2655
- authored: {
2656
- col: authoredNode.col,
2657
- width: authoredNode.width ?? null,
2658
- height: authoredNode.height ?? null,
2659
- yOffset: authoredNode.yOffset ?? null,
2660
- },
2661
- },
2662
- supportedFixes: [],
2663
- });
2664
- }
2665
- byLane.set(node.lane, [...(byLane.get(node.lane) || []), node]);
2666
- }
2667
- for (const [lane, laneNodes] of byLane) {
2668
- for (let left = 0; left < laneNodes.length; left += 1) {
2669
- for (let right = left + 1; right < laneNodes.length; right += 1) {
2670
- if (rectsOverlap(laneNodes[left], laneNodes[right], 8)) {
2671
- const leftNode = laneNodes[left];
2672
- const rightNode = laneNodes[right];
2673
- const rightIndex = nodeSourceIndexes.get(rightNode.id);
2674
- const canonicalNodeIndex = workflow.nodes.findIndex((candidate) => (
2675
- candidate.id === rightNode.id
2676
- ));
2677
- const supportedFixes = layout.colXs.flatMap((_x, col) => {
2678
- if (col === rightNode.col) return [];
2679
- return acceptsFix((document) => {
2680
- document.nodes[canonicalNodeIndex].col = col;
2681
- })
2682
- ? [`set /nodes/${rightIndex}/col to verified free column ${col}`]
2683
- : [];
2684
- });
2685
- const message = `Workflow nodes "${leftNode.id}" and "${rightNode.id}" are less than 8px apart in lane "${lane}".`;
2686
- fail({
2687
- code: 'workflow/node-overlap',
2688
- severity: 'error',
2689
- message,
2690
- subject: { diagramType: 'workflow', node: rightNode.id, path: `/nodes/${rightIndex}` },
2691
- evidence: {
2692
- lane,
2693
- minimumClearancePx: 8,
2694
- nodes: [
2695
- { id: leftNode.id, rect: { x: leftNode.x, y: leftNode.y, width: leftNode.width, height: leftNode.height } },
2696
- { id: rightNode.id, rect: { x: rightNode.x, y: rightNode.y, width: rightNode.width, height: rightNode.height } },
2697
- ],
2698
- },
2699
- supportedFixes,
2700
- });
2701
- }
2702
- }
2703
- }
2704
- }
2705
- }
2706
-
2707
- function gapYBetween(fromLane, toLane, bias = 0.5) {
2708
- const a = laneTop(fromLane) + laneHeight(fromLane);
2709
- const b = laneTop(toLane);
2710
- return a + (b - a) * bias;
2711
- }
2712
-
2713
- function spanForCols(fromCol, toCol, pad = 46, minimumWidth = 0) {
2714
- const start = layout.colXs[fromCol] - pad;
2715
- const end = layout.colXs[toCol] + pad;
2716
- const width = Math.max(end - start, minimumWidth);
2717
- if (fromCol === toCol && width > end - start) {
2718
- return { x: start, width, cx: start + width / 2 };
2719
- }
2720
- const cx = (start + end) / 2;
2721
- return { x: cx - width / 2, width, cx };
2722
- }
2723
-
2724
- function phaseSpan(phase) {
2725
- return spanForCols(
2726
- phase.fromCol,
2727
- phase.toCol,
2728
- 46,
2729
- workflow.schema_version === 2 ? textUnits(phase.label) * 5.6 + 8 : 0,
2730
- );
2731
- }
2732
-
2733
- function groupSpan(group) {
2734
- if (workflow.schema_version === 2) {
2735
- return readableGroupBounds(workflow, group, layout.colXs);
2736
- }
2737
- return spanForCols(
2738
- group.fromCol,
2739
- group.toCol,
2740
- 50,
2741
- 0,
2742
- );
2743
- }
2744
-
2745
- function sameLaneAutoVia(start, end) {
2746
- if (start[0] === end[0] || start[1] === end[1]) return [];
2747
- const midX = (start[0] + end[0]) / 2;
2748
- return [[midX, start[1]], [midX, end[1]]];
2749
- }
2750
-
2751
- function routeClearsUnrelatedNodes(edge, points, clearance = 2) {
2752
- const endpointIds = new Set([edge.from, edge.to]);
2753
- for (const node of nodes.values()) {
2754
- if (endpointIds.has(node.id)) continue;
2755
- for (let index = 0; index < points.length - 1; index += 1) {
2756
- if (segmentIntersectsRect({ start: points[index], end: points[index + 1] }, node, clearance)) {
2757
- return false;
2758
- }
2759
- }
2760
- }
2761
- return true;
2762
- }
2763
-
2764
- function firstRouteNodeCollision(edge, points) {
2765
- const lastSegment = points.length - 2;
2766
- for (const node of nodes.values()) {
2767
- const endpointRole = node.id === edge.from
2768
- ? 'source-endpoint'
2769
- : node.id === edge.to ? 'target-endpoint' : 'unrelated';
2770
- for (let segmentIndex = 0; segmentIndex <= lastSegment; segmentIndex += 1) {
2771
- if (endpointRole === 'source-endpoint' && segmentIndex === 0) continue;
2772
- if (endpointRole === 'target-endpoint' && segmentIndex === lastSegment) continue;
2773
- const clearancePx = endpointRole === 'unrelated' ? 2 : 0;
2774
- const from = points[segmentIndex];
2775
- const to = points[segmentIndex + 1];
2776
- if (segmentIntersectsRect({ start: from, end: to }, node, clearancePx)) {
2777
- return {
2778
- obstacleNode: node.id,
2779
- obstacleRole: endpointRole,
2780
- segmentIndex,
2781
- from: [...from],
2782
- to: [...to],
2783
- clearancePx,
2784
- };
2785
- }
2786
- }
2787
- }
2788
- return null;
2789
- }
2790
-
2791
- function oneBendCrossLaneVia(edge, start, end, fromSide, toSide) {
2792
- const fromVertical = fromSide === 'top' || fromSide === 'bottom';
2793
- const toVertical = toSide === 'top' || toSide === 'bottom';
2794
- if (fromVertical === toVertical) return null;
2795
-
2796
- const corner = fromVertical ? [start[0], end[1]] : [end[0], start[1]];
2797
- const points = normalizeRoutePoints([start, corner, end]);
2798
- if (points.length !== 3 || !routeHonorsEndpointSides(points, fromSide, toSide)) return null;
2799
-
2800
- const segmentsAreReadable = points.slice(0, -1).every((point, index) => (
2801
- Math.hypot(
2802
- points[index + 1][0] - point[0],
2803
- points[index + 1][1] - point[1],
2804
- ) >= 8
2805
- ));
2806
- if (!segmentsAreReadable || !routeClearsUnrelatedNodes(edge, points)) return null;
2807
- return points.slice(1, -1);
2808
- }
2809
-
2810
- const pathCache = new Map();
2811
- const readableSideCache = new Map();
2812
-
2813
- function legacyAutomaticOneBendSides(edge, from, to) {
2814
- const automaticRoute = !edge.via && (!edge.route || edge.route === 'auto');
2815
- const automaticFrom = !edge.fromSide || edge.fromSide === 'auto';
2816
- const automaticTo = !edge.toSide || edge.toSide === 'auto';
2817
- if (!automaticRoute || !automaticFrom || !automaticTo || from.lane === to.lane) return null;
2818
- if (from.cx === to.cx || from.cy === to.cy) return null;
2819
- const verticalFrom = to.cy < from.cy ? 'top' : 'bottom';
2820
- const horizontalTo = to.cx < from.cx ? 'right' : 'left';
2821
- const horizontalFrom = to.cx < from.cx ? 'left' : 'right';
2822
- const verticalTo = to.cy < from.cy ? 'bottom' : 'top';
2823
- const candidates = [
2824
- { fromSide: verticalFrom, toSide: horizontalTo },
2825
- { fromSide: horizontalFrom, toSide: verticalTo },
2826
- ];
2827
-
2828
- return candidates.find(({ fromSide, toSide }) => {
2829
- const start = anchor(from, fromSide);
2830
- const end = anchor(to, toSide);
2831
- return oneBendCrossLaneVia(edge, start, end, fromSide, toSide);
2832
- }) || null;
2833
- }
2834
-
2835
- function readableAutomaticSides(edge, from, to) {
2836
- const automaticRoute = !edge.via
2837
- && edge.channelX === undefined
2838
- && edge.channelY === undefined
2839
- && (!edge.route || edge.route === 'auto');
2840
- const authoredFrom = edge.fromSide && edge.fromSide !== 'auto' ? edge.fromSide : null;
2841
- const authoredTo = edge.toSide && edge.toSide !== 'auto' ? edge.toSide : null;
2842
- if (!automaticRoute || (authoredFrom && authoredTo)) return null;
2843
- if (readableSideCache.has(edge)) return readableSideCache.get(edge);
2844
-
2845
- const preferred = [];
2846
- const legacyPreferred = legacyAutomaticOneBendSides(edge, from, to);
2847
- if (legacyPreferred) preferred.push(legacyPreferred);
2848
- preferred.push({
2849
- fromSide: authoredFrom || defaultFromSide(from, to),
2850
- toSide: authoredTo || defaultToSide(from, to),
2851
- });
2852
- const sideOrder = ['right', 'bottom', 'left', 'top'];
2853
- for (const fromSide of authoredFrom ? [authoredFrom] : sideOrder) {
2854
- for (const toSide of authoredTo ? [authoredTo] : sideOrder) {
2855
- preferred.push({ fromSide, toSide });
2856
- }
2857
- }
2858
-
2859
- const seen = new Set();
2860
- const sidePairs = [];
2861
- for (const candidate of preferred) {
2862
- if (authoredFrom && candidate.fromSide !== authoredFrom) continue;
2863
- if (authoredTo && candidate.toSide !== authoredTo) continue;
2864
- const key = `${candidate.fromSide}:${candidate.toSide}`;
2865
- if (seen.has(key)) continue;
2866
- seen.add(key);
2867
- sidePairs.push(candidate);
2868
- }
2869
-
2870
- const naturalFromSide = authoredFrom || defaultFromSide(from, to);
2871
- const naturalToSide = authoredTo || defaultToSide(from, to);
2872
- const planFor = (candidate, pairOrdinal) => {
2873
- const start = anchor(from, candidate.fromSide);
2874
- const end = anchor(to, candidate.toSide);
2875
- return {
2876
- start,
2877
- end,
2878
- planned: readableAutomaticCandidateSet(
2879
- edge,
2880
- from,
2881
- to,
2882
- start,
2883
- end,
2884
- candidate.fromSide,
2885
- candidate.toSide,
2886
- {
2887
- ordinalOffset: pairOrdinal * 9,
2888
- naturalFromSide,
2889
- naturalToSide,
2890
- },
2891
- ),
2892
- };
2893
- };
2894
-
2895
- const primary = sidePairs[0];
2896
- if (primary) {
2897
- const { planned } = planFor(primary, 0);
2898
- if (planned.candidates.length) {
2899
- readableSideCache.set(edge, primary);
2900
- return primary;
2901
- }
2902
- }
2903
-
2904
- const candidates = [];
2905
- for (const [pairOrdinal, candidate] of sidePairs.entries()) {
2906
- const { planned } = planFor(candidate, pairOrdinal);
2907
- candidates.push(...planned.candidates.map((route) => ({ ...route, ...candidate })));
2908
- }
2909
- candidates.sort((left, right) => compareCost(left.cost, right.cost));
2910
- if (candidates.length) {
2911
- const selected = {
2912
- fromSide: candidates[0].fromSide,
2913
- toSide: candidates[0].toSide,
2914
- };
2915
- readableSideCache.set(edge, selected);
2916
- return selected;
2917
- }
2918
- readableSideCache.set(edge, null);
2919
- return null;
2920
- }
2921
-
2922
- function automaticOneBendSides(edge, from, to) {
2923
- return workflow.schema_version === 2
2924
- ? readableAutomaticSides(edge, from, to)
2925
- : legacyAutomaticOneBendSides(edge, from, to);
2926
- }
2927
-
2928
- const OUTWARD_SIDE_VECTOR = Object.freeze({
2929
- left: [-1, 0],
2930
- right: [1, 0],
2931
- top: [0, -1],
2932
- bottom: [0, 1],
2933
- });
2934
-
2935
- function outwardStub(point, side, distance = 16) {
2936
- const [dx, dy] = OUTWARD_SIDE_VECTOR[side] || [0, 0];
2937
- return [point[0] + dx * distance, point[1] + dy * distance];
2938
- }
2939
-
2940
- function orthogonalRoute(points) {
2941
- return points.every((point, index) => {
2942
- if (!Array.isArray(point) || point.length !== 2 || !isFinitePoint(...point)) return false;
2943
- if (index === 0) return true;
2944
- const previous = points[index - 1];
2945
- const dx = Math.abs(point[0] - previous[0]);
2946
- const dy = Math.abs(point[1] - previous[1]);
2947
- return (dx <= 0.0001) !== (dy <= 0.0001);
2948
- });
2949
- }
2950
-
2951
- function routeClearsEndpointNodes(points, from, to) {
2952
- const lastSegment = points.length - 2;
2953
- for (let index = 0; index <= lastSegment; index += 1) {
2954
- const segment = { start: points[index], end: points[index + 1] };
2955
- if (index > 0 && segmentIntersectsRect(segment, from)) return false;
2956
- if (index < lastSegment && segmentIntersectsRect(segment, to)) return false;
2957
- }
2958
- return true;
2959
- }
2960
-
2961
- function routeMeetsHardRhythm(points) {
2962
- if (points.length === 2) {
2963
- return Math.hypot(points[1][0] - points[0][0], points[1][1] - points[0][1]) + 0.0001 >= 28;
2964
- }
2965
- return points.slice(0, -1).every((point, index) => {
2966
- const length = Math.abs(points[index + 1][0] - point[0]) + Math.abs(points[index + 1][1] - point[1]);
2967
- const endpoint = index === 0 || index === points.length - 2;
2968
- return length + 0.0001 >= (endpoint ? 8 : 16);
2969
- });
2970
- }
2971
-
2972
- function routeLabelClearsNodes(edge, points) {
2973
- if (!edge.label || edge.labelAt) return true;
2974
- const [lx, ly] = workflowEdgeLabelPoint(edge, points);
2975
- const rect = {
2976
- x: lx - workflowLabelWidth(edge.label) / 2,
2977
- y: ly - 10,
2978
- width: workflowLabelWidth(edge.label),
2979
- height: 14,
2980
- };
2981
- return [...nodes.values()].every((node) => !rectsOverlap(rect, node, -2));
2982
- }
2983
-
2984
- function candidateLabelRect(edge, points) {
2985
- if (!edge.label) return null;
2986
- const [lx, ly] = workflowEdgeLabelPoint(edge, points);
2987
- const width = workflowLabelWidth(edge.label);
2988
- return { x: lx - width / 2, y: ly - 10, width, height: 14 };
2989
- }
2990
-
2991
- function labelRouteClearanceDeficit(edge, points, threshold = 8) {
2992
- const candidateLabel = candidateLabelRect(edge, points);
2993
- let deficit = 0;
2994
- for (const [otherEdge, routed] of pathCache) {
2995
- const otherIndex = workflow.edges.indexOf(otherEdge);
2996
- const otherLabel = labelRectFor(otherEdge, otherIndex);
2997
- if (candidateLabel) {
2998
- for (let index = 0; index < routed.points.length - 1; index += 1) {
2999
- const clearance = segmentRectClearance({
3000
- start: routed.points[index],
3001
- end: routed.points[index + 1],
3002
- }, candidateLabel);
3003
- if (clearance != null) deficit += Math.max(0, threshold - clearance);
3004
- }
3005
- }
3006
- if (otherLabel) {
3007
- for (let index = 0; index < points.length - 1; index += 1) {
3008
- const clearance = segmentRectClearance({
3009
- start: points[index],
3010
- end: points[index + 1],
3011
- }, otherLabel);
3012
- if (clearance != null) deficit += Math.max(0, threshold - clearance);
3013
- }
3014
- }
3015
- }
3016
- return deficit;
3017
- }
3018
-
3019
- function routeClearsPlacedLabels(edge, points) {
3020
- const candidateLabel = candidateLabelRect(edge, points);
3021
- for (const [otherEdge, routed] of pathCache) {
3022
- const otherIndex = workflow.edges.indexOf(otherEdge);
3023
- const otherLabel = labelRectFor(otherEdge, otherIndex);
3024
- if (candidateLabel && otherLabel && rectsOverlap(candidateLabel, otherLabel, -2)) return false;
3025
- if (candidateLabel) {
3026
- for (let index = 0; index < routed.points.length - 1; index += 1) {
3027
- const clearance = segmentRectClearance({
3028
- start: routed.points[index],
3029
- end: routed.points[index + 1],
3030
- }, candidateLabel);
3031
- if (clearance != null && clearance + 0.0001 < 4) return false;
3032
- }
3033
- }
3034
- if (otherLabel) {
3035
- for (let index = 0; index < points.length - 1; index += 1) {
3036
- const clearance = segmentRectClearance({
3037
- start: points[index],
3038
- end: points[index + 1],
3039
- }, otherLabel);
3040
- if (clearance != null && clearance + 0.0001 < 4) return false;
3041
- }
3042
- }
3043
- }
3044
- return true;
3045
- }
3046
-
3047
- function routeClearsLegend(edge, points) {
3048
- if (!workflowLegendEntries.length) return true;
3049
- const legendRects = workflowLegendRects();
3050
- for (const rect of legendRects) {
3051
- for (let index = 0; index < points.length - 1; index += 1) {
3052
- if (segmentIntersectsRect({ start: points[index], end: points[index + 1] }, rect)) return false;
3053
- }
3054
- const label = candidateLabelRect(edge, points);
3055
- if (label && rectsOverlap(label, rect)) return false;
3056
- }
3057
- return true;
3058
- }
3059
-
3060
- function routeClearsSceneLabelObstacles(edge, points) {
3061
- const label = candidateLabelRect(edge, points);
3062
- for (const obstacle of workflowSceneLabelObstacles()) {
3063
- for (let index = 0; index < points.length - 1; index += 1) {
3064
- if (segmentIntersectsRect({ start: points[index], end: points[index + 1] }, obstacle)) {
3065
- return false;
3066
- }
3067
- }
3068
- if (label && rectsOverlap(label, obstacle)) return false;
3069
- }
3070
- return true;
3071
- }
3072
-
3073
- function routeClearsFrameBorders(points) {
3074
- return collectBorderRuns({
3075
- routedRelations: [{ points }],
3076
- frames: workflowCompositionFrames(),
3077
- }).length === 0;
3078
- }
3079
-
3080
- function routeExtentCoordinates(edge, points) {
3081
- const coordinates = [...points];
3082
- if (!edge.labelAt) {
3083
- const label = candidateLabelRect(edge, points);
3084
- if (label) {
3085
- coordinates.push([label.x, label.y], [label.x + label.width, label.y + label.height]);
3086
- }
3087
- }
3088
- return coordinates;
3089
- }
3090
-
3091
- function routeFitsCanvasOrigin(edge, points) {
3092
- return routeExtentCoordinates(edge, points).every(([x, y]) => x >= 0 && y >= 0);
3093
- }
3094
-
3095
- function readableCandidateIsFeasible(edge, points, from, to, fromSide, toSide) {
3096
- return points.length >= 2
3097
- && orthogonalRoute(points)
3098
- && routeHonorsEndpointSides(points, fromSide, toSide)
3099
- && routeMeetsHardRhythm(points)
3100
- && routeClearsEndpointNodes(points, from, to)
3101
- && routeClearsUnrelatedNodes(edge, points)
3102
- && routeLabelClearsNodes(edge, points)
3103
- && routeClearsPlacedLabels(edge, points)
3104
- && routeClearsLegend(edge, points)
3105
- && routeClearsSceneLabelObstacles(edge, points)
3106
- && routeClearsFrameBorders(points)
3107
- && routeFitsCanvasOrigin(edge, points);
3108
- }
3109
-
3110
- function corridorViaY(start, end, fromSide, toSide, y) {
3111
- const startStub = outwardStub(start, fromSide);
3112
- const endStub = outwardStub(end, toSide);
3113
- return [startStub, [startStub[0], y], [endStub[0], y], endStub];
3114
- }
3115
-
3116
- function corridorViaX(start, end, fromSide, toSide, x) {
3117
- const startStub = outwardStub(start, fromSide);
3118
- const endStub = outwardStub(end, toSide);
3119
- return [startStub, [x, startStub[1]], [x, endStub[1]], endStub];
3120
- }
3121
-
3122
- function axisOverlapLength(a, b, c, d) {
3123
- const horizontal = Math.abs(a[1] - b[1]) <= 0.0001
3124
- && Math.abs(c[1] - d[1]) <= 0.0001
3125
- && Math.abs(a[1] - c[1]) <= 0.0001;
3126
- const vertical = Math.abs(a[0] - b[0]) <= 0.0001
3127
- && Math.abs(c[0] - d[0]) <= 0.0001
3128
- && Math.abs(a[0] - c[0]) <= 0.0001;
3129
- if (!horizontal && !vertical) return 0;
3130
- const axis = horizontal ? 0 : 1;
3131
- return Math.max(0, Math.min(Math.max(a[axis], b[axis]), Math.max(c[axis], d[axis]))
3132
- - Math.max(Math.min(a[axis], b[axis]), Math.min(c[axis], d[axis])));
3133
- }
3134
-
3135
- function properAxisCrossing(a, b, c, d) {
3136
- const firstHorizontal = Math.abs(a[1] - b[1]) <= 0.0001;
3137
- const secondHorizontal = Math.abs(c[1] - d[1]) <= 0.0001;
3138
- if (firstHorizontal === secondHorizontal) return false;
3139
- const horizontal = firstHorizontal ? [a, b] : [c, d];
3140
- const vertical = firstHorizontal ? [c, d] : [a, b];
3141
- const x = vertical[0][0];
3142
- const y = horizontal[0][1];
3143
- return x > Math.min(horizontal[0][0], horizontal[1][0]) + 0.0001
3144
- && x < Math.max(horizontal[0][0], horizontal[1][0]) - 0.0001
3145
- && y > Math.min(vertical[0][1], vertical[1][1]) + 0.0001
3146
- && y < Math.max(vertical[0][1], vertical[1][1]) - 0.0001;
3147
- }
3148
-
3149
- function routeInteractionMetrics(edge, points) {
3150
- let properCrossingCount = 0;
3151
- let sharedCorridorPx = 0;
3152
- for (const [otherEdge, routed] of pathCache) {
3153
- if ([edge.from, edge.to].some((id) => id === otherEdge.from || id === otherEdge.to)) continue;
3154
- for (let left = 0; left < points.length - 1; left += 1) {
3155
- for (let right = 0; right < routed.points.length - 1; right += 1) {
3156
- if (properAxisCrossing(points[left], points[left + 1], routed.points[right], routed.points[right + 1])) {
3157
- properCrossingCount += 1;
3158
- }
3159
- sharedCorridorPx += axisOverlapLength(
3160
- points[left], points[left + 1], routed.points[right], routed.points[right + 1],
3161
- );
3162
- }
3163
- }
3164
- }
3165
- return { properCrossingCount, sharedCorridorPx };
3166
- }
3167
-
3168
- function automaticForwardReversePx(edge, points) {
3169
- const from = nodes.get(edge.from);
3170
- const to = nodes.get(edge.to);
3171
- if (!from || !to || ['return', 'error'].includes(edge.role) || to.col <= from.col) return 0;
3172
- return points.slice(0, -1).reduce((total, point, index) => (
3173
- total + Math.max(0, point[0] - points[index + 1][0])
3174
- ), 0);
3175
- }
3176
-
3177
- function readableCandidateCost(
3178
- edge,
3179
- points,
3180
- ordinal,
3181
- naturalFromSide,
3182
- naturalToSide,
3183
- ) {
3184
- const interaction = routeInteractionMetrics(edge, points);
3185
- const segmentLengths = points.slice(0, -1).map((point, index) => (
3186
- Math.abs(points[index + 1][0] - point[0]) + Math.abs(points[index + 1][1] - point[1])
3187
- ));
3188
- const routeLength = segmentLengths.reduce((total, length) => total + length, 0);
3189
- const directLength = Math.abs(points.at(-1)[0] - points[0][0]) + Math.abs(points.at(-1)[1] - points[0][1]);
3190
- const interiorPreferred28Deficit = segmentLengths.slice(1, -1)
3191
- .reduce((total, length) => total + Math.max(0, 28 - length), 0);
3192
- const xs = points.map(([x]) => x);
3193
- const ys = points.map(([, y]) => y);
3194
- const canvasGrowthPx = Math.max(0, -Math.min(...xs))
3195
- + Math.max(0, Math.max(...xs) - minimumCanvasWidth)
3196
- + Math.max(0, -Math.min(...ys))
3197
- + Math.max(0, Math.max(...ys) - autoHeight);
3198
- const from = nodes.get(edge.from);
3199
- const to = nodes.get(edge.to);
3200
- const naturalStart = anchor(from, naturalFromSide);
3201
- const naturalEnd = anchor(to, naturalToSide);
3202
- const portDisplacementPx = Math.abs(points[0][0] - naturalStart[0])
3203
- + Math.abs(points[0][1] - naturalStart[1])
3204
- + Math.abs(points.at(-1)[0] - naturalEnd[0])
3205
- + Math.abs(points.at(-1)[1] - naturalEnd[1]);
3206
- const legacyCoordinateDisplacement = Math.abs(from.cx - LEGACY_COLUMN_CENTERS[from.col])
3207
- + Math.abs(to.cx - LEGACY_COLUMN_CENTERS[to.col]);
3208
- return {
3209
- automaticForwardReversePx: automaticForwardReversePx(edge, points),
3210
- properCrossingCount: interaction.properCrossingCount,
3211
- sharedCorridorPx: interaction.sharedCorridorPx,
3212
- labelRouteClearanceDeficit: labelRouteClearanceDeficit(edge, points),
3213
- interiorPreferred28Deficit,
3214
- bendCount: Math.max(0, points.length - 2),
3215
- stretchMilli: Math.round((directLength > 0 ? routeLength / directLength : 1) * 1000),
3216
- canvasGrowthPx,
3217
- portDisplacementMilli: Math.round(portDisplacementPx * 1000),
3218
- legacyCoordinateDisplacement,
3219
- stableCandidateOrdinal: ordinal,
3220
- };
3221
- }
3222
-
3223
- function compareCost(left, right) {
3224
- for (const dimension of READABLE_CANDIDATE_COST_PRIORITY) {
3225
- if ((left[dimension] || 0) !== (right[dimension] || 0)) {
3226
- return (left[dimension] || 0) - (right[dimension] || 0);
3227
- }
3228
- }
3229
- return 0;
3230
- }
3231
-
3232
- function readableAutomaticCandidateSet(
3233
- edge,
3234
- from,
3235
- to,
3236
- start,
3237
- end,
3238
- fromSide,
3239
- toSide,
3240
- {
3241
- ordinalOffset = 0,
3242
- naturalFromSide = fromSide,
3243
- naturalToSide = toSide,
3244
- } = {},
3245
- ) {
3246
- const midX = (start[0] + end[0]) / 2;
3247
- const laneGapY = from.lane === to.lane
3248
- ? laneTop(from.lane) - 16
3249
- : gapYBetween(from.lane, to.lane, edge.bias ?? 0.5);
3250
- const topY = Math.max(8, Math.min(laneTop(from.lane), laneTop(to.lane)) - 16);
3251
- const bottomY = Math.max(
3252
- laneTop(from.lane) + laneHeight(from.lane),
3253
- laneTop(to.lane) + laneHeight(to.lane),
3254
- ) + 16;
3255
- const outsideLeft = layout.laneX - 20;
3256
- const outsideRight = layout.laneX + layout.laneW + 12;
3257
- const rawCandidates = [
3258
- { family: 'facing-straight', via: [] },
3259
- { family: 'horizontal-then-vertical', via: [[end[0], start[1]]] },
3260
- { family: 'vertical-then-horizontal', via: [[start[0], end[1]]] },
3261
- { family: 'lane-gap-corridor', via: corridorViaY(start, end, fromSide, toSide, laneGapY) },
3262
- { family: 'column-gap-corridor', via: corridorViaX(start, end, fromSide, toSide, midX) },
3263
- { family: 'outside-left', via: corridorViaX(start, end, fromSide, toSide, outsideLeft) },
3264
- { family: 'outside-right', via: corridorViaX(start, end, fromSide, toSide, outsideRight) },
3265
- { family: 'top-corridor', via: corridorViaY(start, end, fromSide, toSide, topY) },
3266
- { family: 'bottom-corridor', via: corridorViaY(start, end, fromSide, toSide, bottomY) },
3267
- ];
3268
- const candidates = rawCandidates.map((candidate, ordinal) => ({
3269
- ...candidate,
3270
- ordinal: ordinalOffset + ordinal,
3271
- points: normalizeRoutePoints([start, ...candidate.via, end]),
3272
- })).filter(({ points }) => (
3273
- readableCandidateIsFeasible(edge, points, from, to, fromSide, toSide)
3274
- )).map((candidate) => ({
3275
- ...candidate,
3276
- cost: readableCandidateCost(
3277
- edge,
3278
- candidate.points,
3279
- candidate.ordinal,
3280
- naturalFromSide,
3281
- naturalToSide,
3282
- ),
3283
- })).sort((left, right) => compareCost(left.cost, right.cost));
3284
- return { rawCandidates, candidates, outsideRight };
3285
- }
3286
-
3287
- function readableAutomaticVia(edge, from, to, start, end, fromSide, toSide) {
3288
- const { rawCandidates, candidates, outsideRight } = readableAutomaticCandidateSet(
3289
- edge,
3290
- from,
3291
- to,
3292
- start,
3293
- end,
3294
- fromSide,
3295
- toSide,
3296
- );
3297
-
3298
- if (candidates.length) return candidates[0].points.slice(1, -1);
3299
- const outsideRightCandidate = rawCandidates.find(({ family }) => family === 'outside-right');
3300
- if (outsideRightCandidate) {
3301
- const currentPoints = normalizeRoutePoints([start, ...outsideRightCandidate.via, end]);
3302
- const labelRect = candidateLabelRect(edge, currentPoints);
3303
- let outsideRightMinX = outsideRight;
3304
- for (const node of nodes.values()) {
3305
- if (!labelRect || !rectsOverlap(labelRect, node, -2)) continue;
3306
- const rightwardLabelDeficit = node.x + node.width - 2 - labelRect.x;
3307
- if (rightwardLabelDeficit > 0) {
3308
- outsideRightMinX = Math.max(
3309
- outsideRightMinX,
3310
- outsideRight + rightwardLabelDeficit * 2,
3311
- );
3312
- }
3313
- }
3314
- for (const [otherEdge, routed] of pathCache) {
3315
- const otherIndex = workflow.edges.indexOf(otherEdge);
3316
- const otherLabel = labelRectFor(otherEdge, otherIndex);
3317
- if (labelRect && otherLabel && rectsOverlap(labelRect, otherLabel, -2)) {
3318
- const rightwardLabelDeficit = otherLabel.x + otherLabel.width - 2 - labelRect.x;
3319
- if (rightwardLabelDeficit > 0) {
3320
- outsideRightMinX = Math.max(
3321
- outsideRightMinX,
3322
- outsideRight + rightwardLabelDeficit * 2,
3323
- );
3324
- }
3325
- }
3326
- if (!labelRect) continue;
3327
- for (let index = 0; index < routed.points.length - 1; index += 1) {
3328
- const segment = {
3329
- start: routed.points[index],
3330
- end: routed.points[index + 1],
3331
- };
3332
- const clearance = segmentRectClearance(segment, labelRect);
3333
- if (clearance == null || clearance + 0.0001 >= 4) continue;
3334
- const rightwardLabelDeficit = Math.max(segment.start[0], segment.end[0])
3335
- + 4 - labelRect.x;
3336
- if (rightwardLabelDeficit > 0) {
3337
- outsideRightMinX = Math.max(
3338
- outsideRightMinX,
3339
- outsideRight + rightwardLabelDeficit * 2,
3340
- );
3341
- }
3342
- }
3343
- }
3344
- outsideRightMinX = Math.ceil(outsideRightMinX * 1000) / 1000;
3345
- let rightmostPlacedX = outsideRight;
3346
- for (const node of nodes.values()) {
3347
- rightmostPlacedX = Math.max(rightmostPlacedX, node.x + node.width);
3348
- }
3349
- for (const [otherEdge, routed] of pathCache) {
3350
- for (const [x] of routed.points) rightmostPlacedX = Math.max(rightmostPlacedX, x);
3351
- const otherLabel = labelRectFor(otherEdge, workflow.edges.indexOf(otherEdge));
3352
- if (otherLabel) {
3353
- rightmostPlacedX = Math.max(rightmostPlacedX, otherLabel.x + otherLabel.width);
3354
- }
3355
- }
3356
- let probeGrowth = Math.max(
3357
- 32,
3358
- labelRect?.width ?? 0,
3359
- rightmostPlacedX + 16 - outsideRightMinX,
3360
- );
3361
- let lastInfeasibleX = outsideRight;
3362
- for (let probe = 0; probe < 7; probe += 1) {
3363
- if (outsideRightMinX > outsideRight + 0.0001) {
3364
- const expandedPoints = normalizeRoutePoints([
3365
- start,
3366
- ...corridorViaX(start, end, fromSide, toSide, outsideRightMinX),
3367
- end,
3368
- ]);
3369
- if (readableCandidateIsFeasible(edge, expandedPoints, from, to, fromSide, toSide)) {
3370
- let feasibleX = outsideRightMinX;
3371
- let feasiblePoints = expandedPoints;
3372
- let infeasibleX = lastInfeasibleX;
3373
- for (let refinement = 0;
3374
- refinement < 53 && feasibleX - infeasibleX > 0.001;
3375
- refinement += 1) {
3376
- const midpointX = Math.ceil(((infeasibleX + feasibleX) / 2) * 1000) / 1000;
3377
- if (midpointX >= feasibleX - 0.0001) break;
3378
- const midpointPoints = normalizeRoutePoints([
3379
- start,
3380
- ...corridorViaX(start, end, fromSide, toSide, midpointX),
3381
- end,
3382
- ]);
3383
- if (readableCandidateIsFeasible(
3384
- edge,
3385
- midpointPoints,
3386
- from,
3387
- to,
3388
- fromSide,
3389
- toSide,
3390
- )) {
3391
- feasibleX = midpointX;
3392
- feasiblePoints = midpointPoints;
3393
- } else {
3394
- infeasibleX = midpointX;
3395
- }
3396
- }
3397
- return feasiblePoints.slice(1, -1);
3398
- }
3399
- lastInfeasibleX = outsideRightMinX;
3400
- }
3401
- outsideRightMinX = Math.ceil((outsideRightMinX + probeGrowth) * 1000) / 1000;
3402
- probeGrowth *= 2;
3403
- }
3404
- }
3405
- const hasRelevantAbsolutePin = Array.isArray(edge.labelAt)
3406
- || [...pathCache.keys()].some((otherEdge) => (
3407
- Array.isArray(otherEdge.labelAt) || hasAbsoluteRoutePins(otherEdge)
3408
- ));
3409
- if (hasRelevantAbsolutePin) classifyFailedAutomaticCandidatePins(edge, rawCandidates);
3410
- const horizontallyFacing = (
3411
- fromSide === 'right' && toSide === 'left' && end[0] > start[0]
3412
- ) || (
3413
- fromSide === 'left' && toSide === 'right' && start[0] > end[0]
3414
- );
3415
- if (horizontallyFacing && from.col !== to.col) {
3416
- const fromCol = Math.min(from.col, to.col);
3417
- const toCol = Math.max(from.col, to.col);
3418
- const requiredRankGap = from.width / 2 + 32 + to.width / 2;
3419
- const actualRankGap = layout.colXs[toCol] - layout.colXs[fromCol];
3420
- if (actualRankGap + 0.0001 < requiredRankGap) {
3421
- throw new WorkflowLayoutFeedback({
3422
- kind: 'rank-gap-minimum',
3423
- fromCol,
3424
- toCol,
3425
- minimum: Math.ceil(requiredRankGap * 1000) / 1000,
3426
- edge: edge.id ?? null,
3427
- from: edge.from,
3428
- to: edge.to,
3429
- attemptedCandidateFamilies: rawCandidates.map(({ family }) => family),
3430
- candidateCount: rawCandidates.length,
3431
- });
3432
- }
3433
- }
3434
- if (from.lane !== to.lane && layout.laneGap < 32) {
3435
- throw new WorkflowLayoutFeedback({
3436
- kind: 'lane-gap-minimum',
3437
- minimum: 32,
3438
- edge: edge.id ?? null,
3439
- from: edge.from,
3440
- to: edge.to,
3441
- attemptedCandidateFamilies: rawCandidates.map(({ family }) => family),
3442
- candidateCount: rawCandidates.length,
3443
- });
3444
- }
3445
- const message = `Workflow edge "${workflowEdgeName(edge)}" has no feasible readable-v2 automatic route.`;
3446
- throwDiagnosticError(message, [{
3447
- code: 'workflow/solver-budget-exhausted',
3448
- severity: 'error',
3449
- message,
3450
- subject: {
3451
- diagramType: 'workflow',
3452
- edge: edge.id ?? null,
3453
- from: edge.from,
3454
- to: edge.to,
3455
- },
3456
- evidence: {
3457
- attemptedCandidateFamilies: rawCandidates.map(({ family }) => family),
3458
- candidateCount: rawCandidates.length,
3459
- },
3460
- supportedFixes: [],
3461
- }]);
3462
- }
3463
-
3464
- function readablePresetVia(edge, from, to, start, end, fromSide, toSide) {
3465
- const preset = edge.route;
3466
- let via;
3467
- switch (preset) {
3468
- case 'straight':
3469
- via = [];
3470
- break;
3471
- case 'drop': {
3472
- const y = gapYBetween(from.lane, to.lane, edge.bias ?? 0.5);
3473
- via = [[start[0], y], [end[0], y]];
3474
- break;
3475
- }
3476
- case 'outside-right': {
3477
- const x = layout.laneX + layout.laneW + 12;
3478
- via = [[x, start[1]], [x, end[1]]];
3479
- break;
3480
- }
3481
- case 'return-left': {
3482
- const x = Math.min(from.x, to.x) - 28;
3483
- via = [[x, start[1]], [x, end[1]]];
3484
- break;
3485
- }
3486
- case 'bottom-channel': {
3487
- const y = Math.max(from.y + from.height, to.y + to.height) + 32;
3488
- via = [[start[0], y], [end[0], y]];
3489
- break;
3490
- }
3491
- case 'up-channel': {
3492
- const y = Math.min(from.y, to.y) - 28;
3493
- via = [[start[0], y], [end[0], y]];
3494
- break;
3495
- }
3496
- default:
3497
- return readableAutomaticVia(edge, from, to, start, end, fromSide, toSide);
3498
- }
3499
- const points = normalizeRoutePoints([start, ...via, end]);
3500
- if (readableCandidateIsFeasible(edge, points, from, to, fromSide, toSide)
3501
- && routeMatchesPresetFamily(preset, points, from, to)) {
3502
- return points.slice(1, -1);
3503
- }
3504
- const message = `Workflow edge "${workflowEdgeName(edge)}" cannot satisfy route preset "${preset}" under readable-v2 constraints (minimum 8px endpoint stubs, 16px interior turns, and 28px direct clearance).`;
3505
- const edgeIndex = workflow.edges.indexOf(edge);
3506
- const edgeName = workflowEdgeName(edge);
3507
- const supportedFixes = [];
3508
- for (const candidatePreset of ['straight', 'drop', 'outside-right', 'return-left', 'bottom-channel', 'up-channel']) {
3509
- if (candidatePreset === preset) continue;
3510
- if (acceptsFix((document) => {
3511
- document.edges[edgeIndex].route = candidatePreset;
3512
- })) {
3513
- supportedFixes.push(`set edge "${edgeName}" route to verified preset "${candidatePreset}"`);
3514
- }
3515
- }
3516
- if (acceptsFix((document) => {
3517
- delete document.edges[edgeIndex].route;
3518
- })) {
3519
- supportedFixes.push(`remove route from edge "${edgeName}" so readable-v2 can use its verified automatic candidate`);
3520
- }
3521
- throwDiagnosticError(message, [{
3522
- code: 'workflow/route-preset-conflict',
3523
- severity: 'error',
3524
- message,
3525
- subject: {
3526
- diagramType: 'workflow',
3527
- edge: edge.id ?? null,
3528
- from: edge.from,
3529
- to: edge.to,
3530
- route: preset,
3531
- },
3532
- evidence: {
3533
- attemptedCandidateFamily: preset,
3534
- points,
3535
- fromSide,
3536
- toSide,
3537
- requiredEndpointStubPx: 8,
3538
- requiredInteriorSegmentPx: 16,
3539
- requiredDirectClearancePx: 28,
3540
- },
3541
- supportedFixes,
3542
- }]);
3543
- }
3544
-
3545
- function routeVia(
3546
- edge,
3547
- from,
3548
- to,
3549
- start,
3550
- end,
3551
- fromSide,
3552
- toSide,
3553
- { validateReadablePreset = true } = {},
3554
- ) {
3555
- if (edge.via) return edge.via;
3556
- const hasCoordinatePins = edge.channelX !== undefined || edge.channelY !== undefined;
3557
- if (workflow.schema_version === 2
3558
- && edge.route
3559
- && edge.route !== 'auto'
3560
- && !hasCoordinatePins
3561
- && validateReadablePreset) {
3562
- return readablePresetVia(edge, from, to, start, end, fromSide, toSide);
3563
- }
3564
- switch (edge.route || 'auto') {
3565
- case 'straight':
3566
- return [];
3567
- case 'drop': {
3568
- const y = edge.channelY ?? gapYBetween(from.lane, to.lane, edge.bias ?? 0.5);
3569
- return [[start[0], y], [end[0], y]];
3570
- }
3571
- case 'outside-right': {
3572
- const x = edge.channelX ?? layout.laneX + layout.laneW + 12;
3573
- return [[x, start[1]], [x, end[1]]];
3574
- }
3575
- case 'return-left': {
3576
- const x = edge.channelX ?? Math.min(from.x, to.x) - 28;
3577
- return [[x, start[1]], [x, end[1]]];
3578
- }
3579
- case 'bottom-channel': {
3580
- const y = edge.channelY ?? Math.max(from.y + from.height, to.y + to.height) + 32;
3581
- return [[start[0], y], [end[0], y]];
3582
- }
3583
- case 'up-channel': {
3584
- const y = edge.channelY ?? Math.min(from.y, to.y) - 28;
3585
- return [[start[0], y], [end[0], y]];
3586
- }
3587
- case 'auto':
3588
- default: {
3589
- if (workflow.schema_version === 2) {
3590
- if (edge.channelX !== undefined && edge.channelY !== undefined) {
3591
- return [[edge.channelX, start[1]], [edge.channelX, edge.channelY], [end[0], edge.channelY]];
3592
- }
3593
- if (edge.channelX !== undefined) return [[edge.channelX, start[1]], [edge.channelX, end[1]]];
3594
- if (edge.channelY !== undefined) return [[start[0], edge.channelY], [end[0], edge.channelY]];
3595
- return readableAutomaticVia(edge, from, to, start, end, fromSide, toSide);
3596
- }
3597
- if (from.lane === to.lane) return sameLaneAutoVia(start, end);
3598
- const oneBendVia = oneBendCrossLaneVia(edge, start, end, fromSide, toSide);
3599
- if (oneBendVia) return oneBendVia;
3600
- const y = gapYBetween(from.lane, to.lane, edge.bias ?? 0.5);
3601
- return [[start[0], y], [end[0], y]];
3602
- }
3603
- }
3604
- }
3605
-
3606
- function workflowEdgeLabelPoint(edge, points) {
3607
- if (workflow.schema_version === 1) {
3608
- if (edge.labelAt || Number.isInteger(edge.labelSegment) || points.length !== 3) {
3609
- return labelPoint(edge, points);
3610
- }
3611
- const segmentLengths = [0, 1].map((index) => Math.hypot(
3612
- points[index + 1][0] - points[index][0],
3613
- points[index + 1][1] - points[index][1],
3614
- ));
3615
- const labelSegment = segmentLengths[0] >= segmentLengths[1] ? 0 : 1;
3616
- const point = labelPoint({ ...edge, labelSegment }, points);
3617
- if (points[labelSegment][0] === points[labelSegment + 1][0]) point[1] += 10;
3618
- return point;
3619
- }
3620
- if (edge.labelAt || Number.isInteger(edge.labelSegment) || points.length <= 2) {
3621
- return labelPoint(edge, points);
3622
- }
3623
- const segments = points.slice(0, -1).map((point, index) => ({
3624
- index,
3625
- horizontal: Math.abs(points[index + 1][1] - point[1]) <= 0.0001,
3626
- length: Math.hypot(
3627
- points[index + 1][0] - point[0],
3628
- points[index + 1][1] - point[1],
3629
- ),
3630
- })).sort((left, right) => (
3631
- Number(right.horizontal) - Number(left.horizontal)
3632
- || right.length - left.length
3633
- || left.index - right.index
3634
- ));
3635
- const labelSegment = segments[0]?.index ?? 0;
3636
- const point = labelPoint({ ...edge, labelSegment }, points);
3637
- if (points[labelSegment][0] === points[labelSegment + 1][0]) point[1] += 10;
3638
- return point;
3639
- }
3640
-
3641
- function edgeSides(edge) {
3642
- const from = nodes.get(edge.from);
3643
- const to = nodes.get(edge.to);
3644
- const resolved = workflow.schema_version === 2 ? readableSideCache.get(edge) : null;
3645
- if (resolved) return resolved;
3646
- const oneBendSides = automaticOneBendSides(edge, from, to);
3647
- if (oneBendSides) return oneBendSides;
3648
- if (workflow.schema_version === 2
3649
- && layout.channelLabelEdgeKeys?.has(stableValueKey(edge))
3650
- && !edge.fromSide
3651
- && !edge.toSide) {
3652
- return { fromSide: 'top', toSide: 'top' };
3653
- }
3654
- return {
3655
- fromSide: chosenSide(edge.fromSide, defaultFromSide(from, to)),
3656
- toSide: chosenSide(edge.toSide, defaultToSide(from, to)),
3657
- };
3658
- }
3659
-
3660
- const automaticPorts = automaticPortSpread(workflow.edges, nodes, {
3661
- sideFor: (edge, endpoint) => edgeSides(edge)[endpoint === 'source' ? 'fromSide' : 'toSide'],
3662
- });
3663
-
3664
- function readableAutomaticRoute(edge, from, to, primarySides, primaryPorts) {
3665
- const authoredFrom = edge.fromSide && edge.fromSide !== 'auto' ? edge.fromSide : null;
3666
- const authoredTo = edge.toSide && edge.toSide !== 'auto' ? edge.toSide : null;
3667
- const sideOrder = ['right', 'bottom', 'left', 'top'];
3668
- const sidePairs = [primarySides];
3669
- for (const fromSide of authoredFrom ? [authoredFrom] : sideOrder) {
3670
- for (const toSide of authoredTo ? [authoredTo] : sideOrder) {
3671
- sidePairs.push({ fromSide, toSide });
3672
- }
3673
- }
3674
-
3675
- const naturalFromSide = authoredFrom || defaultFromSide(from, to);
3676
- const naturalToSide = authoredTo || defaultToSide(from, to);
3677
- const seen = new Set();
3678
- const plans = [];
3679
- const feedback = [];
3680
- let firstFailure = null;
3681
- for (const candidateSides of sidePairs) {
3682
- if (authoredFrom && candidateSides.fromSide !== authoredFrom) continue;
3683
- if (authoredTo && candidateSides.toSide !== authoredTo) continue;
3684
- const key = `${candidateSides.fromSide}:${candidateSides.toSide}`;
3685
- if (seen.has(key)) continue;
3686
- const pairOrdinal = seen.size;
3687
- seen.add(key);
3688
- const primary = pairOrdinal === 0;
3689
- const start = primaryPorts?.from && primary
3690
- ? primaryPorts.from
3691
- : anchor(from, candidateSides.fromSide);
3692
- const end = primaryPorts?.to && primary
3693
- ? primaryPorts.to
3694
- : anchor(to, candidateSides.toSide);
3695
- const planned = readableAutomaticCandidateSet(
3696
- edge,
3697
- from,
3698
- to,
3699
- start,
3700
- end,
3701
- candidateSides.fromSide,
3702
- candidateSides.toSide,
3703
- {
3704
- ordinalOffset: pairOrdinal * 9,
3705
- naturalFromSide,
3706
- naturalToSide,
3707
- },
3708
- );
3709
- plans.push(...planned.candidates.map((candidate) => ({
3710
- ...candidate,
3711
- ...candidateSides,
3712
- })));
3713
- if (planned.candidates.length) continue;
3714
- try {
3715
- const expandedVia = withDiagnosticRecordingSuppressed(() => readableAutomaticVia(
3716
- edge,
3717
- from,
3718
- to,
3719
- start,
3720
- end,
3721
- candidateSides.fromSide,
3722
- candidateSides.toSide,
3723
- ));
3724
- const expandedPoints = normalizeRoutePoints([start, ...expandedVia, end]);
3725
- const outsideRightOrdinal = planned.rawCandidates.findIndex(({ family }) => (
3726
- family === 'outside-right'
3727
- ));
3728
- const ordinal = pairOrdinal * 9 + Math.max(0, outsideRightOrdinal);
3729
- plans.push({
3730
- family: 'outside-right',
3731
- ordinal,
3732
- points: expandedPoints,
3733
- cost: readableCandidateCost(
3734
- edge,
3735
- expandedPoints,
3736
- ordinal,
3737
- naturalFromSide,
3738
- naturalToSide,
3739
- ),
3740
- ...candidateSides,
3741
- });
3742
- } catch (error) {
3743
- if (error instanceof WorkflowLayoutFeedback) {
3744
- feedback.push({ error, pairOrdinal });
3745
- } else if (!firstFailure) {
3746
- firstFailure = error;
3747
- }
3748
- }
3749
- }
3750
-
3751
- plans.sort((left, right) => compareCost(left.cost, right.cost));
3752
- if (plans.length) {
3753
- const selected = plans[0];
3754
- return {
3755
- points: selected.points,
3756
- fromSide: selected.fromSide,
3757
- toSide: selected.toSide,
3758
- };
3759
- }
3760
-
3761
- const feedbackPriority = {
3762
- 'rank-gap-minimum': 0,
3763
- 'lane-gap-minimum': 1,
3764
- };
3765
- feedback.sort((left, right) => (
3766
- (feedbackPriority[left.error.request?.kind] ?? 99)
3767
- - (feedbackPriority[right.error.request?.kind] ?? 99)
3768
- || left.pairOrdinal - right.pairOrdinal
3769
- ));
3770
- if (feedback.length) throw feedback[0].error;
3771
- const authoredSideFields = [
3772
- ...(authoredFrom ? ['fromSide'] : []),
3773
- ...(authoredTo ? ['toSide'] : []),
3774
- ];
3775
- if (authoredSideFields.length) {
3776
- const alternatives = verifiedPinRemovalAlternatives(
3777
- edge,
3778
- authoredSideFields,
3779
- 'so readable-v2 can replan the remaining endpoint-side pins',
3780
- );
3781
- const sourceAnchor = primaryPorts?.from || anchor(from, primarySides.fromSide);
3782
- const targetAnchor = primaryPorts?.to || anchor(to, primarySides.toSide);
3783
- const attemptedEvidence = firstFailure?.archifyDiagnostics?.[0]?.evidence || {};
3784
- throwExplicitPinConflict(edge, 'readable route feasibility with authored endpoint sides', {
3785
- conflictingPins: conflictPinsFromRemovalSets(
3786
- edge,
3787
- alternatives.removalSets,
3788
- authoredSideFields,
3789
- ),
3790
- actualCoordinates: {
3791
- sourceAnchor: [...sourceAnchor],
3792
- targetAnchor: [...targetAnchor],
3793
- },
3794
- fromSide: primarySides.fromSide,
3795
- toSide: primarySides.toSide,
3796
- ...(attemptedEvidence.attemptedCandidateFamilies
3797
- ? { attemptedCandidateFamilies: attemptedEvidence.attemptedCandidateFamilies }
3798
- : {}),
3799
- ...(attemptedEvidence.candidateCount !== undefined
3800
- ? { candidateCount: attemptedEvidence.candidateCount }
3801
- : {}),
3802
- }, alternatives.supportedFixes);
3803
- }
3804
- if (firstFailure) throw firstFailure;
3805
- throw new Error('readable-v2 automatic route enumeration produced no result');
3806
- }
3807
-
3808
- function isReadableControlledRoute(edge) {
3809
- return workflow.schema_version === 2 && (
3810
- Array.isArray(edge.via)
3811
- || edge.channelX !== undefined
3812
- || edge.channelY !== undefined
3813
- || (edge.route && edge.route !== 'auto')
3814
- );
3815
- }
3816
-
3817
- function readableControlledRoute(edge, from, to) {
3818
- const authoredFrom = edge.fromSide && edge.fromSide !== 'auto' ? edge.fromSide : null;
3819
- const authoredTo = edge.toSide && edge.toSide !== 'auto' ? edge.toSide : null;
3820
- const naturalFromSide = authoredFrom || defaultFromSide(from, to);
3821
- const naturalToSide = authoredTo || defaultToSide(from, to);
3822
- const sideOrder = ['right', 'bottom', 'left', 'top'];
3823
- const preferredPairs = [{
3824
- fromSide: naturalFromSide,
3825
- toSide: naturalToSide,
3826
- }];
3827
- for (const fromSide of authoredFrom ? [authoredFrom] : sideOrder) {
3828
- for (const toSide of authoredTo ? [authoredTo] : sideOrder) {
3829
- preferredPairs.push({ fromSide, toSide });
3830
- }
3831
- }
3832
-
3833
- const seen = new Set();
3834
- const sidePairs = preferredPairs.filter(({ fromSide, toSide }) => {
3835
- if (authoredFrom && fromSide !== authoredFrom) return false;
3836
- if (authoredTo && toSide !== authoredTo) return false;
3837
- const key = `${fromSide}:${toSide}`;
3838
- if (seen.has(key)) return false;
3839
- seen.add(key);
3840
- return true;
3841
- });
3842
- const hasAbsoluteRoutePins = Array.isArray(edge.via)
3843
- || edge.channelX !== undefined
3844
- || edge.channelY !== undefined;
3845
- const candidates = [];
3846
- const diagnosticCandidates = [];
3847
- const materializedCandidates = [];
3848
- for (const [ordinal, { fromSide, toSide }] of sidePairs.entries()) {
3849
- const start = anchor(from, fromSide);
3850
- const end = anchor(to, toSide);
3851
- const via = routeVia(
3852
- edge,
3853
- from,
3854
- to,
3855
- start,
3856
- end,
3857
- fromSide,
3858
- toSide,
3859
- { validateReadablePreset: false },
3860
- );
3861
- const authoredPoints = [start, ...via, end];
3862
- const points = hasAbsoluteRoutePins
3863
- ? authoredPoints
3864
- : normalizeRoutePoints(authoredPoints);
3865
- const materialized = { points, fromSide, toSide, ordinal };
3866
- materializedCandidates.push(materialized);
3867
- if (points.length >= 2
3868
- && points.every((point) => (
3869
- Array.isArray(point) && point.length === 2 && isFinitePoint(...point)
3870
- ))
3871
- && routeHonorsEndpointSides(points, fromSide, toSide)) {
3872
- diagnosticCandidates.push(materialized);
3873
- }
3874
- if (!readableCandidateIsFeasible(edge, points, from, to, fromSide, toSide)) continue;
3875
- if (edge.route && edge.route !== 'auto' && !routeMatchesPresetFamily(
3876
- edge.route,
3877
- points,
3878
- from,
3879
- to,
3880
- )) continue;
3881
- if (presentChannelPins(edge).some((field) => (
3882
- !routeContainsChannelPin(points, field, edge[field])
3883
- ))) continue;
3884
- candidates.push({
3885
- points,
3886
- fromSide,
3887
- toSide,
3888
- cost: readableCandidateCost(
3889
- edge,
3890
- points,
3891
- ordinal,
3892
- naturalFromSide,
3893
- naturalToSide,
3894
- ),
3895
- });
3896
- }
3897
- candidates.sort((left, right) => compareCost(left.cost, right.cost));
3898
- if (candidates.length) return candidates[0];
3899
-
3900
- // Absolute geometry is authoritative even when it is invalid. Preserve the
3901
- // best endpoint-side inference so validation can diagnose the authored
3902
- // segment or preset that actually failed instead of silently falling back to
3903
- // default sides and changing the route's meaning.
3904
- if (hasAbsoluteRoutePins) {
3905
- return diagnosticCandidates[0] || materializedCandidates[0] || null;
3906
- }
3907
-
3908
- // Preset-only routes retain their dedicated typed conflict (and verified
3909
- // alternative search) when exhaustive side inference found no valid plan.
3910
- const fallback = materializedCandidates[0];
3911
- if (!fallback) return null;
3912
- const fallbackVia = readablePresetVia(
3913
- edge,
3914
- from,
3915
- to,
3916
- fallback.points[0],
3917
- fallback.points.at(-1),
3918
- fallback.fromSide,
3919
- fallback.toSide,
3920
- );
3921
- return {
3922
- ...fallback,
3923
- points: normalizeRoutePoints([
3924
- fallback.points[0],
3925
- ...fallbackVia,
3926
- fallback.points.at(-1),
3927
- ]),
3928
- };
3929
- }
3930
-
3931
- function pathFor(edge) {
3932
- if (pathCache.has(edge)) return pathCache.get(edge);
3933
- const from = nodes.get(edge.from);
3934
- const to = nodes.get(edge.to);
3935
- if (isReadableControlledRoute(edge)) {
3936
- const planned = readableControlledRoute(edge, from, to);
3937
- if (planned) {
3938
- readableSideCache.set(edge, {
3939
- fromSide: planned.fromSide,
3940
- toSide: planned.toSide,
3941
- });
3942
- const routed = { d: polylinePath(planned.points), points: planned.points };
3943
- pathCache.set(edge, routed);
3944
- return routed;
3945
- }
3946
- }
3947
- const ports = automaticPorts.get(edge);
3948
- const { fromSide, toSide } = edgeSides(edge);
3949
- const readableAutomatic = workflow.schema_version === 2
3950
- && !Array.isArray(edge.via)
3951
- && edge.channelX === undefined
3952
- && edge.channelY === undefined
3953
- && (!edge.route || edge.route === 'auto');
3954
- if (readableAutomatic) {
3955
- const planned = readableAutomaticRoute(
3956
- edge,
3957
- from,
3958
- to,
3959
- { fromSide, toSide },
3960
- ports,
3961
- );
3962
- readableSideCache.set(edge, {
3963
- fromSide: planned.fromSide,
3964
- toSide: planned.toSide,
3965
- });
3966
- const routed = { d: polylinePath(planned.points), points: planned.points };
3967
- pathCache.set(edge, routed);
3968
- return routed;
3969
- }
3970
- const start = ports?.from || anchor(from, fromSide);
3971
- const end = ports?.to || anchor(to, toSide);
3972
- const authoredPoints = [start, ...routeVia(edge, from, to, start, end, fromSide, toSide), end];
3973
- const hasAbsoluteRoutePins = Array.isArray(edge.via)
3974
- || edge.channelX !== undefined
3975
- || edge.channelY !== undefined;
3976
- const points = workflow.schema_version === 2 && !hasAbsoluteRoutePins
3977
- ? normalizeRoutePoints(authoredPoints)
3978
- : authoredPoints;
3979
- const routed = { d: polylinePath(points), points };
3980
- pathCache.set(edge, routed);
3981
- return routed;
3982
- }
3983
-
3984
- function labelRectFor(edge, relationIndex) {
3985
- if (!edge.label || !nodes.has(edge.from) || !nodes.has(edge.to)) return null;
3986
- const [lx, ly] = workflowEdgeLabelPoint(edge, pathFor(edge).points);
3987
- const width = workflowLabelWidth(edge.label);
3988
- return {
3989
- relation: edge,
3990
- relationIndex,
3991
- label: edge.label,
3992
- x: lx - width / 2,
3993
- y: ly - 10,
3994
- width,
3995
- height: 14,
3996
- lx,
3997
- ly,
3998
- };
3999
- }
4000
-
4001
- function measuredContentBounds() {
4002
- let left = layout.laneX;
4003
- let top = 27;
4004
- let right = layout.laneX + layout.laneW;
4005
- let bottom = legendY() + 18;
4006
- const owners = {
4007
- left: 'workflow lanes',
4008
- top: asArray(workflow.phases).length ? 'phase header band' : 'workflow top padding',
4009
- right: 'workflow lanes',
4010
- bottom: workflowLegendEntries.length ? 'legend' : 'workflow lanes and bottom padding',
4011
- };
4012
- const includePoint = ([x, y], contributor) => {
4013
- if (x < left) {
4014
- left = x;
4015
- owners.left = contributor;
4016
- }
4017
- if (y < top) {
4018
- top = y;
4019
- owners.top = contributor;
4020
- }
4021
- if (x > right) {
4022
- right = x;
4023
- owners.right = contributor;
4024
- }
4025
- if (y > bottom) {
4026
- bottom = y;
4027
- owners.bottom = contributor;
4028
- }
4029
- };
4030
- const includeRect = (rect, contributor) => {
4031
- includePoint([rect.x, rect.y], contributor);
4032
- includePoint([rect.x + rect.width, rect.y + rect.height], contributor);
4033
- };
4034
-
4035
- for (const node of nodes.values()) includeRect(node, `node ${node.id}`);
4036
- for (const [index, edge] of workflow.edges.entries()) {
4037
- if (!nodes.has(edge.from) || !nodes.has(edge.to)) continue;
4038
- for (const point of pathFor(edge).points) includePoint(point, `edge ${edge.id || index}`);
4039
- const label = labelRectFor(edge, index);
4040
- if (label) includeRect(label, `edge ${edge.id || index} label mask`);
4041
- }
4042
- for (const phase of asArray(workflow.phases)) {
4043
- if (!Number.isInteger(phase.fromCol) || !Number.isInteger(phase.toCol)
4044
- || phase.fromCol < 0 || phase.toCol >= layout.colXs.length || phase.fromCol > phase.toCol) continue;
4045
- const span = phaseSpan(phase);
4046
- includeRect({ x: span.x, y: 27, width: span.width, height: 16 }, `phase ${phase.id}`);
4047
- }
4048
- for (const group of asArray(workflow.groups)) {
4049
- if (!laneIndex.has(group.lane) || !Number.isInteger(group.fromCol) || !Number.isInteger(group.toCol)
4050
- || group.fromCol < 0 || group.toCol >= layout.colXs.length || group.fromCol > group.toCol) continue;
4051
- const span = groupSpan(group);
4052
- includeRect({
4053
- x: span.x,
4054
- y: laneTop(group.lane) + layout.laneTitleH + GROUP_FRAME_TOP_INSET,
4055
- width: span.width,
4056
- height: workflow.schema_version === 2
4057
- ? laneHeight(group.lane) - layout.laneTitleH
4058
- - GROUP_FRAME_TOP_INSET - GROUP_FRAME_BOTTOM_INSET
4059
- : layout.laneH - layout.laneTitleH - 16,
4060
- }, `group ${group.id}`);
4061
- if (workflow.schema_version === 2) {
4062
- const frameY = laneTop(group.lane) + layout.laneTitleH + GROUP_FRAME_TOP_INSET;
4063
- const labelBaseline = frameY + GROUP_LABEL_BASELINE_OFFSET;
4064
- includeRect({
4065
- x: span.x + 10,
4066
- y: labelBaseline - GROUP_LABEL_MASK_ASCENT,
4067
- width: textUnits(group.label) * 5.6,
4068
- height: GROUP_LABEL_MASK_H,
4069
- }, `group ${group.id} label`);
4070
- }
4071
- }
4072
- if (workflowLegendEntries.length) {
4073
- for (const rect of workflowLegendRects()) includeRect(rect, `legend ${rect.kind}`);
4074
- }
4075
- return {
4076
- left,
4077
- top,
4078
- right,
4079
- bottom,
4080
- contributors: [...new Set([
4081
- ...Object.values(owners),
4082
- ...asArray(layout.widthContributors),
4083
- ...asArray(layout.heightContributors),
4084
- ])],
4085
- };
4086
- }
4087
-
4088
- function finalizeReadableViewBox() {
4089
- if (workflow.schema_version !== 2) {
4090
- requiredViewBox = [...viewBox];
4091
- return;
4092
- }
4093
- const bounds = measuredContentBounds();
4094
- requiredViewBox = [
4095
- Math.max(minimumCanvasWidth, Math.ceil(bounds.right + 16)),
4096
- Math.max(autoHeight, Math.ceil(bounds.bottom + 18)),
4097
- ];
4098
- const outsideOrigin = bounds.left < 0 || bounds.top < 0;
4099
- if (outsideOrigin) {
4100
- const hasAbsolutePins = workflow.edges.some((edge) => (
4101
- Array.isArray(edge.via)
4102
- || Array.isArray(edge.labelAt)
4103
- || edge.channelX !== undefined
4104
- || edge.channelY !== undefined
4105
- ));
4106
- const message = `Workflow geometry extends above or left of the viewBox origin (${Math.round(bounds.left)}, ${Math.round(bounds.top)}).`;
4107
- throwDiagnosticError(message, [{
4108
- code: hasAbsolutePins ? 'workflow/explicit-pin-conflict' : 'workflow/solver-budget-exhausted',
4109
- severity: 'error',
4110
- message,
4111
- subject: { diagramType: 'workflow', path: '/meta/viewBox' },
4112
- evidence: {
4113
- actualViewBox: [...viewBox],
4114
- requiredViewBox: [...requiredViewBox],
4115
- contentBounds: [bounds.left, bounds.top, bounds.right, bounds.bottom],
4116
- contributors: bounds.contributors,
4117
- },
4118
- supportedFixes: [],
4119
- }]);
4120
- }
4121
- if (!workflow.meta?.viewBox) {
4122
- viewBox = [...requiredViewBox];
4123
- return;
4124
- }
4125
- const tooNarrow = viewBox[0] < requiredViewBox[0];
4126
- const tooShort = viewBox[1] < requiredViewBox[1];
4127
- if (!tooNarrow && !tooShort) return;
4128
- const message = `Workflow viewBox ${viewBox[0]}×${viewBox[1]} cannot contain the readable-v2 layout; minimum ${requiredViewBox[0]}×${requiredViewBox[1]}.`;
4129
- const supportedFixes = [];
4130
- if (acceptsFix((document) => {
4131
- document.meta.viewBox = [...requiredViewBox];
4132
- })) {
4133
- supportedFixes.push(`set meta.viewBox to at least [${requiredViewBox[0]}, ${requiredViewBox[1]}]`);
4134
- }
4135
- if (acceptsFix((document) => {
4136
- delete document.meta.viewBox;
4137
- })) {
4138
- supportedFixes.push('omit meta.viewBox so the compiler can use its measured intrinsic canvas');
4139
- }
4140
- throwDiagnosticError(message, [{
4141
- code: 'workflow/viewbox-capacity',
4142
- severity: 'error',
4143
- message,
4144
- subject: { diagramType: 'workflow', path: '/meta/viewBox' },
4145
- evidence: {
4146
- actualViewBox: [...viewBox],
4147
- requiredViewBox: [...requiredViewBox],
4148
- contentBounds: [bounds.left, bounds.top, bounds.right, bounds.bottom],
4149
- contributors: bounds.contributors,
4150
- },
4151
- supportedFixes,
4152
- }]);
4153
- }
4154
-
4155
- function renderLane(lane, index) {
4156
- const y = laneTop(lane.id);
4157
- const height = laneHeight(index);
4158
- const exception = lane.variant === 'exception'
4159
- ? `\n <rect data-graph-role="structural-frame" data-composition-frame-kind="exception-lane" data-composition-frame-id="lane-${index}-exception" x="${layout.laneX + 6}" y="${y + 6}" width="${layout.laneW - 12}" height="${height - 12}" rx="8" class="c-security-group" stroke-width="1"/>`
4160
- : '';
4161
- const labelClass = lane.variant === 'exception' ? 't-security' : 't-dim';
4162
- const prefix = lane.variant === 'exception' ? 'EX' : String(index + 1).padStart(2, '0');
4163
- return ` <rect data-graph-role="structural-frame" data-composition-frame-kind="lane" data-composition-frame-id="lane-${index}" x="${layout.laneX}" y="${y}" width="${layout.laneW}" height="${height}" rx="10" class="c-lane" stroke-width="1"/>${exception}
4164
- <text x="${layout.laneX + 14}" y="${y + 22}" class="${labelClass}" font-size="10" font-weight="600">${prefix} / ${esc(lane.label)}</text>`;
4165
- }
4166
-
4167
- function renderPhase(phase) {
4168
- const span = phaseSpan(phase);
4169
- const accent = variantAccent(phase.variant);
4170
- const [lineClass] = arrowClassMap[phase.variant || 'default'] || arrowClassMap.default;
4171
- return ` <line x1="${span.x}" y1="35" x2="${span.x + span.width}" y2="35" class="${lineClass}" stroke-width="1.1"/>
4172
- <rect x="${span.x}" y="27" width="${span.width}" height="16" rx="4" class="c-mask"/>
4173
- <text x="${span.cx}" y="39" class="${accent}" font-size="8" font-weight="600" text-anchor="middle">${esc(phase.label)}</text>`;
4174
- }
4175
-
4176
- function renderGroup(group, index) {
4177
- const span = groupSpan(group);
4178
- const y = laneTop(group.lane) + layout.laneTitleH + GROUP_FRAME_TOP_INSET;
4179
- const height = workflow.schema_version === 2
4180
- ? laneHeight(group.lane) - layout.laneTitleH
4181
- - GROUP_FRAME_TOP_INSET - GROUP_FRAME_BOTTOM_INSET
4182
- : layout.laneH - layout.laneTitleH - 16;
4183
- const cls = group.variant === 'security' ? 'c-security-group' : 'c-lane';
4184
- const textClass = variantAccent(group.variant);
4185
- const labelY = workflow.schema_version === 2 ? y + GROUP_LABEL_BASELINE_OFFSET : y + 14;
4186
- return ` <rect data-graph-role="structural-frame" data-composition-frame-kind="group" data-composition-frame-id="group-${index}" x="${span.x}" y="${y}" width="${span.width}" height="${height}" rx="9" class="${cls}" stroke-width="1"/>
4187
- <text x="${span.x + 10}" y="${labelY}" class="${textClass}" font-size="7" font-weight="600">${esc(group.label)}</text>`;
4188
- }
4189
-
4190
- function renderNode(node) {
4191
- const fill = componentFill[node.type] || 'c-external';
4192
- const accent = componentText[node.type] || 't-muted';
4193
- const hasSub = node.sublabel != null && node.sublabel !== '';
4194
- const labelFontSize = fittedNodeFontSize(node.label, brandLabelFitWidth(node, node.width), nodeTextFit.labelPreferred, nodeTextFit.labelMinimum);
4195
- const sublabelFontSize = hasSub
4196
- ? fittedNodeFontSize(node.sublabel, node.width, nodeTextFit.sublabelPreferred, nodeTextFit.sublabelMinimum)
4197
- : nodeTextFit.sublabelPreferred;
4198
- const sub = hasSub
4199
- ? `\n <text data-detail="context" x="${node.cx}" y="${node.y + 38}" class="t-muted" font-size="${sublabelFontSize}" text-anchor="middle">${esc(node.sublabel)}</text>`
4200
- : '';
4201
- const tag = node.tag
4202
- ? `\n <text data-detail="fine" x="${node.cx}" y="${node.y + node.height - 12}" class="${accent}" font-size="${fittedNodeFontSize(node.tag, node.width, nodeTextFit.tagPreferred, nodeTextFit.tagMinimum)}" text-anchor="middle">${esc(node.tag)}</text>`
4203
- : '';
4204
- const brand = renderBrandMark(node, { x: node.x + node.width - 22, y: node.y + 6 });
4205
- const passport = { kind: node.type, sublabel: node.sublabel, tag: node.tag, context: nodeContext(node), ...brandMetadataFor(node) };
4206
- return ` <g ${focusNodeAttrs(node.id, node.label, passport, workflow.meta.locale)}>
4207
- ${focusNodeTitle(node.label, passport)}
4208
- <rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="6" class="c-mask"/>
4209
- <rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="6" class="${fill}"${animateAttr(workflow.meta, 'node', nodeStep(node))} stroke-width="1.5"/>
4210
- ${renderSemanticSigil(node.type, { x: node.x + 6, y: node.y + 6 })}${brand ? `\n ${brand}` : ''}
4211
- <text data-node-label=""${hasSub ? ' data-detail-anchor=""' : ''} x="${node.cx}" y="${node.y + 21}" class="t-primary" font-size="${labelFontSize}" font-weight="600" text-anchor="middle">${esc(node.label)}</text>${sub}${tag}
4212
- </g>`;
4213
- }
4214
-
4215
- function renderEdgePath(edge, index) {
4216
- const [cls, marker] = arrowClassMap[edge.variant || 'default'] || arrowClassMap.default;
4217
- const routed = pathFor(edge);
4218
- const strokeWidth = edge.width || (edge.variant === 'emphasis' ? 1.8 : 1.4);
4219
- return ` <path ${focusEdgeAttrs(edge.from, edge.to, edge.label, index, edge.id)} data-composition-points="${routePointsValue(routed.points)}" d="${routed.d}" class="${cls}"${animateAttr(workflow.meta, 'edge', edgeSteps.get(edge))} stroke-width="${strokeWidth}" marker-end="url(#${marker})"/>`;
4220
- }
4221
-
4222
- function renderEdgeLabel(edge, index) {
4223
- if (!edge.label) return '';
4224
- const routed = pathFor(edge);
4225
- const [lx, ly] = workflowEdgeLabelPoint(edge, routed.points);
4226
- const labelW = workflowLabelWidth(edge.label);
4227
- return ` <g data-detail="context" ${focusEdgeAttrs(edge.from, edge.to, edge.label, index, edge.id)}>
4228
- <rect x="${lx - labelW / 2}" y="${ly - 10}" width="${labelW}" height="14" rx="3" class="c-mask"/>
4229
- <text x="${lx}" y="${ly}" class="${variantAccent(edge.variant, { dashed: 't-database' })}" font-size="8" text-anchor="middle">${esc(edge.label)}</text>
4230
- </g>`;
4231
- }
4232
-
4233
- function renderLegend() {
4234
- const obstacles = workflow.schema_version === 2
4235
- ? relationshipLegendObstacles(workflow.edges, {
4236
- pointsFor: (edge) => pathFor(edge).points,
4237
- labelRectFor,
4238
- })
4239
- : [];
4240
- return renderResolvedLegend({
4241
- entries: workflowLegendEntries,
4242
- locale: workflow.meta.locale,
4243
- layout: workflowLegendLayout(obstacles),
4244
- renderSwatch: (entry) => `<rect x="${entry.x}" y="${entry.baseline - 8}" width="14" height="9" rx="2" class="${componentFill[entry.kind] || 'c-external'}" stroke-width="1"/>`,
4245
- });
4246
- }
4247
-
4248
- function renderSvg() {
4249
- return ` <svg viewBox="0 0 ${viewBox[0]} ${viewBox[1]}" ${svgRootAttrs(workflow.meta, 'workflow diagram')}>
4250
- ${svgAccessibleText(workflow.meta, 'workflow')}
4251
- ${renderDefinitions()}
4252
-
4253
- <!-- Background Grid -->
4254
- <rect width="100%" height="100%" fill="url(#grid)" />
4255
-
4256
- <!-- Swimlanes -->
4257
- ${workflow.lanes.map(renderLane).join('\n\n')}
4258
-
4259
- <!-- Phase headers -->
4260
- ${asArray(workflow.phases).map(renderPhase).join('\n')}
4261
-
4262
- <!-- Workflow groups -->
4263
- ${asArray(workflow.groups).map(renderGroup).join('\n')}
4264
-
4265
- <!-- Edge paths -->
4266
- ${workflow.edges.map(renderEdgePath).join('\n')}
4267
-
4268
- <!-- Nodes -->
4269
- ${[...nodes.values()].map(renderNode).join('\n\n')}
4270
-
4271
- <!-- Edge labels -->
4272
- ${workflow.edges.map(renderEdgeLabel).join('\n')}
4273
-
4274
- <!-- Legend -->
4275
- ${renderLegend()}
4276
- </svg>`;
4277
- }
4278
-
4279
-
4280
- try {
4281
- validateReadableInputsBeforeRouting();
4282
- validateReadablePinnedGeometry();
4283
- validateWorkflow();
4284
- finalizeReadableViewBox();
4285
- const svg = renderSvg();
4286
- const receipt = {
4287
- contract: layout.contract,
4288
- viewBox: [...viewBox],
4289
- requiredViewBox: [...requiredViewBox],
4290
- columns: [...layout.colXs],
4291
- nodes: [...nodes.values()].map((node) => ({
4292
- id: node.id,
4293
- lane: node.lane,
4294
- col: node.col,
4295
- x: node.x,
4296
- y: node.y,
4297
- width: node.width,
4298
- height: node.height,
4299
- })),
4300
- edges: workflow.edges.map((edge) => ({
4301
- id: edge.id ?? null,
4302
- from: edge.from,
4303
- to: edge.to,
4304
- points: pathFor(edge).points.map((point) => [...point]),
4305
- })),
4306
- labels: workflow.edges.flatMap((edge) => {
4307
- if (!edge.label || !nodes.has(edge.from) || !nodes.has(edge.to)) return [];
4308
- const [x, y] = workflowEdgeLabelPoint(edge, pathFor(edge).points);
4309
- return [{ edge: edge.id ?? null, label: edge.label, x, y, width: workflowLabelWidth(edge.label), height: 14 }];
4310
- }),
4311
- diagnostics: [],
4312
- };
4313
- return { ok: true, svg, receipt };
4314
- } catch (error) {
4315
- if (!Array.isArray(error?.archifyDiagnostics)) throw error;
4316
- const diagnostics = error.archifyDiagnostics.map((diagnostic) => ({ ...diagnostic }));
4317
- return compilerFailure(layout.contract, diagnostics, error.message);
4318
- }
4319
- }
4320
-
4321
- function feedbackFailure(request) {
4322
- const message = `Workflow edge "${request.edge || `${request.from}->${request.to}`}" exhausted bounded readable-v2 layout feedback without a feasible automatic route.`;
4323
- const diagnostics = [{
4324
- code: 'workflow/solver-budget-exhausted',
4325
- severity: 'error',
4326
- message,
4327
- subject: {
4328
- diagramType: 'workflow',
4329
- edge: request.edge,
4330
- from: request.from,
4331
- to: request.to,
4332
- },
4333
- evidence: {
4334
- attemptedCandidateFamilies: request.attemptedCandidateFamilies,
4335
- candidateCount: request.candidateCount,
4336
- },
4337
- supportedFixes: [],
4338
- }];
4339
- return compilerFailure('readable-v2', diagnostics, message);
4340
- }
4341
-
4342
- function compileWorkflowWithFeedback({ workflow, qualityProfile, discoverFixes = true } = {}) {
4343
- let layoutFeedback = {};
4344
- for (let attempt = 0; attempt <= MAX_READABLE_LAYOUT_FEEDBACK_ROUNDS; attempt += 1) {
4345
- try {
4346
- return compileWorkflowInternal({
4347
- workflow,
4348
- qualityProfile,
4349
- discoverFixes,
4350
- layoutFeedback,
4351
- });
4352
- } catch (error) {
4353
- if (!(error instanceof WorkflowLayoutFeedback)) throw error;
4354
- const request = error.request;
4355
- let nextFeedback = null;
4356
- if (request.kind === 'rank-gap-minimum'
4357
- && Number.isInteger(request.fromCol)
4358
- && Number.isInteger(request.toCol)
4359
- && Number.isFinite(request.minimum)) {
4360
- const key = `${request.fromCol}:${request.toCol}`;
4361
- const current = layoutFeedback.rankGapMinimums?.[key] ?? -Infinity;
4362
- if (request.minimum > current + 0.0001) {
4363
- nextFeedback = {
4364
- ...layoutFeedback,
4365
- rankGapMinimums: {
4366
- ...(layoutFeedback.rankGapMinimums || {}),
4367
- [key]: request.minimum,
4368
- },
4369
- rankGapContributors: {
4370
- ...(layoutFeedback.rankGapContributors || {}),
4371
- [key]: [
4372
- `rank ${request.fromCol}→${request.toCol} route clearance`,
4373
- `edge ${request.edge || `${request.from}->${request.to}`} route`,
4374
- ],
4375
- },
4376
- };
4377
- }
4378
- } else if (request.kind === 'lane-gap-minimum'
4379
- && Number.isFinite(request.minimum)
4380
- && request.minimum > (layoutFeedback.laneGapMin ?? -Infinity) + 0.0001) {
4381
- nextFeedback = {
4382
- ...layoutFeedback,
4383
- laneGapMin: request.minimum,
4384
- laneGapContributors: [
4385
- `edge ${request.edge || `${request.from}->${request.to}`} lane-gap route clearance`,
4386
- ],
4387
- };
4388
- }
4389
- if (!nextFeedback || attempt === MAX_READABLE_LAYOUT_FEEDBACK_ROUNDS) {
4390
- return feedbackFailure(request);
4391
- }
4392
- layoutFeedback = nextFeedback;
4393
- }
4394
- }
4395
- throw new Error('unreachable readable-v2 layout feedback state');
4396
- }
4397
-
4398
- export function compileWorkflow({ workflow, qualityProfile } = {}) {
4399
- return compileWorkflowWithFeedback({ workflow, qualityProfile });
4400
- }