@transitrix/diagrams 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (541) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/__tests__/edge-path.test.d.ts +1 -0
  4. package/dist/__tests__/edge-path.test.js +39 -0
  5. package/dist/__tests__/scope.test.d.ts +1 -0
  6. package/dist/__tests__/scope.test.js +23 -0
  7. package/dist/__tests__/yaml-normalize.test.d.ts +1 -0
  8. package/dist/__tests__/yaml-normalize.test.js +148 -0
  9. package/dist/activities/__tests__/cpm.test.d.ts +1 -0
  10. package/dist/activities/__tests__/cpm.test.js +146 -0
  11. package/dist/activities/__tests__/gantt.test.d.ts +1 -0
  12. package/dist/activities/__tests__/gantt.test.js +224 -0
  13. package/dist/activities/__tests__/layout.test.d.ts +1 -0
  14. package/dist/activities/__tests__/layout.test.js +98 -0
  15. package/dist/activities/__tests__/validate.test.d.ts +1 -0
  16. package/dist/activities/__tests__/validate.test.js +363 -0
  17. package/dist/activities/cpm.d.ts +2 -0
  18. package/dist/activities/cpm.js +86 -0
  19. package/dist/activities/gantt.d.ts +6 -0
  20. package/dist/activities/gantt.js +250 -0
  21. package/dist/activities/index.d.ts +5 -0
  22. package/dist/activities/index.js +4 -0
  23. package/dist/activities/layout.d.ts +2 -0
  24. package/dist/activities/layout.js +103 -0
  25. package/dist/activities/types.d.ts +125 -0
  26. package/dist/activities/types.js +1 -0
  27. package/dist/activities/validate.d.ts +2 -0
  28. package/dist/activities/validate.js +326 -0
  29. package/dist/activity-card/__tests__/example.test.d.ts +1 -0
  30. package/dist/activity-card/__tests__/example.test.js +56 -0
  31. package/dist/activity-card/__tests__/layout.test.d.ts +1 -0
  32. package/dist/activity-card/__tests__/layout.test.js +57 -0
  33. package/dist/activity-card/__tests__/resolver.test.d.ts +1 -0
  34. package/dist/activity-card/__tests__/resolver.test.js +146 -0
  35. package/dist/activity-card/__tests__/validate.test.d.ts +1 -0
  36. package/dist/activity-card/__tests__/validate.test.js +116 -0
  37. package/dist/activity-card/index.d.ts +5 -0
  38. package/dist/activity-card/index.js +3 -0
  39. package/dist/activity-card/layout.d.ts +7 -0
  40. package/dist/activity-card/layout.js +167 -0
  41. package/dist/activity-card/resolver.d.ts +7 -0
  42. package/dist/activity-card/resolver.js +310 -0
  43. package/dist/activity-card/types.d.ts +175 -0
  44. package/dist/activity-card/types.js +20 -0
  45. package/dist/activity-card/validate.d.ts +3 -0
  46. package/dist/activity-card/validate.js +145 -0
  47. package/dist/actor/__tests__/validate.test.d.ts +1 -0
  48. package/dist/actor/__tests__/validate.test.js +57 -0
  49. package/dist/actor/index.d.ts +3 -0
  50. package/dist/actor/index.js +2 -0
  51. package/dist/actor/types.d.ts +20 -0
  52. package/dist/actor/types.js +4 -0
  53. package/dist/actor/validate.d.ts +2 -0
  54. package/dist/actor/validate.js +34 -0
  55. package/dist/applications/__tests__/validate.test.d.ts +1 -0
  56. package/dist/applications/__tests__/validate.test.js +223 -0
  57. package/dist/applications/index.d.ts +2 -0
  58. package/dist/applications/index.js +1 -0
  59. package/dist/applications/types.d.ts +39 -0
  60. package/dist/applications/types.js +1 -0
  61. package/dist/applications/validate.d.ts +3 -0
  62. package/dist/applications/validate.js +109 -0
  63. package/dist/assertion/__tests__/example.test.d.ts +1 -0
  64. package/dist/assertion/__tests__/example.test.js +32 -0
  65. package/dist/assertion/__tests__/validate.test.d.ts +1 -0
  66. package/dist/assertion/__tests__/validate.test.js +130 -0
  67. package/dist/assertion/index.d.ts +4 -0
  68. package/dist/assertion/index.js +2 -0
  69. package/dist/assertion/types.d.ts +43 -0
  70. package/dist/assertion/types.js +7 -0
  71. package/dist/assertion/validate.d.ts +9 -0
  72. package/dist/assertion/validate.js +137 -0
  73. package/dist/blocks/__tests__/layout.test.d.ts +1 -0
  74. package/dist/blocks/__tests__/layout.test.js +153 -0
  75. package/dist/blocks/__tests__/validate.test.d.ts +1 -0
  76. package/dist/blocks/__tests__/validate.test.js +235 -0
  77. package/dist/blocks/index.d.ts +4 -0
  78. package/dist/blocks/index.js +2 -0
  79. package/dist/blocks/layout.d.ts +4 -0
  80. package/dist/blocks/layout.js +145 -0
  81. package/dist/blocks/types.d.ts +56 -0
  82. package/dist/blocks/types.js +1 -0
  83. package/dist/blocks/validate.d.ts +9 -0
  84. package/dist/blocks/validate.js +186 -0
  85. package/dist/capability-map/__tests__/validate.test.d.ts +1 -0
  86. package/dist/capability-map/__tests__/validate.test.js +212 -0
  87. package/dist/capability-map/index.d.ts +2 -0
  88. package/dist/capability-map/index.js +1 -0
  89. package/dist/capability-map/types.d.ts +26 -0
  90. package/dist/capability-map/types.js +1 -0
  91. package/dist/capability-map/validate.d.ts +3 -0
  92. package/dist/capability-map/validate.js +103 -0
  93. package/dist/change/__tests__/validate.test.d.ts +1 -0
  94. package/dist/change/__tests__/validate.test.js +73 -0
  95. package/dist/change/index.d.ts +2 -0
  96. package/dist/change/index.js +1 -0
  97. package/dist/change/types.d.ts +17 -0
  98. package/dist/change/types.js +3 -0
  99. package/dist/change/validate.d.ts +2 -0
  100. package/dist/change/validate.js +57 -0
  101. package/dist/compliance/__tests__/classify.test.d.ts +1 -0
  102. package/dist/compliance/__tests__/classify.test.js +24 -0
  103. package/dist/compliance/__tests__/confidence.test.d.ts +1 -0
  104. package/dist/compliance/__tests__/confidence.test.js +78 -0
  105. package/dist/compliance/__tests__/coverage-metric.test.d.ts +1 -0
  106. package/dist/compliance/__tests__/coverage-metric.test.js +164 -0
  107. package/dist/compliance/__tests__/demo-scenario.test.d.ts +1 -0
  108. package/dist/compliance/__tests__/demo-scenario.test.js +226 -0
  109. package/dist/compliance/__tests__/example.test.d.ts +1 -0
  110. package/dist/compliance/__tests__/example.test.js +51 -0
  111. package/dist/compliance/__tests__/gap-report.test.d.ts +1 -0
  112. package/dist/compliance/__tests__/gap-report.test.js +113 -0
  113. package/dist/compliance/__tests__/html.test.d.ts +1 -0
  114. package/dist/compliance/__tests__/html.test.js +122 -0
  115. package/dist/compliance/__tests__/impact.test.d.ts +1 -0
  116. package/dist/compliance/__tests__/impact.test.js +410 -0
  117. package/dist/compliance/__tests__/markdown.test.d.ts +1 -0
  118. package/dist/compliance/__tests__/markdown.test.js +85 -0
  119. package/dist/compliance/__tests__/views.test.d.ts +1 -0
  120. package/dist/compliance/__tests__/views.test.js +59 -0
  121. package/dist/compliance/classify.d.ts +27 -0
  122. package/dist/compliance/classify.js +60 -0
  123. package/dist/compliance/confidence.d.ts +27 -0
  124. package/dist/compliance/confidence.js +43 -0
  125. package/dist/compliance/coverage-metric.d.ts +74 -0
  126. package/dist/compliance/coverage-metric.js +181 -0
  127. package/dist/compliance/gap-report.d.ts +20 -0
  128. package/dist/compliance/gap-report.js +64 -0
  129. package/dist/compliance/html.d.ts +22 -0
  130. package/dist/compliance/html.js +256 -0
  131. package/dist/compliance/impact.d.ts +251 -0
  132. package/dist/compliance/impact.js +464 -0
  133. package/dist/compliance/index.d.ts +16 -0
  134. package/dist/compliance/index.js +9 -0
  135. package/dist/compliance/markdown.d.ts +18 -0
  136. package/dist/compliance/markdown.js +129 -0
  137. package/dist/compliance/reverse-index.d.ts +8 -0
  138. package/dist/compliance/reverse-index.js +30 -0
  139. package/dist/compliance/types.d.ts +98 -0
  140. package/dist/compliance/types.js +5 -0
  141. package/dist/compliance/views.d.ts +14 -0
  142. package/dist/compliance/views.js +35 -0
  143. package/dist/compliance-matrix/__tests__/build.test.d.ts +1 -0
  144. package/dist/compliance-matrix/__tests__/build.test.js +183 -0
  145. package/dist/compliance-matrix/__tests__/example.test.d.ts +1 -0
  146. package/dist/compliance-matrix/__tests__/example.test.js +49 -0
  147. package/dist/compliance-matrix/build.d.ts +18 -0
  148. package/dist/compliance-matrix/build.js +0 -0
  149. package/dist/compliance-matrix/index.d.ts +2 -0
  150. package/dist/compliance-matrix/index.js +1 -0
  151. package/dist/compliance-matrix/types.d.ts +86 -0
  152. package/dist/compliance-matrix/types.js +8 -0
  153. package/dist/confidence/__tests__/score.test.d.ts +1 -0
  154. package/dist/confidence/__tests__/score.test.js +188 -0
  155. package/dist/confidence/index.d.ts +2 -0
  156. package/dist/confidence/index.js +1 -0
  157. package/dist/confidence/score.d.ts +62 -0
  158. package/dist/confidence/score.js +215 -0
  159. package/dist/confidence/types.d.ts +79 -0
  160. package/dist/confidence/types.js +6 -0
  161. package/dist/edge-path.d.ts +18 -0
  162. package/dist/edge-path.js +35 -0
  163. package/dist/element-envelope.d.ts +15 -0
  164. package/dist/element-envelope.js +55 -0
  165. package/dist/factor/__tests__/validate.test.d.ts +1 -0
  166. package/dist/factor/__tests__/validate.test.js +83 -0
  167. package/dist/factor/index.d.ts +3 -0
  168. package/dist/factor/index.js +2 -0
  169. package/dist/factor/types.d.ts +23 -0
  170. package/dist/factor/types.js +6 -0
  171. package/dist/factor/validate.d.ts +2 -0
  172. package/dist/factor/validate.js +69 -0
  173. package/dist/fgca/__tests__/chain-table.test.d.ts +1 -0
  174. package/dist/fgca/__tests__/chain-table.test.js +158 -0
  175. package/dist/fgca/__tests__/layout.test.d.ts +1 -0
  176. package/dist/fgca/__tests__/layout.test.js +140 -0
  177. package/dist/fgca/__tests__/parse-canonical.test.d.ts +1 -0
  178. package/dist/fgca/__tests__/parse-canonical.test.js +224 -0
  179. package/dist/fgca/__tests__/preview-layout.test.d.ts +1 -0
  180. package/dist/fgca/__tests__/preview-layout.test.js +106 -0
  181. package/dist/fgca/__tests__/validate.test.d.ts +1 -0
  182. package/dist/fgca/__tests__/validate.test.js +125 -0
  183. package/dist/fgca/chain-table.d.ts +36 -0
  184. package/dist/fgca/chain-table.js +177 -0
  185. package/dist/fgca/index.d.ts +16 -0
  186. package/dist/fgca/index.js +11 -0
  187. package/dist/fgca/layout.d.ts +27 -0
  188. package/dist/fgca/layout.js +156 -0
  189. package/dist/fgca/nodeTypes.d.ts +6 -0
  190. package/dist/fgca/nodeTypes.js +10 -0
  191. package/dist/fgca/nodes/FGCAActivityNode.d.ts +13 -0
  192. package/dist/fgca/nodes/FGCAActivityNode.js +35 -0
  193. package/dist/fgca/nodes/FGCAChangeNode.d.ts +12 -0
  194. package/dist/fgca/nodes/FGCAChangeNode.js +35 -0
  195. package/dist/fgca/nodes/FGCAFactorNode.d.ts +12 -0
  196. package/dist/fgca/nodes/FGCAFactorNode.js +35 -0
  197. package/dist/fgca/nodes/FGCAGoalNode.d.ts +14 -0
  198. package/dist/fgca/nodes/FGCAGoalNode.js +40 -0
  199. package/dist/fgca/parse-canonical.d.ts +58 -0
  200. package/dist/fgca/parse-canonical.js +339 -0
  201. package/dist/fgca/preview-layout.d.ts +87 -0
  202. package/dist/fgca/preview-layout.js +122 -0
  203. package/dist/fgca/types.d.ts +39 -0
  204. package/dist/fgca/types.js +7 -0
  205. package/dist/fgca/validate.d.ts +14 -0
  206. package/dist/fgca/validate.js +108 -0
  207. package/dist/geometry.d.ts +7 -0
  208. package/dist/geometry.js +2 -0
  209. package/dist/goals/__tests__/layout.test.d.ts +1 -0
  210. package/dist/goals/__tests__/layout.test.js +153 -0
  211. package/dist/goals/__tests__/mutations.test.d.ts +1 -0
  212. package/dist/goals/__tests__/mutations.test.js +95 -0
  213. package/dist/goals/__tests__/parse-canonical.test.d.ts +1 -0
  214. package/dist/goals/__tests__/parse-canonical.test.js +129 -0
  215. package/dist/goals/__tests__/validate.test.d.ts +1 -0
  216. package/dist/goals/__tests__/validate.test.js +183 -0
  217. package/dist/goals/index.d.ts +6 -0
  218. package/dist/goals/index.js +4 -0
  219. package/dist/goals/layout.d.ts +11 -0
  220. package/dist/goals/layout.js +147 -0
  221. package/dist/goals/mutations.d.ts +6 -0
  222. package/dist/goals/mutations.js +116 -0
  223. package/dist/goals/parse-canonical.d.ts +29 -0
  224. package/dist/goals/parse-canonical.js +257 -0
  225. package/dist/goals/types.d.ts +71 -0
  226. package/dist/goals/types.js +1 -0
  227. package/dist/goals/validate.d.ts +2 -0
  228. package/dist/goals/validate.js +122 -0
  229. package/dist/index.d.ts +23 -0
  230. package/dist/index.js +23 -0
  231. package/dist/issues/__tests__/layout.test.d.ts +1 -0
  232. package/dist/issues/__tests__/layout.test.js +62 -0
  233. package/dist/issues/__tests__/validate.test.d.ts +1 -0
  234. package/dist/issues/__tests__/validate.test.js +82 -0
  235. package/dist/issues/index.d.ts +4 -0
  236. package/dist/issues/index.js +2 -0
  237. package/dist/issues/layout.d.ts +10 -0
  238. package/dist/issues/layout.js +82 -0
  239. package/dist/issues/types.d.ts +61 -0
  240. package/dist/issues/types.js +6 -0
  241. package/dist/issues/validate.d.ts +3 -0
  242. package/dist/issues/validate.js +143 -0
  243. package/dist/process-blueprint/__tests__/compliance-lane.test.d.ts +1 -0
  244. package/dist/process-blueprint/__tests__/compliance-lane.test.js +272 -0
  245. package/dist/process-blueprint/__tests__/layout.test.d.ts +1 -0
  246. package/dist/process-blueprint/__tests__/layout.test.js +159 -0
  247. package/dist/process-blueprint/__tests__/validate.test.d.ts +1 -0
  248. package/dist/process-blueprint/__tests__/validate.test.js +280 -0
  249. package/dist/process-blueprint/index.d.ts +4 -0
  250. package/dist/process-blueprint/index.js +2 -0
  251. package/dist/process-blueprint/layout.d.ts +2 -0
  252. package/dist/process-blueprint/layout.js +416 -0
  253. package/dist/process-blueprint/types.d.ts +220 -0
  254. package/dist/process-blueprint/types.js +1 -0
  255. package/dist/process-blueprint/validate.d.ts +3 -0
  256. package/dist/process-blueprint/validate.js +165 -0
  257. package/dist/process-map/__tests__/validate.test.d.ts +1 -0
  258. package/dist/process-map/__tests__/validate.test.js +199 -0
  259. package/dist/process-map/index.d.ts +2 -0
  260. package/dist/process-map/index.js +1 -0
  261. package/dist/process-map/types.d.ts +32 -0
  262. package/dist/process-map/types.js +1 -0
  263. package/dist/process-map/validate.d.ts +3 -0
  264. package/dist/process-map/validate.js +115 -0
  265. package/dist/products/__tests__/validate.test.d.ts +1 -0
  266. package/dist/products/__tests__/validate.test.js +210 -0
  267. package/dist/products/index.d.ts +2 -0
  268. package/dist/products/index.js +1 -0
  269. package/dist/products/types.d.ts +28 -0
  270. package/dist/products/types.js +1 -0
  271. package/dist/products/validate.d.ts +3 -0
  272. package/dist/products/validate.js +93 -0
  273. package/dist/repo-validate/__tests__/validate-repo.test.d.ts +1 -0
  274. package/dist/repo-validate/__tests__/validate-repo.test.js +132 -0
  275. package/dist/repo-validate/index.d.ts +2 -0
  276. package/dist/repo-validate/index.js +1 -0
  277. package/dist/repo-validate/types.d.ts +34 -0
  278. package/dist/repo-validate/types.js +14 -0
  279. package/dist/repo-validate/validate-repo.d.ts +8 -0
  280. package/dist/repo-validate/validate-repo.js +189 -0
  281. package/dist/requirement/__tests__/example.test.d.ts +1 -0
  282. package/dist/requirement/__tests__/example.test.js +21 -0
  283. package/dist/requirement/__tests__/validate.test.d.ts +1 -0
  284. package/dist/requirement/__tests__/validate.test.js +80 -0
  285. package/dist/requirement/index.d.ts +4 -0
  286. package/dist/requirement/index.js +2 -0
  287. package/dist/requirement/types.d.ts +25 -0
  288. package/dist/requirement/types.js +4 -0
  289. package/dist/requirement/validate.d.ts +7 -0
  290. package/dist/requirement/validate.js +81 -0
  291. package/dist/scenarios/__tests__/validate.test.d.ts +1 -0
  292. package/dist/scenarios/__tests__/validate.test.js +183 -0
  293. package/dist/scenarios/index.d.ts +2 -0
  294. package/dist/scenarios/index.js +1 -0
  295. package/dist/scenarios/types.d.ts +45 -0
  296. package/dist/scenarios/types.js +1 -0
  297. package/dist/scenarios/validate.d.ts +3 -0
  298. package/dist/scenarios/validate.js +110 -0
  299. package/dist/schema-version.d.ts +16 -0
  300. package/dist/schema-version.js +16 -0
  301. package/dist/scope.d.ts +23 -0
  302. package/dist/scope.js +38 -0
  303. package/dist/stakeholder/__tests__/validate.test.d.ts +1 -0
  304. package/dist/stakeholder/__tests__/validate.test.js +86 -0
  305. package/dist/stakeholder/index.d.ts +3 -0
  306. package/dist/stakeholder/index.js +2 -0
  307. package/dist/stakeholder/types.d.ts +24 -0
  308. package/dist/stakeholder/types.js +5 -0
  309. package/dist/stakeholder/validate.d.ts +2 -0
  310. package/dist/stakeholder/validate.js +58 -0
  311. package/dist/target-state/__tests__/validate.test.d.ts +1 -0
  312. package/dist/target-state/__tests__/validate.test.js +62 -0
  313. package/dist/target-state/index.d.ts +2 -0
  314. package/dist/target-state/index.js +1 -0
  315. package/dist/target-state/types.d.ts +18 -0
  316. package/dist/target-state/types.js +7 -0
  317. package/dist/target-state/validate.d.ts +2 -0
  318. package/dist/target-state/validate.js +61 -0
  319. package/dist/theme/index.d.ts +4 -0
  320. package/dist/theme/index.js +2 -0
  321. package/dist/theme/themes.d.ts +16 -0
  322. package/dist/theme/themes.js +213 -0
  323. package/dist/theme/tokens.d.ts +158 -0
  324. package/dist/theme/tokens.js +105 -0
  325. package/dist/typed-id.d.ts +20 -0
  326. package/dist/typed-id.js +38 -0
  327. package/dist/validation-types.d.ts +34 -0
  328. package/dist/validation-types.js +1 -0
  329. package/dist/webview/__tests__/entry-notations.test.d.ts +1 -0
  330. package/dist/webview/__tests__/entry-notations.test.js +62 -0
  331. package/dist/webview/__tests__/entry.test.d.ts +1 -0
  332. package/dist/webview/__tests__/entry.test.js +56 -0
  333. package/dist/webview/__tests__/render-goals.test.d.ts +1 -0
  334. package/dist/webview/__tests__/render-goals.test.js +65 -0
  335. package/dist/webview/entry.d.ts +38 -0
  336. package/dist/webview/entry.js +182 -0
  337. package/dist/webview/render-activities.d.ts +24 -0
  338. package/dist/webview/render-activities.js +127 -0
  339. package/dist/webview/render-activity-card.d.ts +5 -0
  340. package/dist/webview/render-activity-card.js +168 -0
  341. package/dist/webview/render-applications.d.ts +11 -0
  342. package/dist/webview/render-applications.js +105 -0
  343. package/dist/webview/render-blocks.d.ts +5 -0
  344. package/dist/webview/render-blocks.js +71 -0
  345. package/dist/webview/render-capability-map.d.ts +7 -0
  346. package/dist/webview/render-capability-map.js +88 -0
  347. package/dist/webview/render-fgca.d.ts +8 -0
  348. package/dist/webview/render-fgca.js +94 -0
  349. package/dist/webview/render-goals.d.ts +6 -0
  350. package/dist/webview/render-goals.js +92 -0
  351. package/dist/webview/render-process-blueprint.d.ts +5 -0
  352. package/dist/webview/render-process-blueprint.js +135 -0
  353. package/dist/webview/render-process-map.d.ts +7 -0
  354. package/dist/webview/render-process-map.js +82 -0
  355. package/dist/webview/render-products.d.ts +7 -0
  356. package/dist/webview/render-products.js +81 -0
  357. package/dist/webview/render-scenarios.d.ts +7 -0
  358. package/dist/webview/render-scenarios.js +75 -0
  359. package/dist/webview/render-util.d.ts +16 -0
  360. package/dist/webview/render-util.js +31 -0
  361. package/dist/webview/transitrix-render.css +323 -0
  362. package/dist/webview/transitrix-render.js +6452 -0
  363. package/dist/webview/transitrix-render.js.map +7 -0
  364. package/dist/yaml-normalize.d.ts +20 -0
  365. package/dist/yaml-normalize.js +57 -0
  366. package/package.json +40 -0
  367. package/src/__tests__/edge-path.test.ts +46 -0
  368. package/src/__tests__/scope.test.ts +27 -0
  369. package/src/__tests__/yaml-normalize.test.ts +158 -0
  370. package/src/activities/__tests__/cpm.test.ts +164 -0
  371. package/src/activities/__tests__/gantt.test.ts +245 -0
  372. package/src/activities/__tests__/layout.test.ts +112 -0
  373. package/src/activities/__tests__/validate.test.ts +409 -0
  374. package/src/activities/cpm.ts +95 -0
  375. package/src/activities/gantt.ts +276 -0
  376. package/src/activities/index.ts +28 -0
  377. package/src/activities/layout.ts +112 -0
  378. package/src/activities/types.ts +148 -0
  379. package/src/activities/validate.ts +357 -0
  380. package/src/activity-card/__tests__/example.test.ts +66 -0
  381. package/src/activity-card/__tests__/layout.test.ts +63 -0
  382. package/src/activity-card/__tests__/resolver.test.ts +161 -0
  383. package/src/activity-card/__tests__/validate.test.ts +131 -0
  384. package/src/activity-card/index.ts +32 -0
  385. package/src/activity-card/layout.ts +194 -0
  386. package/src/activity-card/resolver.ts +337 -0
  387. package/src/activity-card/types.ts +237 -0
  388. package/src/activity-card/validate.ts +158 -0
  389. package/src/actor/__tests__/validate.test.ts +67 -0
  390. package/src/actor/index.ts +3 -0
  391. package/src/actor/types.ts +29 -0
  392. package/src/actor/validate.ts +38 -0
  393. package/src/applications/__tests__/validate.test.ts +256 -0
  394. package/src/applications/index.ts +11 -0
  395. package/src/applications/types.ts +43 -0
  396. package/src/applications/validate.ts +131 -0
  397. package/src/assertion/__tests__/example.test.ts +37 -0
  398. package/src/assertion/__tests__/validate.test.ts +142 -0
  399. package/src/assertion/index.ts +12 -0
  400. package/src/assertion/types.ts +58 -0
  401. package/src/assertion/validate.ts +145 -0
  402. package/src/blocks/__tests__/layout.test.ts +169 -0
  403. package/src/blocks/__tests__/validate.test.ts +260 -0
  404. package/src/blocks/index.ts +17 -0
  405. package/src/blocks/layout.ts +186 -0
  406. package/src/blocks/types.ts +62 -0
  407. package/src/blocks/validate.ts +206 -0
  408. package/src/capability-map/__tests__/validate.test.ts +238 -0
  409. package/src/capability-map/index.ts +8 -0
  410. package/src/capability-map/types.ts +29 -0
  411. package/src/capability-map/validate.ts +127 -0
  412. package/src/change/__tests__/validate.test.ts +88 -0
  413. package/src/change/index.ts +2 -0
  414. package/src/change/types.ts +23 -0
  415. package/src/change/validate.ts +61 -0
  416. package/src/compliance/__tests__/classify.test.ts +27 -0
  417. package/src/compliance/__tests__/confidence.test.ts +98 -0
  418. package/src/compliance/__tests__/coverage-metric.test.ts +188 -0
  419. package/src/compliance/__tests__/demo-scenario.test.ts +273 -0
  420. package/src/compliance/__tests__/example.test.ts +57 -0
  421. package/src/compliance/__tests__/gap-report.test.ts +129 -0
  422. package/src/compliance/__tests__/html.test.ts +142 -0
  423. package/src/compliance/__tests__/impact.test.ts +482 -0
  424. package/src/compliance/__tests__/markdown.test.ts +95 -0
  425. package/src/compliance/__tests__/views.test.ts +64 -0
  426. package/src/compliance/classify.ts +84 -0
  427. package/src/compliance/confidence.ts +69 -0
  428. package/src/compliance/coverage-metric.ts +263 -0
  429. package/src/compliance/gap-report.ts +94 -0
  430. package/src/compliance/html.ts +295 -0
  431. package/src/compliance/impact.ts +677 -0
  432. package/src/compliance/index.ts +56 -0
  433. package/src/compliance/markdown.ts +135 -0
  434. package/src/compliance/reverse-index.ts +33 -0
  435. package/src/compliance/types.ts +120 -0
  436. package/src/compliance/views.ts +46 -0
  437. package/src/compliance-matrix/__tests__/build.test.ts +200 -0
  438. package/src/compliance-matrix/__tests__/example.test.ts +57 -0
  439. package/src/compliance-matrix/build.ts +0 -0
  440. package/src/compliance-matrix/index.ts +11 -0
  441. package/src/compliance-matrix/types.ts +102 -0
  442. package/src/confidence/__tests__/score.test.ts +254 -0
  443. package/src/confidence/index.ts +21 -0
  444. package/src/confidence/score.ts +243 -0
  445. package/src/confidence/types.ts +96 -0
  446. package/src/edge-path.ts +44 -0
  447. package/src/element-envelope.ts +76 -0
  448. package/src/factor/__tests__/validate.test.ts +98 -0
  449. package/src/factor/index.ts +8 -0
  450. package/src/factor/types.ts +41 -0
  451. package/src/factor/validate.ts +74 -0
  452. package/src/fgca/__tests__/chain-table.test.ts +172 -0
  453. package/src/fgca/__tests__/layout.test.ts +179 -0
  454. package/src/fgca/__tests__/parse-canonical.test.ts +248 -0
  455. package/src/fgca/__tests__/preview-layout.test.ts +124 -0
  456. package/src/fgca/__tests__/validate.test.ts +142 -0
  457. package/src/fgca/chain-table.ts +225 -0
  458. package/src/fgca/index.ts +45 -0
  459. package/src/fgca/layout.ts +204 -0
  460. package/src/fgca/nodeTypes.ts +11 -0
  461. package/src/fgca/nodes/FGCAActivityNode.tsx +65 -0
  462. package/src/fgca/nodes/FGCAChangeNode.tsx +64 -0
  463. package/src/fgca/nodes/FGCAFactorNode.tsx +64 -0
  464. package/src/fgca/nodes/FGCAGoalNode.tsx +72 -0
  465. package/src/fgca/parse-canonical.ts +400 -0
  466. package/src/fgca/preview-layout.ts +217 -0
  467. package/src/fgca/types.ts +50 -0
  468. package/src/fgca/validate.ts +126 -0
  469. package/src/geometry.ts +9 -0
  470. package/src/goals/__tests__/layout.test.ts +175 -0
  471. package/src/goals/__tests__/mutations.test.ts +111 -0
  472. package/src/goals/__tests__/parse-canonical.test.ts +145 -0
  473. package/src/goals/__tests__/validate.test.ts +202 -0
  474. package/src/goals/index.ts +21 -0
  475. package/src/goals/layout.ts +172 -0
  476. package/src/goals/mutations.ts +123 -0
  477. package/src/goals/parse-canonical.ts +286 -0
  478. package/src/goals/types.ts +85 -0
  479. package/src/goals/validate.ts +136 -0
  480. package/src/index.ts +23 -0
  481. package/src/process-blueprint/__tests__/compliance-lane.test.ts +320 -0
  482. package/src/process-blueprint/__tests__/layout.test.ts +195 -0
  483. package/src/process-blueprint/__tests__/validate.test.ts +310 -0
  484. package/src/process-blueprint/index.ts +31 -0
  485. package/src/process-blueprint/layout.ts +531 -0
  486. package/src/process-blueprint/types.ts +242 -0
  487. package/src/process-blueprint/validate.ts +181 -0
  488. package/src/process-map/__tests__/validate.test.ts +223 -0
  489. package/src/process-map/index.ts +10 -0
  490. package/src/process-map/types.ts +36 -0
  491. package/src/process-map/validate.ts +135 -0
  492. package/src/products/__tests__/validate.test.ts +239 -0
  493. package/src/products/index.ts +9 -0
  494. package/src/products/types.ts +31 -0
  495. package/src/products/validate.ts +112 -0
  496. package/src/repo-validate/__tests__/validate-repo.test.ts +149 -0
  497. package/src/repo-validate/index.ts +2 -0
  498. package/src/repo-validate/types.ts +50 -0
  499. package/src/repo-validate/validate-repo.ts +196 -0
  500. package/src/requirement/__tests__/example.test.ts +24 -0
  501. package/src/requirement/__tests__/validate.test.ts +96 -0
  502. package/src/requirement/index.ts +4 -0
  503. package/src/requirement/types.ts +35 -0
  504. package/src/requirement/validate.ts +92 -0
  505. package/src/scenarios/__tests__/validate.test.ts +209 -0
  506. package/src/scenarios/index.ts +15 -0
  507. package/src/scenarios/types.ts +37 -0
  508. package/src/scenarios/validate.ts +122 -0
  509. package/src/schema-version.ts +16 -0
  510. package/src/scope.ts +46 -0
  511. package/src/stakeholder/__tests__/validate.test.ts +101 -0
  512. package/src/stakeholder/index.ts +3 -0
  513. package/src/stakeholder/types.ts +33 -0
  514. package/src/stakeholder/validate.ts +63 -0
  515. package/src/target-state/__tests__/validate.test.ts +74 -0
  516. package/src/target-state/index.ts +2 -0
  517. package/src/target-state/types.ts +28 -0
  518. package/src/target-state/validate.ts +67 -0
  519. package/src/theme/index.ts +4 -0
  520. package/src/theme/themes.ts +237 -0
  521. package/src/theme/tokens.ts +133 -0
  522. package/src/typed-id.ts +56 -0
  523. package/src/validation-types.ts +36 -0
  524. package/src/webview/__tests__/entry-notations.test.ts +77 -0
  525. package/src/webview/__tests__/entry.test.ts +64 -0
  526. package/src/webview/__tests__/render-goals.test.ts +72 -0
  527. package/src/webview/entry.ts +292 -0
  528. package/src/webview/render-activities.ts +156 -0
  529. package/src/webview/render-activity-card.ts +234 -0
  530. package/src/webview/render-applications.ts +123 -0
  531. package/src/webview/render-blocks.ts +91 -0
  532. package/src/webview/render-capability-map.ts +98 -0
  533. package/src/webview/render-fgca.ts +122 -0
  534. package/src/webview/render-goals.ts +110 -0
  535. package/src/webview/render-process-blueprint.ts +207 -0
  536. package/src/webview/render-process-map.ts +93 -0
  537. package/src/webview/render-products.ts +91 -0
  538. package/src/webview/render-scenarios.ts +90 -0
  539. package/src/webview/render-util.ts +34 -0
  540. package/src/webview/styles.css +323 -0
  541. package/src/yaml-normalize.ts +56 -0
