@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,677 @@
1
+ // Compliance-impact matrix derivation + markdown renderer
2
+ // (vkgeorgia/strategy#166 — interim renderer; CV-1 view-config wiring
3
+ // per strategy#84 decomposition refresh).
4
+ //
5
+ // Implements the render contract from
6
+ // methodology/notations/views/21-compliance-impact.md §5 at the coarsest grain
7
+ // (`product` columns × `obligation` rows). Stage / task grouping is planned
8
+ // for CV-3a and intentionally out of scope here.
9
+
10
+ import type { AssertionStatus } from '../assertion/types.js';
11
+ import type { ComplianceCanon } from './classify.js';
12
+ import { buildComplianceIndex } from './reverse-index.js';
13
+ import type { ComplianceIndex, IndexAssertion, IndexRequirement, ObjectDetailInput, ObjectDetailDef, DeadlineStatus } from './types.js';
14
+
15
+ /** Filter selecting REQUIREMENTs by codex source (jurisdiction / regime keys
16
+ * are accepted for forward compatibility but not yet honoured — the canon
17
+ * projection does not carry those fields). */
18
+ export interface ImpactObligationFilter {
19
+ derived_from_codex?: string[];
20
+ }
21
+
22
+ export interface ImpactSubjects {
23
+ products?: string[];
24
+ processes?: string[];
25
+ }
26
+
27
+ export interface ImpactStatusDisplay {
28
+ show?: AssertionStatus[];
29
+ }
30
+
31
+ export interface ImpactEmptyCellLabels {
32
+ /** Label used when no admitted ASSERTION binds the cell (modelling gap). */
33
+ no_obligation_label?: string;
34
+ /** Label used when an admitted ASSERTION exists with status `n_a`
35
+ * (modelled fact: the obligation does not apply). */
36
+ no_obligation_applies_label?: string;
37
+ }
38
+
39
+ // ── Column model (CV-3a) ────────────────────────────────────────────────────
40
+
41
+ /**
42
+ * A matrix column descriptor.
43
+ *
44
+ * At the coarsest grain (`grouping.columns: 'product'`) a column maps to a
45
+ * single subject: `{ subjectId, label: subjectId }`.
46
+ *
47
+ * At `grouping.columns: 'product-stage'` a column represents one (product,
48
+ * stage) pair: `{ subjectId: productId, stageId, label: productId + ':' + stageId }`.
49
+ * A cell is populated when an ASSERTION has `subject === subjectId` and
50
+ * `stageId ∈ assertion.realised_via` (or `realised_via` is absent, meaning
51
+ * the assertion covers the entire subject — it then contributes to every
52
+ * stage column for that subject).
53
+ */
54
+ export interface ImpactColumn {
55
+ /** Subject (product / process / capability) the column belongs to. */
56
+ subjectId: string;
57
+ /** Stage ID — present only when grouping.columns is not 'product'. */
58
+ stageId?: string;
59
+ /** Human-readable column header. */
60
+ label: string;
61
+ }
62
+
63
+ /**
64
+ * Grouping options for matrix columns.
65
+ *
66
+ * `columns` controls the column grain:
67
+ * - `'product'` (default) — one column per subject.
68
+ * - `'product-stage'` — one column per (subject, stage) pair derived
69
+ * from `objectDetails` passed to `buildImpactMatrix`.
70
+ */
71
+ export interface ImpactGrouping {
72
+ columns: 'object' | 'object-details';
73
+ }
74
+
75
+ /**
76
+ * Named, versioned view-config — the report *definition*.
77
+ *
78
+ * A saved view config is the source of truth for a deterministic report run:
79
+ * given the same config + canon, `buildImpactMatrix` always produces the
80
+ * same matrix. Configs are stored in YAML files (top-level `view:` key) and
81
+ * referenced by `id`.
82
+ *
83
+ * `snapshot_at` (ISO 8601 date) records when the report was last generated.
84
+ * CV-3 blueprint-lane rendering uses it to mark obligations that appeared in
85
+ * the canon *after* the snapshot as "new" (dashed-border cell decoration).
86
+ */
87
+ export interface ImpactViewConfig {
88
+ id: string;
89
+ name: string;
90
+ description?: string;
91
+ /**
92
+ * ISO 8601 date of the last report snapshot (YYYY-MM-DD).
93
+ * Populated automatically by the CLI on each run and stored back to the
94
+ * view-config file, giving CV-3 its "new since last run" signal.
95
+ */
96
+ snapshot_at?: string;
97
+ subjects: ImpactSubjects;
98
+ obligations: {
99
+ include?: string[];
100
+ filter?: ImpactObligationFilter;
101
+ };
102
+ status_display?: ImpactStatusDisplay;
103
+ empty_cells?: ImpactEmptyCellLabels;
104
+ order_rows_by?: 'id' | 'name';
105
+ /**
106
+ * Column grouping options. Omit (or set `columns: 'product'`) for the
107
+ * default single-column-per-subject behaviour. Set `columns: 'product-stage'`
108
+ * and pass `objectDetails` into `buildImpactMatrix` to expand each subject
109
+ * into per-stage sub-columns.
110
+ */
111
+ grouping?: ImpactGrouping;
112
+ }
113
+
114
+ // ── Pinned defaults ─────────────────────────────────────────────────────────
115
+
116
+ /**
117
+ * Explicit defaults for every optional field in ImpactViewConfig.
118
+ *
119
+ * When a view config omits a field, COMPLIANCE_IMPACT_DEFAULTS defines the
120
+ * assumed behaviour. The CLI prints these so re-runs without a full config
121
+ * are auditable ("what I assumed").
122
+ */
123
+ export const COMPLIANCE_IMPACT_DEFAULTS = {
124
+ /** Accept all four active statuses + n_a in cell aggregation. */
125
+ status_display: {
126
+ show: ['compliant', 'partial', 'non_compliant', 'under_review', 'n_a'] as AssertionStatus[],
127
+ },
128
+ /** Order rows by canonical REQUIREMENT ID (lexicographic). */
129
+ order_rows_by: 'id' as const,
130
+ /** §5.3 empty-cell labels. */
131
+ empty_cells: {
132
+ no_obligation_label: 'No mapped obligation (current model)',
133
+ no_obligation_applies_label: 'No obligation applies',
134
+ },
135
+ /** Obligation scope: all known REQUIREMENTs in the canon (no filter). */
136
+ obligations: { include: undefined as string[] | undefined, filter: undefined },
137
+ /** Subjects: empty — must be supplied explicitly in the view config. */
138
+ subjects: { products: [] as string[], processes: [] as string[] },
139
+ /** Column grain: one column per business object (no stage decomposition). */
140
+ grouping: { columns: 'object' as const },
141
+ } as const;
142
+
143
+ // ── View-config parser ──────────────────────────────────────────────────────
144
+
145
+ export type ParseImpactViewConfigResult =
146
+ | { ok: true; config: ImpactViewConfig }
147
+ | { ok: false; errors: string[] };
148
+
149
+ /**
150
+ * Validate and normalise a raw (YAML-parsed) value into an ImpactViewConfig.
151
+ *
152
+ * Accepts both the bare config object and the view-config file shape
153
+ * (`{ view: { id, name, … } }`) — the top-level `view:` wrapper is unwrapped
154
+ * automatically.
155
+ *
156
+ * Every optional field is filled from COMPLIANCE_IMPACT_DEFAULTS, so callers
157
+ * can rely on the returned config being complete without any `??` chains.
158
+ *
159
+ * Returns `{ ok: false, errors }` on schema violations rather than throwing,
160
+ * so the CLI can surface a clean error message without a stack trace.
161
+ */
162
+ export function parseImpactViewConfig(raw: unknown): ParseImpactViewConfigResult {
163
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
164
+ return { ok: false, errors: ['view config: expected an object at the document root'] };
165
+ }
166
+ const top = raw as Record<string, unknown>;
167
+
168
+ // Unwrap optional `view:` wrapper (view-config YAML files use this key).
169
+ const v: Record<string, unknown> =
170
+ 'view' in top && top.view && typeof top.view === 'object' && !Array.isArray(top.view)
171
+ ? (top.view as Record<string, unknown>)
172
+ : top;
173
+
174
+ const errors: string[] = [];
175
+ if (!v.id || typeof v.id !== 'string') errors.push('view.id: required string');
176
+ if (!v.name || typeof v.name !== 'string') errors.push('view.name: required string');
177
+ if (v.subjects !== undefined && (typeof v.subjects !== 'object' || Array.isArray(v.subjects))) {
178
+ errors.push('view.subjects: expected an object');
179
+ }
180
+ if (errors.length) return { ok: false, errors };
181
+
182
+ const subjects =
183
+ v.subjects && typeof v.subjects === 'object' && !Array.isArray(v.subjects)
184
+ ? (v.subjects as Record<string, unknown>)
185
+ : {};
186
+ const obligations =
187
+ v.obligations && typeof v.obligations === 'object' && !Array.isArray(v.obligations)
188
+ ? (v.obligations as Record<string, unknown>)
189
+ : {};
190
+ const obFilter =
191
+ obligations.filter && typeof obligations.filter === 'object' && !Array.isArray(obligations.filter)
192
+ ? (obligations.filter as Record<string, unknown>)
193
+ : null;
194
+ const statusDisplay =
195
+ v.status_display && typeof v.status_display === 'object' && !Array.isArray(v.status_display)
196
+ ? (v.status_display as Record<string, unknown>)
197
+ : {};
198
+ const emptyCells =
199
+ v.empty_cells && typeof v.empty_cells === 'object' && !Array.isArray(v.empty_cells)
200
+ ? (v.empty_cells as Record<string, unknown>)
201
+ : {};
202
+
203
+ const config: ImpactViewConfig = {
204
+ id: v.id as string,
205
+ name: v.name as string,
206
+ description: typeof v.description === 'string' ? v.description : undefined,
207
+ snapshot_at: typeof v.snapshot_at === 'string' ? v.snapshot_at : undefined,
208
+ subjects: {
209
+ products: Array.isArray(subjects.products)
210
+ ? (subjects.products as unknown[]).filter((x): x is string => typeof x === 'string')
211
+ : [...COMPLIANCE_IMPACT_DEFAULTS.subjects.products],
212
+ processes: Array.isArray(subjects.processes)
213
+ ? (subjects.processes as unknown[]).filter((x): x is string => typeof x === 'string')
214
+ : [...COMPLIANCE_IMPACT_DEFAULTS.subjects.processes],
215
+ },
216
+ obligations: {
217
+ include: Array.isArray(obligations.include)
218
+ ? (obligations.include as unknown[]).filter((x): x is string => typeof x === 'string')
219
+ : undefined,
220
+ filter: obFilter
221
+ ? {
222
+ derived_from_codex: Array.isArray(obFilter.derived_from_codex)
223
+ ? (obFilter.derived_from_codex as unknown[]).filter((x): x is string => typeof x === 'string')
224
+ : undefined,
225
+ }
226
+ : undefined,
227
+ },
228
+ status_display: {
229
+ show: Array.isArray(statusDisplay.show)
230
+ ? (statusDisplay.show as unknown[]).filter((x): x is AssertionStatus => typeof x === 'string')
231
+ : [...COMPLIANCE_IMPACT_DEFAULTS.status_display.show],
232
+ },
233
+ empty_cells: {
234
+ no_obligation_label:
235
+ typeof emptyCells.no_obligation_label === 'string'
236
+ ? emptyCells.no_obligation_label
237
+ : COMPLIANCE_IMPACT_DEFAULTS.empty_cells.no_obligation_label,
238
+ no_obligation_applies_label:
239
+ typeof emptyCells.no_obligation_applies_label === 'string'
240
+ ? emptyCells.no_obligation_applies_label
241
+ : COMPLIANCE_IMPACT_DEFAULTS.empty_cells.no_obligation_applies_label,
242
+ },
243
+ order_rows_by: v.order_rows_by === 'name' ? 'name' : COMPLIANCE_IMPACT_DEFAULTS.order_rows_by,
244
+ };
245
+
246
+ return { ok: true, config };
247
+ }
248
+
249
+ /** A single cell of the rendered matrix. */
250
+ export interface ImpactCell {
251
+ /** Resolved status when an ASSERTION binds the cell, else null. */
252
+ status: AssertionStatus | null;
253
+ /** Empty-cell condition per §5.3 — one of:
254
+ * - `bound` : at least one admitted ASSERTION binds the cell; `status` is set.
255
+ * - `n_a_only`: every binding ASSERTION has status `n_a` (modelled fact).
256
+ * - `gap` : no admitted ASSERTION binds the cell (modelling gap). */
257
+ kind: 'bound' | 'n_a_only' | 'gap';
258
+ /** The assertions that contributed to this cell, id-sorted. */
259
+ assertions: IndexAssertion[];
260
+ /**
261
+ * CV-3 blueprint-lane decorations.
262
+ *
263
+ * `isNew` — the row's REQUIREMENT was admitted after the report
264
+ * `snapshot_at` date (dashed-border decoration).
265
+ * `isUrgent` — the cell is a gap AND the requirement has a deadline
266
+ * whose temporal status is `past_due` or `in_force`.
267
+ * `deadlineStatus` — temporal distance to the deadline (or `'none'`).
268
+ */
269
+ decoration: {
270
+ isNew: boolean;
271
+ isUrgent: boolean;
272
+ deadlineStatus: DeadlineStatus;
273
+ };
274
+ }
275
+
276
+ export interface ImpactMatrix {
277
+ viewId: string;
278
+ viewName: string;
279
+ description?: string;
280
+ /**
281
+ * ISO 8601 date of the last report snapshot, copied from the view config.
282
+ * CV-3 blueprint-lane rendering uses this to flag obligations that appeared
283
+ * after the snapshot as "new" (dashed-border cell decoration).
284
+ */
285
+ snapshotAt?: string;
286
+ /** Row dimension — REQUIREMENT projections, in the configured order. */
287
+ rows: IndexRequirement[];
288
+ /**
289
+ * Column dimension.
290
+ *
291
+ * At `grouping.columns: 'product'` (default) each entry has only `subjectId`
292
+ * and `label`. At `'product-stage'` entries additionally carry `stageId`.
293
+ */
294
+ columns: ImpactColumn[];
295
+ /** Cells, indexed as `cell[rowIdx][colIdx]`. */
296
+ cells: ImpactCell[][];
297
+ /** Canonical empty-cell labels actually used (after defaults applied). */
298
+ emptyLabels: Required<ImpactEmptyCellLabels>;
299
+ /**
300
+ * CV-3 derived obligations lane.
301
+ *
302
+ * For each column, the set of codex source IDs (`REQUIREMENT.derived_from`)
303
+ * that appear in at least one non-gap cell in that column. Renderers use
304
+ * this to display a "laws lane" header above or below the matrix columns.
305
+ * Empty array when no requirement in the column has a `derived_from`.
306
+ */
307
+ obligationsLane: string[][];
308
+ }
309
+
310
+ const DEFAULT_NO_OBLIGATION_LABEL = 'No mapped obligation (current model)';
311
+ const DEFAULT_NO_OBLIGATION_APPLIES_LABEL = 'No obligation applies';
312
+
313
+ // ── Deadline temporal status (CV-3) ─────────────────────────────────────────
314
+
315
+ /** Number of days within which a deadline is considered "in force" (imminent). */
316
+ const IN_FORCE_HORIZON_DAYS = 30;
317
+
318
+ /**
319
+ * Classify a REQUIREMENT deadline relative to a reference date.
320
+ *
321
+ * @param deadline ISO 8601 date string (YYYY-MM-DD), or undefined.
322
+ * @param today Reference date string (YYYY-MM-DD); defaults to today.
323
+ */
324
+ export function computeDeadlineStatus(
325
+ deadline: string | undefined,
326
+ today: string = new Date().toISOString().slice(0, 10),
327
+ ): DeadlineStatus {
328
+ if (!deadline) return 'none';
329
+ if (deadline < today) return 'past_due';
330
+ const daysAway = Math.round(
331
+ (new Date(deadline).getTime() - new Date(today).getTime()) / (1000 * 60 * 60 * 24),
332
+ );
333
+ return daysAway <= IN_FORCE_HORIZON_DAYS ? 'in_force' : 'upcoming';
334
+ }
335
+
336
+ function byId<T extends { id: string }>(a: T, b: T): number {
337
+ return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
338
+ }
339
+
340
+ function resolveObligations(
341
+ config: ImpactViewConfig,
342
+ index: ComplianceIndex,
343
+ allRequirements: IndexRequirement[],
344
+ ): IndexRequirement[] {
345
+ // `include` wins over `filter` per §4 (and COMPIMP-007).
346
+ if (config.obligations.include?.length) {
347
+ const out: IndexRequirement[] = [];
348
+ for (const id of config.obligations.include) {
349
+ const r = index.requirementById.get(id);
350
+ if (r) out.push(r);
351
+ }
352
+ return out;
353
+ }
354
+ const codices = config.obligations.filter?.derived_from_codex ?? [];
355
+ if (codices.length === 0) return [...allRequirements].sort(byId);
356
+ const seen = new Set<string>();
357
+ const out: IndexRequirement[] = [];
358
+ for (const codex of codices) {
359
+ for (const r of index.requirementsByLaw.get(codex) ?? []) {
360
+ if (!seen.has(r.id)) {
361
+ seen.add(r.id);
362
+ out.push(r);
363
+ }
364
+ }
365
+ }
366
+ return out;
367
+ }
368
+
369
+ function orderRows(rows: IndexRequirement[], key: 'id' | 'name' | undefined): IndexRequirement[] {
370
+ const sorted = [...rows];
371
+ if (key === 'name') {
372
+ sorted.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
373
+ } else {
374
+ sorted.sort(byId);
375
+ }
376
+ return sorted;
377
+ }
378
+
379
+ /** Aggregate multiple matching assertions into one cell value, per §5.2 step 4.
380
+ * Assumes at least one assertion matches an allowed status; caller filters. */
381
+ function aggregateStatus(assertions: IndexAssertion[]): AssertionStatus {
382
+ let sawPartial = false;
383
+ let sawUnderReview = false;
384
+ let sawCompliant = false;
385
+ for (const a of assertions) {
386
+ if (a.status === 'non_compliant') return 'non_compliant';
387
+ if (a.status === 'partial') sawPartial = true;
388
+ else if (a.status === 'under_review') sawUnderReview = true;
389
+ else if (a.status === 'compliant') sawCompliant = true;
390
+ }
391
+ if (sawPartial) return 'partial';
392
+ if (sawUnderReview) return 'under_review';
393
+ if (sawCompliant) return 'compliant';
394
+ return 'n_a';
395
+ }
396
+
397
+ // ── Column builder helpers (CV-3a) ──────────────────────────────────────────
398
+
399
+ function buildProductColumns(config: ImpactViewConfig): ImpactColumn[] {
400
+ const subjects = [...(config.subjects.products ?? []), ...(config.subjects.processes ?? [])];
401
+ return subjects.map(id => ({ subjectId: id, label: id }));
402
+ }
403
+
404
+ function buildObjectDetailColumns(config: ImpactViewConfig, objectDetails: ObjectDetailInput[]): ImpactColumn[] {
405
+ const detailMap = new Map<string, ObjectDetailDef[]>(objectDetails.map(d => [d.objectId, d.details]));
406
+ const subjects = [...(config.subjects.products ?? []), ...(config.subjects.processes ?? [])];
407
+ const cols: ImpactColumn[] = [];
408
+ for (const subjectId of subjects) {
409
+ const details = detailMap.get(subjectId);
410
+ if (!details || details.length === 0) {
411
+ cols.push({ subjectId, label: subjectId });
412
+ } else {
413
+ for (const detail of details) {
414
+ cols.push({ subjectId, stageId: detail.id, label: `${subjectId}:${detail.id}` });
415
+ }
416
+ }
417
+ }
418
+ return cols;
419
+ }
420
+
421
+ /**
422
+ * Returns true when assertion `a` contributes to column `col`.
423
+ *
424
+ * At `'product'` grain: `a.subject` must match `col.subjectId`.
425
+ * At `'product-stage'` grain: `a.subject` must match AND either
426
+ * `a.realised_via` is absent (claim covers the whole subject, so every stage
427
+ * inherits it) OR `col.stageId` ∈ `a.realised_via`.
428
+ */
429
+ function assertionMatchesColumn(a: IndexAssertion, col: ImpactColumn): boolean {
430
+ if (a.subject !== col.subjectId) return false;
431
+ if (!col.stageId) return true;
432
+ if (!a.realised_via || a.realised_via.length === 0) return true;
433
+ return a.realised_via.includes(col.stageId);
434
+ }
435
+
436
+ /**
437
+ * Build the matrix per §5.2.
438
+ *
439
+ * At the default `grouping.columns: 'product'` grain, one column per subject.
440
+ * At `grouping.columns: 'product-stage'`, pass `objectDetails` to expand each
441
+ * subject into per-stage sub-columns (typically extracted from a
442
+ * process-blueprint document via `extractObjectDetails`). Subjects with no
443
+ * stage mapping fall back to a single product-grain column automatically.
444
+ *
445
+ * Subjects are taken from `config.subjects.products` and
446
+ * `config.subjects.processes` (in that order). Subjects with no binding
447
+ * obligation still appear as columns so the gap is visible.
448
+ */
449
+ export function buildImpactMatrix(
450
+ canon: ComplianceCanon,
451
+ config: ImpactViewConfig,
452
+ objectDetails?: ObjectDetailInput[],
453
+ ): ImpactMatrix {
454
+ const index = buildComplianceIndex({
455
+ requirements: canon.requirements,
456
+ assertions: canon.assertions,
457
+ });
458
+
459
+ const obligations = orderRows(resolveObligations(config, index, canon.requirements), config.order_rows_by);
460
+
461
+ const useStageGrain =
462
+ config.grouping?.columns === 'object-details' && objectDetails && objectDetails.length > 0;
463
+ const columns: ImpactColumn[] = useStageGrain
464
+ ? buildObjectDetailColumns(config, objectDetails!)
465
+ : buildProductColumns(config);
466
+
467
+ const allowedStatuses = new Set<AssertionStatus>(
468
+ config.status_display?.show ?? ['compliant', 'partial', 'non_compliant', 'under_review', 'n_a'],
469
+ );
470
+
471
+ const rowIndex = new Set(obligations.map(r => r.id));
472
+ const cells: ImpactCell[][] = obligations.map(() => columns.map(() => emptyCell()));
473
+
474
+ for (let c = 0; c < columns.length; c++) {
475
+ const col = columns[c];
476
+ const subjectAssertions = index.assertionsBySubject.get(col.subjectId) ?? [];
477
+ for (const a of subjectAssertions) {
478
+ if (!rowIndex.has(a.about)) continue;
479
+ if (!allowedStatuses.has(a.status)) continue;
480
+ if (!assertionMatchesColumn(a, col)) continue;
481
+ const rowIdx = obligations.findIndex(r => r.id === a.about);
482
+ if (rowIdx < 0) continue;
483
+ cells[rowIdx][c].assertions.push(a);
484
+ }
485
+ }
486
+
487
+ // Resolve each cell to a status / kind.
488
+ for (let r = 0; r < cells.length; r++) {
489
+ for (let c = 0; c < cells[r].length; c++) {
490
+ const cell = cells[r][c];
491
+ cell.assertions.sort(byId);
492
+ if (cell.assertions.length === 0) {
493
+ cell.kind = 'gap';
494
+ cell.status = null;
495
+ continue;
496
+ }
497
+ if (cell.assertions.every(a => a.status === 'n_a')) {
498
+ cell.kind = 'n_a_only';
499
+ cell.status = 'n_a';
500
+ continue;
501
+ }
502
+ cell.kind = 'bound';
503
+ cell.status = aggregateStatus(cell.assertions);
504
+ }
505
+ }
506
+
507
+ // ── CV-3 decorations ────────────────────────────────────────────────────
508
+ const today = new Date().toISOString().slice(0, 10);
509
+ for (let r = 0; r < cells.length; r++) {
510
+ const req = obligations[r];
511
+ const deadlineStatus = computeDeadlineStatus(req.deadline, today);
512
+ // "new" = requirement admitted after the snapshot date.
513
+ const isNew =
514
+ !!config.snapshot_at &&
515
+ !!req.admitted_at &&
516
+ req.admitted_at > config.snapshot_at;
517
+ for (let c = 0; c < cells[r].length; c++) {
518
+ const cell = cells[r][c];
519
+ const isUrgent =
520
+ cell.kind === 'gap' &&
521
+ (deadlineStatus === 'past_due' || deadlineStatus === 'in_force');
522
+ cell.decoration = { isNew, isUrgent, deadlineStatus };
523
+ }
524
+ }
525
+
526
+ // ── CV-3 derived obligations lane ───────────────────────────────────────
527
+ // For each column: collect codex source IDs from non-gap rows.
528
+ const obligationsLane: string[][] = columns.map((_, c) => {
529
+ const codexIds = new Set<string>();
530
+ for (let r = 0; r < cells.length; r++) {
531
+ if (cells[r][c].kind !== 'gap') {
532
+ for (const src of obligations[r].derived_from ?? []) {
533
+ codexIds.add(src);
534
+ }
535
+ }
536
+ }
537
+ return [...codexIds].sort();
538
+ });
539
+
540
+ return {
541
+ viewId: config.id,
542
+ viewName: config.name,
543
+ description: config.description,
544
+ snapshotAt: config.snapshot_at,
545
+ rows: obligations,
546
+ columns,
547
+ cells,
548
+ emptyLabels: {
549
+ no_obligation_label: config.empty_cells?.no_obligation_label ?? DEFAULT_NO_OBLIGATION_LABEL,
550
+ no_obligation_applies_label:
551
+ config.empty_cells?.no_obligation_applies_label ?? DEFAULT_NO_OBLIGATION_APPLIES_LABEL,
552
+ },
553
+ obligationsLane,
554
+ };
555
+ }
556
+
557
+ function emptyCell(): ImpactCell {
558
+ return {
559
+ status: null,
560
+ kind: 'gap',
561
+ assertions: [],
562
+ decoration: { isNew: false, isUrgent: false, deadlineStatus: 'none' },
563
+ };
564
+ }
565
+
566
+ // ── Process-blueprint stage extractor (CV-3a) ───────────────────────────────
567
+
568
+ /**
569
+ * Extract `ObjectDetailInput` from a raw (YAML-parsed) process-blueprint document.
570
+ *
571
+ * Accepts both the bare document object and the wrapped form
572
+ * (`{ process_blueprint: { id, stages: […] } }`).
573
+ * Returns `null` when the document is not a recognisable process-blueprint or
574
+ * has no `id` / `stages` array so callers can skip unrecognised files safely.
575
+ *
576
+ * Pass the returned value into `buildImpactMatrix` as part of the
577
+ * `objectDetails` array when `grouping.columns: 'product-stage'` is configured.
578
+ */
579
+ export function extractObjectDetails(doc: unknown): ObjectDetailInput | null {
580
+ if (!doc || typeof doc !== 'object' || Array.isArray(doc)) return null;
581
+ const top = doc as Record<string, unknown>;
582
+
583
+ const bp: Record<string, unknown> | null =
584
+ top.notation === 'process_blueprint'
585
+ ? top
586
+ : top.process_blueprint && typeof top.process_blueprint === 'object' && !Array.isArray(top.process_blueprint)
587
+ ? (top.process_blueprint as Record<string, unknown>)
588
+ : null;
589
+
590
+ if (!bp) return null;
591
+ const id = typeof bp.id === 'string' ? bp.id : null;
592
+ if (!id) return null;
593
+ if (!Array.isArray(bp.stages)) return null;
594
+
595
+ const details: ObjectDetailDef[] = [];
596
+ for (const s of bp.stages) {
597
+ if (!s || typeof s !== 'object' || Array.isArray(s)) continue;
598
+ const sid = typeof s.id === 'string' ? s.id : null;
599
+ if (!sid) continue;
600
+ details.push({ id: sid, name: typeof s.name === 'string' ? s.name : sid });
601
+ }
602
+
603
+ return { objectId: id, details };
604
+ }
605
+
606
+ // ── Markdown rendering ──────────────────────────────────────────────────────
607
+
608
+ const STATUS_GLYPH: Record<AssertionStatus, string> = {
609
+ compliant: 'OK',
610
+ partial: 'PARTIAL',
611
+ non_compliant: 'FAIL',
612
+ under_review: 'REVIEW',
613
+ n_a: 'N/A',
614
+ };
615
+
616
+ function escMd(s: string): string {
617
+ return s.replace(/\|/g, '\\|');
618
+ }
619
+
620
+ /**
621
+ * Render the matrix as a GitHub-flavoured markdown table.
622
+ *
623
+ * Cell display:
624
+ * - `bound` → the §5.2 deterministic status label.
625
+ * - `n_a_only` → the "No obligation applies" label (or `view.empty_cells`
626
+ * override).
627
+ * - `gap` → the "No mapped obligation (current model)" label (or
628
+ * override).
629
+ */
630
+ export function renderImpactMarkdown(matrix: ImpactMatrix): string {
631
+ const lines: string[] = [];
632
+ lines.push(`# ${matrix.viewName}`);
633
+ lines.push('');
634
+ lines.push(`View ID: \`${matrix.viewId}\``);
635
+ if (matrix.snapshotAt) lines.push(`Report snapshot: ${matrix.snapshotAt}`);
636
+ if (matrix.description) {
637
+ lines.push('');
638
+ lines.push(matrix.description);
639
+ }
640
+ lines.push('');
641
+
642
+ if (matrix.rows.length === 0) {
643
+ lines.push('_No obligations in scope — the configured filter / include selected zero REQUIREMENTs._');
644
+ return lines.join('\n') + '\n';
645
+ }
646
+ if (matrix.columns.length === 0) {
647
+ lines.push('_No subjects in scope — `view.subjects` is empty._');
648
+ return lines.join('\n') + '\n';
649
+ }
650
+
651
+ const header = ['Obligation', ...matrix.columns.map(col => escMd(col.label))];
652
+ lines.push('| ' + header.join(' | ') + ' |');
653
+ lines.push('|' + header.map(() => '---').join('|') + '|');
654
+
655
+ for (let r = 0; r < matrix.rows.length; r++) {
656
+ const row = matrix.rows[r];
657
+ const rowLabel = `${row.id}${row.name && row.name !== row.id ? ` — ${row.name}` : ''}`;
658
+ const cells = matrix.cells[r].map(cell => renderCell(cell, matrix.emptyLabels));
659
+ lines.push('| ' + [escMd(rowLabel), ...cells].join(' | ') + ' |');
660
+ }
661
+
662
+ // Legend — the §5.3 distinction must be visible in the rendered output.
663
+ lines.push('');
664
+ lines.push('## Legend');
665
+ lines.push('');
666
+ lines.push('- **OK / PARTIAL / FAIL / REVIEW / N/A** — aggregated `ASSERTION.status` per §5.2.');
667
+ lines.push(`- **${escMd(matrix.emptyLabels.no_obligation_label)}** — modelling gap; no admitted ASSERTION binds this (obligation, subject) pair.`);
668
+ lines.push(`- **${escMd(matrix.emptyLabels.no_obligation_applies_label)}** — modelled fact; an admitted ASSERTION with status \`n_a\` excludes this pair.`);
669
+
670
+ return lines.join('\n') + '\n';
671
+ }
672
+
673
+ function renderCell(cell: ImpactCell, labels: Required<ImpactEmptyCellLabels>): string {
674
+ if (cell.kind === 'gap') return escMd(labels.no_obligation_label);
675
+ if (cell.kind === 'n_a_only') return escMd(labels.no_obligation_applies_label);
676
+ return STATUS_GLYPH[cell.status as AssertionStatus];
677
+ }