@@ -0,0 +1,357 @@
1
+ import type {
2
+ ActivityDoc,
3
+ Activity,
4
+ ActivityValidationResult,
5
+ ActivityValidationError,
6
+ ActivityValidationWarning,
7
+ } from './types.js';
8
+
9
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
10
+ const VALID_WEEKDAYS = new Set(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']);
11
+
12
+ export function validateActivities(input: unknown): ActivityValidationResult {
13
+ const errors: ActivityValidationError[] = [];
14
+ const warnings: ActivityValidationWarning[] = [];
15
+
16
+ if (!input || typeof input !== 'object') {
17
+ return { valid: false, errors: [{ code: 'ACT-001', message: 'Input must be an object' }], warnings };
18
+ }
19
+
20
+ const raw = input as Record<string, unknown>;
21
+
22
+ // ACT-001: notation must equal "activities"
23
+ if (raw.notation === undefined) {
24
+ errors.push({ code: 'ACT-001', message: 'notation field is required' });
25
+ return { valid: false, errors, warnings };
26
+ }
27
+ if (raw.notation !== 'activities') {
28
+ errors.push({ code: 'ACT-001', message: `notation must be "activities", got "${String(raw.notation)}"` });
29
+ return { valid: false, errors, warnings };
30
+ }
31
+
32
+ if (!Array.isArray(raw.activities)) {
33
+ errors.push({ code: 'SCHEMA_INVALID', message: 'activities must be an array', path: 'activities' });
34
+ return { valid: false, errors, warnings };
35
+ }
36
+
37
+ // ACT-014 / ACT-015: project.calendar validation (run before per-activity loop;
38
+ // surfaces clearly even when activities also have issues).
39
+ if (raw.project && typeof raw.project === 'object') {
40
+ const project = raw.project as Record<string, unknown>;
41
+ const cal = project.calendar;
42
+ if (cal && typeof cal === 'object') {
43
+ const calendar = cal as Record<string, unknown>;
44
+ if (calendar.working_days !== undefined) {
45
+ if (!Array.isArray(calendar.working_days)) {
46
+ errors.push({ code: 'ACT-014', message: 'project.calendar.working_days must be an array of weekday names', path: 'project.calendar.working_days' });
47
+ } else {
48
+ const seen = new Set<string>();
49
+ for (let i = 0; i < calendar.working_days.length; i++) {
50
+ const raw = calendar.working_days[i];
51
+ const day = typeof raw === 'string' ? raw.toLowerCase() : '';
52
+ if (!VALID_WEEKDAYS.has(day)) {
53
+ errors.push({
54
+ code: 'ACT-014',
55
+ message: `project.calendar.working_days[${i}] "${String(raw)}" must be one of: mon, tue, wed, thu, fri, sat, sun`,
56
+ path: `project.calendar.working_days[${i}]`,
57
+ });
58
+ } else if (seen.has(day)) {
59
+ errors.push({
60
+ code: 'ACT-014',
61
+ message: `project.calendar.working_days has duplicate entry "${day}"`,
62
+ path: `project.calendar.working_days[${i}]`,
63
+ });
64
+ } else {
65
+ seen.add(day);
66
+ }
67
+ }
68
+ }
69
+ }
70
+ if (calendar.holidays !== undefined) {
71
+ if (!Array.isArray(calendar.holidays)) {
72
+ errors.push({ code: 'ACT-015', message: 'project.calendar.holidays must be an array of ISO 8601 dates', path: 'project.calendar.holidays' });
73
+ } else {
74
+ for (let i = 0; i < calendar.holidays.length; i++) {
75
+ const h = calendar.holidays[i];
76
+ if (typeof h !== 'string' || !DATE_RE.test(h)) {
77
+ errors.push({
78
+ code: 'ACT-015',
79
+ message: `project.calendar.holidays[${i}] "${String(h)}" must be an ISO 8601 date (YYYY-MM-DD)`,
80
+ path: `project.calendar.holidays[${i}]`,
81
+ });
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ const doc = raw as unknown as ActivityDoc;
90
+ const idSet = new Set<string>();
91
+
92
+ for (let i = 0; i < doc.activities.length; i++) {
93
+ const a = doc.activities[i] as unknown;
94
+ const path = `activities[${i}]`;
95
+
96
+ if (!a || typeof a !== 'object') {
97
+ errors.push({ code: 'SCHEMA_INVALID', message: 'activity entry must be an object', path });
98
+ continue;
99
+ }
100
+ const act = a as Record<string, unknown>;
101
+
102
+ // ACT-010: reject single-value forms
103
+ if ('goal' in act) {
104
+ errors.push({ code: 'ACT-010', message: `Use "goals: []" array form instead of "goal:" (activity at index ${i})`, path });
105
+ }
106
+ if ('predecessor' in act) {
107
+ errors.push({ code: 'ACT-010', message: `Use "predecessors: []" array form instead of "predecessor:" (activity at index ${i})`, path });
108
+ }
109
+ if ('tag' in act) {
110
+ errors.push({ code: 'ACT-010', message: `Use "tags: []" array form instead of "tag:" (activity at index ${i})`, path });
111
+ }
112
+
113
+ // ACT-002: non-empty id
114
+ if (!act.id || typeof act.id !== 'string' || (act.id as string).trim() === '') {
115
+ errors.push({ code: 'ACT-002', message: `Activity at index ${i} is missing a non-empty id`, path });
116
+ continue;
117
+ }
118
+ const id = act.id as string;
119
+
120
+ // ACT-003: non-empty name
121
+ if (!act.name || typeof act.name !== 'string' || (act.name as string).trim() === '') {
122
+ errors.push({ code: 'ACT-003', message: `Activity "${id}" is missing a non-empty name`, path });
123
+ }
124
+
125
+ // ACT-004: unique ids
126
+ if (idSet.has(id)) {
127
+ errors.push({ code: 'ACT-004', message: `Duplicate activity id: "${id}"`, path });
128
+ } else {
129
+ idSet.add(id);
130
+ }
131
+
132
+ // ACT-009: non-negative numeric fields
133
+ const numericFields = ['duration', 'labor_cost', 'resources_cost', 'effort', 'score', 'sort'] as const;
134
+ for (const field of numericFields) {
135
+ const val = act[field];
136
+ if (val !== undefined && val !== null) {
137
+ if (typeof val !== 'number' || val < 0) {
138
+ errors.push({ code: 'ACT-009', message: `Activity "${id}" field "${field}" must be a non-negative number, got ${String(val)}`, path });
139
+ }
140
+ }
141
+ }
142
+
143
+ // ACT-011: warn if no duration
144
+ if (act.duration === undefined || act.duration === null) {
145
+ warnings.push({ code: 'ACT-011', message: `Activity "${id}" has no duration — cannot participate in CPM analysis`, path });
146
+ }
147
+ }
148
+
149
+ if (errors.length > 0) return { valid: false, errors, warnings };
150
+
151
+ // Second pass — checks that need the full id set
152
+ for (let i = 0; i < doc.activities.length; i++) {
153
+ const a = doc.activities[i];
154
+ const path = `activities[${i}]`;
155
+
156
+ // ACT-007: self-loop
157
+ if (Array.isArray(a.predecessors) && a.predecessors.includes(a.id)) {
158
+ errors.push({ code: 'ACT-007', message: `Activity "${a.id}" lists itself as a predecessor`, path });
159
+ }
160
+
161
+ // ACT-005: predecessor existence (intra-document)
162
+ for (const predId of (a.predecessors ?? [])) {
163
+ if (!idSet.has(predId)) {
164
+ errors.push({ code: 'ACT-005', message: `Activity "${a.id}" references unknown predecessor "${predId}"`, path });
165
+ }
166
+ }
167
+
168
+ // ACT-008: date format validation + end_date >= start_date.
169
+ // The orchestrator's pre-release review flagged that the raw lexicographic
170
+ // string compare is only correct for strict YYYY-MM-DD, with no format
171
+ // check applied. Now we validate the format first; the order compare runs
172
+ // only when both dates are valid (otherwise the comparison message would
173
+ // be misleading).
174
+ let startValid = true;
175
+ let endValid = true;
176
+ if (a.start_date !== undefined) {
177
+ if (typeof a.start_date !== 'string' || !DATE_RE.test(a.start_date)) {
178
+ errors.push({ code: 'ACT-008', message: `Activity "${a.id}" start_date "${String(a.start_date)}" must be ISO 8601 YYYY-MM-DD`, path });
179
+ startValid = false;
180
+ }
181
+ }
182
+ if (a.end_date !== undefined) {
183
+ if (typeof a.end_date !== 'string' || !DATE_RE.test(a.end_date)) {
184
+ errors.push({ code: 'ACT-008', message: `Activity "${a.id}" end_date "${String(a.end_date)}" must be ISO 8601 YYYY-MM-DD`, path });
185
+ endValid = false;
186
+ }
187
+ }
188
+ if (a.start_date && a.end_date && startValid && endValid) {
189
+ if (a.end_date < a.start_date) {
190
+ errors.push({ code: 'ACT-008', message: `Activity "${a.id}" end_date "${a.end_date}" is before start_date "${a.start_date}"`, path });
191
+ }
192
+ }
193
+ }
194
+
195
+ if (errors.length > 0) return { valid: false, errors, warnings };
196
+
197
+ // ACT-006: no cycles
198
+ const cycleError = detectCycle(doc.activities);
199
+ if (cycleError) errors.push(cycleError);
200
+
201
+ if (errors.length > 0) return { valid: false, errors, warnings };
202
+
203
+ // ACT-012: date/duration consistency (warning only)
204
+ for (let i = 0; i < doc.activities.length; i++) {
205
+ const a = doc.activities[i];
206
+ if (a.start_date && a.end_date && a.duration !== undefined) {
207
+ const start = new Date(a.start_date).getTime();
208
+ const end = new Date(a.end_date).getTime();
209
+ const diffDays = Math.round((end - start) / 86400000);
210
+ if (Math.abs(diffDays - a.duration) > 1) {
211
+ warnings.push({
212
+ code: 'ACT-012',
213
+ message: `Activity "${a.id}" duration ${a.duration} is inconsistent with date range of ~${diffDays} days`,
214
+ path: `activities[${i}]`,
215
+ });
216
+ }
217
+ }
218
+ }
219
+
220
+ // ACT-013: orphan activities (warning only)
221
+ const successorIds = new Set<string>();
222
+ for (const a of doc.activities) {
223
+ for (const pred of (a.predecessors ?? [])) successorIds.add(pred);
224
+ }
225
+ for (let i = 0; i < doc.activities.length; i++) {
226
+ const a = doc.activities[i];
227
+ const hasSuccessors = successorIds.has(a.id);
228
+ const hasGoals = Array.isArray(a.goals) && a.goals.length > 0;
229
+ const hasPredecessors = Array.isArray(a.predecessors) && a.predecessors.length > 0;
230
+ if (doc.activities.length > 1 && !hasSuccessors && !hasGoals && !hasPredecessors) {
231
+ warnings.push({
232
+ code: 'ACT-013',
233
+ message: `Activity "${a.id}" appears to be structurally orphan (no predecessors, successors, or goals)`,
234
+ path: `activities[${i}]`,
235
+ });
236
+ }
237
+ }
238
+
239
+ // ACT-016: milestone (duration=0) with both pinned dates MUST have start == end.
240
+ for (let i = 0; i < doc.activities.length; i++) {
241
+ const a = doc.activities[i];
242
+ if (a.duration === 0 && a.start_date && a.end_date && a.start_date !== a.end_date) {
243
+ errors.push({
244
+ code: 'ACT-016',
245
+ message: `Milestone "${a.id}" has duration 0 but start_date "${a.start_date}" ≠ end_date "${a.end_date}"`,
246
+ path: `activities[${i}]`,
247
+ });
248
+ }
249
+ }
250
+
251
+ if (errors.length > 0) return { valid: false, errors, warnings };
252
+
253
+ // ACT-017 / ACT-018: phase warnings. A "phase" is an activity that is referenced
254
+ // by another activity's `parent` field. ACT-017 warns when a phase carries its
255
+ // own duration/dates (those roll up from children). ACT-018 warns when an
256
+ // activity that looks like a phase (no duration, no pinned dates) has no
257
+ // children — possibly an unused placeholder.
258
+ const childCount = new Map<string, number>();
259
+ for (const a of doc.activities) {
260
+ if (typeof a.parent === 'string' && a.parent.length > 0) {
261
+ childCount.set(a.parent, (childCount.get(a.parent) ?? 0) + 1);
262
+ }
263
+ }
264
+ for (let i = 0; i < doc.activities.length; i++) {
265
+ const a = doc.activities[i];
266
+ const isReferencedAsParent = (childCount.get(a.id) ?? 0) > 0;
267
+ if (isReferencedAsParent) {
268
+ const carriesOwnTiming =
269
+ (a.duration !== undefined && a.duration !== null) ||
270
+ typeof a.start_date === 'string' ||
271
+ typeof a.end_date === 'string';
272
+ if (carriesOwnTiming) {
273
+ warnings.push({
274
+ code: 'ACT-017',
275
+ message: `Phase "${a.id}" carries its own duration/dates — those should roll up from children, not be authored directly`,
276
+ path: `activities[${i}]`,
277
+ });
278
+ }
279
+ } else {
280
+ // Not referenced as parent. ACT-018 only fires when the activity LOOKS
281
+ // like a phase: no duration declared and no pinned dates. A leaf without
282
+ // duration is already covered by ACT-011 — the same activity may surface
283
+ // both warnings, which is correct (separate concerns: CPM-unfit vs
284
+ // empty-phase).
285
+ const looksLikePhase =
286
+ (a.duration === undefined || a.duration === null) &&
287
+ !a.start_date &&
288
+ !a.end_date;
289
+ if (looksLikePhase) {
290
+ warnings.push({
291
+ code: 'ACT-018',
292
+ message: `Activity "${a.id}" has no duration and no children — may be an empty phase`,
293
+ path: `activities[${i}]`,
294
+ });
295
+ }
296
+ }
297
+ }
298
+
299
+ // ACT-019: Gantt view will not render when neither project.start_date nor
300
+ // any per-activity pinned date pair is present. Network view is unaffected.
301
+ const hasProjectStart = typeof doc.project?.start_date === 'string' && doc.project.start_date.length > 0;
302
+ const hasPinnedActivity = doc.activities.some(
303
+ (a) => typeof a.start_date === 'string' && typeof a.end_date === 'string',
304
+ );
305
+ if (!hasProjectStart && !hasPinnedActivity) {
306
+ warnings.push({
307
+ code: 'ACT-019',
308
+ message: 'Gantt view will not render: project.start_date is absent and no activity has both start_date and end_date pinned. Network view is unaffected.',
309
+ });
310
+ }
311
+
312
+ return { valid: errors.length === 0, errors, warnings };
313
+ }
314
+
315
+ function detectCycle(activities: Activity[]): ActivityValidationError | null {
316
+ const successors = new Map<string, string[]>();
317
+ for (const a of activities) {
318
+ successors.set(a.id, []);
319
+ }
320
+ for (const a of activities) {
321
+ for (const pred of (a.predecessors ?? [])) {
322
+ const list = successors.get(pred);
323
+ if (list) list.push(a.id);
324
+ }
325
+ }
326
+
327
+ // Kahn's algorithm: if we can't process all nodes, there's a cycle
328
+ const inDegree = new Map<string, number>();
329
+ for (const a of activities) inDegree.set(a.id, 0);
330
+ for (const a of activities) {
331
+ for (const pred of (a.predecessors ?? [])) {
332
+ inDegree.set(a.id, (inDegree.get(a.id) ?? 0) + 1);
333
+ }
334
+ }
335
+
336
+ const queue: string[] = [];
337
+ for (const [id, deg] of inDegree) {
338
+ if (deg === 0) queue.push(id);
339
+ }
340
+
341
+ let processed = 0;
342
+ while (queue.length > 0) {
343
+ const id = queue.shift()!;
344
+ processed++;
345
+ for (const succ of (successors.get(id) ?? [])) {
346
+ const newDeg = (inDegree.get(succ) ?? 1) - 1;
347
+ inDegree.set(succ, newDeg);
348
+ if (newDeg === 0) queue.push(succ);
349
+ }
350
+ }
351
+
352
+ if (processed < activities.length) {
353
+ const cycleNodes = activities.filter(a => (inDegree.get(a.id) ?? 0) > 0).map(a => a.id);
354
+ return { code: 'ACT-006', message: `Cycle detected in activity dependency graph involving: ${cycleNodes.join(', ')}` };
355
+ }
356
+ return null;
357
+ }
@@ -0,0 +1,66 @@
1
+ // Conformance test for the shipped worked example. Exercises the full
2
+ // validate → resolve → layout pipeline against the real co-located example
3
+ // files under `tests/fixtures/notation-corpus/activity-card/`, so the success signal of #134 is
4
+ // pinned and any drift in the example or the resolver is caught by CI.
5
+
6
+ import { describe, it, expect } from 'vitest';
7
+ import { readFileSync, readdirSync } from 'node:fs';
8
+ import { fileURLToPath } from 'node:url';
9
+ import path from 'node:path';
10
+ import yaml from 'js-yaml';
11
+ import { validateActivityCard } from '../validate.js';
12
+ import { resolveActivityCard } from '../resolver.js';
13
+ import { layoutActivityCard } from '../layout.js';
14
+ import type { ActivityCardDoc } from '../types.js';
15
+
16
+ const dir = path.resolve(
17
+ path.dirname(fileURLToPath(import.meta.url)),
18
+ '../../../../../tests/fixtures/notation-corpus/activity-card',
19
+ );
20
+ const load = (f: string): unknown => yaml.load(readFileSync(path.join(dir, f), 'utf-8'));
21
+
22
+ /** Recursively load every `*.yaml` doc under a canon subtree (mirrors the
23
+ * extension's element/relation walk). */
24
+ function loadYamlTree(root: string): unknown[] {
25
+ const out: unknown[] = [];
26
+ for (const entry of readdirSync(root, { withFileTypes: true })) {
27
+ const full = path.join(root, entry.name);
28
+ if (entry.isDirectory()) out.push(...loadYamlTree(full));
29
+ else if (entry.name.endsWith('.yaml')) out.push(yaml.load(readFileSync(full, 'utf-8')));
30
+ }
31
+ return out;
32
+ }
33
+
34
+ describe('activity-card worked example (eu-programme)', () => {
35
+ it('validates, resolves, and lays out the success-signal card', () => {
36
+ const card = load('eu-programme.activity-card.transitrix.yaml');
37
+ const elements = loadYamlTree(path.join(dir, 'canon', 'elements'));
38
+ const relations = loadYamlTree(path.join(dir, 'canon', 'relations'));
39
+
40
+ const v = validateActivityCard(card);
41
+ expect(v.valid, JSON.stringify(v.errors)).toBe(true);
42
+
43
+ const r = resolveActivityCard(card as ActivityCardDoc, { elements, relations });
44
+ expect(r.valid, JSON.stringify(r.errors)).toBe(true);
45
+ expect(r.warnings, JSON.stringify(r.warnings)).toHaveLength(0);
46
+
47
+ const c = r.resolved!;
48
+ expect(c.project.name).toBe('EU MDR conformity programme');
49
+ expect(c.project.valid_from).toBe('2026-04-01');
50
+ expect(c.project.start_date).toBe('2026-04-15');
51
+ expect(c.project.end_date).toBe('2027-03-15');
52
+ expect(c.milestones.map((m) => m.id)).toEqual([
53
+ 'MILESTONE-EU-CONFORMITY-CERT-1',
54
+ 'MILESTONE-EU-MARKET-LAUNCH-1',
55
+ ]);
56
+ expect(c.motivation.factors.map((f) => f.id)).toEqual(['FACTOR-EU-MDR-1']);
57
+ expect(c.motivation.goals.map((g) => g.id)).toEqual(['GOAL-EU-MARKET-1']);
58
+ expect(c.motivation.changes.map((x) => x.id)).toEqual(['CHANGE-EU-COMPLIANCE-1']);
59
+ expect(c.childActivities).toHaveLength(4);
60
+
61
+ const layout = layoutActivityCard(c);
62
+ expect(layout.bounds.height).toBeGreaterThan(0);
63
+ expect(layout.chainEdges).toContainEqual({ sourceId: 'FACTOR-EU-MDR-1', targetId: 'GOAL-EU-MARKET-1' });
64
+ expect(layout.chainEdges).toContainEqual({ sourceId: 'GOAL-EU-MARKET-1', targetId: 'CHANGE-EU-COMPLIANCE-1' });
65
+ });
66
+ });
@@ -0,0 +1,63 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { layoutActivityCard, ARCHIMATE_CLASS } from '../layout.js';
3
+ import type { ResolvedActivityCard } from '../types.js';
4
+
5
+ const RESOLVED: ResolvedActivityCard = {
6
+ cardId: 'ACTIVITY_CARD-EU-PROGRAMME-1',
7
+ cardDescription: 'Summary',
8
+ project: {
9
+ id: 'ACTIVITY-EU-PROGRAMME-1',
10
+ name: 'EU MDR conformity programme',
11
+ valid_from: '2026-04-01',
12
+ start_date: '2026-04-15',
13
+ end_date: '2027-03-15',
14
+ },
15
+ milestones: [
16
+ { id: 'MILESTONE-CERT-1', name: 'Cert', date: '2027-01-31', deliversChanges: ['CHANGE-EU-COMPLIANCE-1'] },
17
+ ],
18
+ motivation: {
19
+ factors: [{ id: 'FACTOR-EU-MDR-1', name: 'EU MDR regulation' }],
20
+ goals: [{ id: 'GOAL-EU-MARKET-1', name: 'Access EU market', factorIds: ['FACTOR-EU-MDR-1'] }],
21
+ changes: [{ id: 'CHANGE-EU-COMPLIANCE-1', name: 'Achieve MDR compliance', goalIds: ['GOAL-EU-MARKET-1'] }],
22
+ },
23
+ childActivities: [
24
+ { id: 'ACTIVITY-EU-CHILD-1', name: 'Notified-body engagement', start_date: '2026-05-01', end_date: '2026-12-01', owner: 'UNIT-REGULATORY' },
25
+ ],
26
+ };
27
+
28
+ describe('layoutActivityCard', () => {
29
+ it('produces all sections with positive geometry', () => {
30
+ const l = layoutActivityCard(RESOLVED);
31
+ expect(l.bounds.width).toBeGreaterThan(0);
32
+ expect(l.bounds.height).toBeGreaterThan(0);
33
+ expect(l.title.name).toBe('EU MDR conformity programme');
34
+ expect(l.dateFields).toHaveLength(3);
35
+ expect(l.dateFields[0]).toMatchObject({ label: 'Initiation', value: '2026-04-01' });
36
+ expect(l.milestones).toHaveLength(1);
37
+ expect(l.milestones[0].archimateClass).toBe(ARCHIMATE_CLASS.MILESTONE);
38
+ expect(l.childActivities[0].archimateClass).toBe(ARCHIMATE_CLASS.ACTIVITY);
39
+ });
40
+
41
+ it('builds F→G and G→C edges', () => {
42
+ const l = layoutActivityCard(RESOLVED);
43
+ expect(l.chainEdges).toContainEqual({ sourceId: 'FACTOR-EU-MDR-1', targetId: 'GOAL-EU-MARKET-1' });
44
+ expect(l.chainEdges).toContainEqual({ sourceId: 'GOAL-EU-MARKET-1', targetId: 'CHANGE-EU-COMPLIANCE-1' });
45
+ });
46
+
47
+ it('renders "—" for absent dates', () => {
48
+ const l = layoutActivityCard({ ...RESOLVED, project: { id: 'A', name: 'n' } });
49
+ expect(l.dateFields.map((d) => d.value)).toEqual(['—', '—', '—']);
50
+ });
51
+
52
+ it('handles an empty card without throwing', () => {
53
+ const l = layoutActivityCard({
54
+ cardId: 'ACTIVITY_CARD-X-1',
55
+ project: { id: 'ACTIVITY-X-1', name: 'Empty' },
56
+ milestones: [],
57
+ motivation: { factors: [], goals: [], changes: [] },
58
+ childActivities: [],
59
+ });
60
+ expect(l.milestones).toHaveLength(0);
61
+ expect(l.bounds.height).toBeGreaterThan(0);
62
+ });
63
+ });
@@ -0,0 +1,161 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { resolveActivityCard } from '../resolver.js';
3
+ import type { ActivityCardDoc } from '../types.js';
4
+
5
+ const CARD: ActivityCardDoc = {
6
+ notation: 'activity-card',
7
+ spec_version: '0.1',
8
+ activity_card: {
9
+ id: 'ACTIVITY_CARD-EU-PROGRAMME-1',
10
+ project: 'ACTIVITY-EU-PROGRAMME-1',
11
+ description: 'Executive summary.',
12
+ milestones: [
13
+ { id: 'MILESTONE-CERT-1', name: 'Cert', date: '2027-01-31', delivers_changes: ['CHANGE-EU-COMPLIANCE-1'] },
14
+ ],
15
+ },
16
+ };
17
+
18
+ // ── Canon element store (one element per object) ─────────────────────────────
19
+ const PROJECT = {
20
+ notation: 'activity',
21
+ id: 'ACTIVITY-EU-PROGRAMME-1',
22
+ name: 'EU MDR conformity programme',
23
+ valid_from: '2026-04-01',
24
+ valid_to: null,
25
+ start_date: '2026-04-15',
26
+ end_date: '2027-03-15',
27
+ delivers_changes: ['CHANGE-EU-COMPLIANCE-1'],
28
+ };
29
+ const CHILD = {
30
+ notation: 'activity',
31
+ id: 'ACTIVITY-EU-CHILD-1',
32
+ name: 'Notified-body engagement',
33
+ parent: 'ACTIVITY-EU-PROGRAMME-1',
34
+ start_date: '2026-05-01',
35
+ end_date: '2026-12-01',
36
+ owner: 'ACTOR-REGULATORY-1',
37
+ };
38
+ const UNRELATED = {
39
+ notation: 'activity',
40
+ id: 'ACTIVITY-UNRELATED-1',
41
+ name: 'Other',
42
+ parent: 'ACTIVITY-SOMETHING-ELSE-1',
43
+ };
44
+ const FACTOR = { notation: 'factor', id: 'FACTOR-EU-MDR-1', name: 'EU MDR regulation' };
45
+ const GOAL = { notation: 'goal', id: 'GOAL-EU-MARKET-1', name: 'Access EU market', factors: ['FACTOR-EU-MDR-1'] };
46
+ const CHANGE = {
47
+ notation: 'change',
48
+ id: 'CHANGE-EU-COMPLIANCE-1',
49
+ name: 'Achieve MDR compliance',
50
+ goals: ['GOAL-EU-MARKET-1'],
51
+ };
52
+
53
+ // ── Canon relation store ─────────────────────────────────────────────────────
54
+ // The project's goal link is a first-class `activity_goal` relation, not an
55
+ // inline `goals:` field on the activity element.
56
+ const REL_GOAL = {
57
+ notation: 'relation',
58
+ id: 'REL-EU-PROGRAMME-GOAL-1',
59
+ type: 'activity_goal',
60
+ from: 'ACTIVITY-EU-PROGRAMME-1',
61
+ to: 'GOAL-EU-MARKET-1',
62
+ valid_from: '2026-04-01',
63
+ valid_to: null,
64
+ };
65
+
66
+ const elements = [PROJECT, CHILD, UNRELATED, FACTOR, GOAL, CHANGE];
67
+ const relations = [REL_GOAL];
68
+ const sources = { elements, relations };
69
+
70
+ describe('resolveActivityCard', () => {
71
+ it('resolves a full card from elements + relations', () => {
72
+ const r = resolveActivityCard(CARD, sources);
73
+ expect(r.valid, JSON.stringify(r.errors)).toBe(true);
74
+ const c = r.resolved!;
75
+ expect(c.project.name).toBe('EU MDR conformity programme');
76
+ expect(c.project.valid_from).toBe('2026-04-01');
77
+ expect(c.project.start_date).toBe('2026-04-15');
78
+ expect(c.milestones).toHaveLength(1);
79
+ expect(c.motivation.factors.map((f) => f.id)).toEqual(['FACTOR-EU-MDR-1']);
80
+ expect(c.motivation.goals.map((g) => g.id)).toEqual(['GOAL-EU-MARKET-1']);
81
+ expect(c.motivation.changes.map((ch) => ch.id)).toEqual(['CHANGE-EU-COMPLIANCE-1']);
82
+ // only the activity whose parent = the project
83
+ expect(c.childActivities.map((a) => a.id)).toEqual(['ACTIVITY-EU-CHILD-1']);
84
+ });
85
+
86
+ it('falls back to the inline goals field when no activity_goal relation exists', () => {
87
+ const projectWithInline = { ...PROJECT, goals: ['GOAL-EU-MARKET-1'] };
88
+ const r = resolveActivityCard(CARD, {
89
+ elements: [projectWithInline, CHILD, FACTOR, GOAL, CHANGE],
90
+ relations: [],
91
+ });
92
+ expect(r.valid).toBe(true);
93
+ expect(r.resolved!.motivation.goals.map((g) => g.id)).toEqual(['GOAL-EU-MARKET-1']);
94
+ });
95
+
96
+ it('PC-001 when the project activity element is absent', () => {
97
+ const r = resolveActivityCard(CARD, { elements: [FACTOR, GOAL, CHANGE], relations });
98
+ expect(r.valid).toBe(false);
99
+ expect(r.errors.some((e) => e.code === 'PC-001')).toBe(true);
100
+ });
101
+
102
+ it('PC-002 when the activity carries an explicit non-project type', () => {
103
+ const r = resolveActivityCard(CARD, {
104
+ elements: [{ ...PROJECT, activity_type: 'Task' }, CHILD, FACTOR, GOAL, CHANGE],
105
+ relations,
106
+ });
107
+ expect(r.errors.some((e) => e.code === 'PC-002')).toBe(true);
108
+ });
109
+
110
+ it('PC-003 when a milestone change is not in the project changes', () => {
111
+ const card: ActivityCardDoc = {
112
+ ...CARD,
113
+ activity_card: {
114
+ ...CARD.activity_card,
115
+ milestones: [{ id: 'MILESTONE-X-1', name: 'x', date: '2027-01-01', delivers_changes: ['CHANGE-NOT-THERE-1'] }],
116
+ },
117
+ };
118
+ const r = resolveActivityCard(card, sources);
119
+ expect(r.errors.some((e) => e.code === 'PC-003')).toBe(true);
120
+ });
121
+
122
+ it('PC-004 warns when a milestone falls outside the lifecycle window', () => {
123
+ const r = resolveActivityCard(CARD, {
124
+ elements: [{ ...PROJECT, valid_to: '2026-12-31' }, CHILD, FACTOR, GOAL, CHANGE],
125
+ relations,
126
+ });
127
+ expect(r.valid).toBe(true);
128
+ expect(r.warnings.some((w) => w.code === 'PC-004')).toBe(true);
129
+ });
130
+
131
+ it('LIFECYCLE-001 when valid_from is missing', () => {
132
+ const r = resolveActivityCard(CARD, {
133
+ elements: [{ ...PROJECT, valid_from: undefined }, CHILD, FACTOR, GOAL, CHANGE],
134
+ relations,
135
+ });
136
+ expect(r.errors.some((e) => e.code === 'LIFECYCLE-001')).toBe(true);
137
+ });
138
+
139
+ it('LIFECYCLE-003 when valid_to precedes valid_from', () => {
140
+ const r = resolveActivityCard(CARD, {
141
+ elements: [{ ...PROJECT, valid_to: '2025-01-01' }, CHILD, FACTOR, GOAL, CHANGE],
142
+ relations,
143
+ });
144
+ expect(r.errors.some((e) => e.code === 'LIFECYCLE-003')).toBe(true);
145
+ });
146
+
147
+ it('warns and omits when a change element is not found in canon', () => {
148
+ const r = resolveActivityCard(CARD, { elements: [PROJECT, CHILD, FACTOR, GOAL], relations });
149
+ expect(r.valid).toBe(true);
150
+ expect(r.resolved!.motivation.changes).toHaveLength(0);
151
+ expect(r.warnings.some((w) => w.code === 'PC-001')).toBe(true);
152
+ });
153
+
154
+ it('does not crash on malformed element/relation docs', () => {
155
+ const r = resolveActivityCard(CARD, {
156
+ elements: [null, 'x', PROJECT, 42, { notation: 'goal', id: null }],
157
+ relations: [null, 'x', { notation: 'relation' }],
158
+ });
159
+ expect(r.valid).toBe(true);
160
+ });
161
+ });