@transitrix/diagrams 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +47 -47
  3. package/dist/__tests__/edge-path.test.js +8 -0
  4. package/dist/activity-card/__tests__/example.test.js +20 -1
  5. package/dist/activity-card/__tests__/layout.test.js +102 -11
  6. package/dist/activity-card/__tests__/resolver.test.js +139 -6
  7. package/dist/activity-card/index.d.ts +1 -1
  8. package/dist/activity-card/layout.d.ts +1 -1
  9. package/dist/activity-card/layout.js +220 -103
  10. package/dist/activity-card/resolver.js +107 -26
  11. package/dist/activity-card/types.d.ts +150 -27
  12. package/dist/activity-card/validate.js +3 -0
  13. package/dist/actor/__tests__/validate.test.js +63 -31
  14. package/dist/actor/index.d.ts +1 -1
  15. package/dist/actor/index.js +1 -1
  16. package/dist/actor/types.d.ts +7 -3
  17. package/dist/actor/types.js +17 -3
  18. package/dist/actor/validate.js +55 -26
  19. package/dist/assertion/types.d.ts +3 -1
  20. package/dist/assertion/types.js +1 -1
  21. package/dist/change/__tests__/validate.test.js +40 -34
  22. package/dist/change/index.d.ts +1 -0
  23. package/dist/change/types.d.ts +1 -1
  24. package/dist/change/types.js +2 -2
  25. package/dist/change/validate.d.ts +6 -1
  26. package/dist/change/validate.js +68 -41
  27. package/dist/compliance/__tests__/classify.test.js +34 -0
  28. package/dist/compliance/__tests__/coverage-metric.test.js +146 -46
  29. package/dist/compliance/__tests__/html.test.js +2 -2
  30. package/dist/compliance/__tests__/impact.test.js +311 -6
  31. package/dist/compliance/classify.d.ts +6 -0
  32. package/dist/compliance/classify.js +9 -1
  33. package/dist/compliance/coverage-metric.d.ts +31 -11
  34. package/dist/compliance/coverage-metric.js +149 -39
  35. package/dist/compliance/html.js +77 -74
  36. package/dist/compliance/impact.d.ts +77 -16
  37. package/dist/compliance/impact.js +193 -25
  38. package/dist/compliance/index.d.ts +2 -2
  39. package/dist/compliance/index.js +1 -1
  40. package/dist/compliance/markdown.js +1 -0
  41. package/dist/compliance/types.d.ts +25 -6
  42. package/dist/edge-path.d.ts +8 -4
  43. package/dist/edge-path.js +11 -6
  44. package/dist/factor/__tests__/validate.test.js +74 -45
  45. package/dist/factor/index.d.ts +3 -2
  46. package/dist/factor/index.js +1 -1
  47. package/dist/factor/types.d.ts +4 -7
  48. package/dist/factor/types.js +10 -5
  49. package/dist/factor/validate.d.ts +6 -1
  50. package/dist/factor/validate.js +68 -50
  51. package/dist/fgca/__tests__/resolver.test.js +185 -0
  52. package/dist/fgca/index.d.ts +2 -0
  53. package/dist/fgca/index.js +1 -0
  54. package/dist/fgca/resolver.d.ts +49 -0
  55. package/dist/fgca/resolver.js +124 -0
  56. package/dist/goals/index.d.ts +1 -1
  57. package/dist/index.d.ts +5 -0
  58. package/dist/index.js +5 -0
  59. package/dist/process-blueprint/__tests__/layout.test.js +37 -0
  60. package/dist/process-blueprint/layout.js +5 -1
  61. package/dist/process-blueprint/types.d.ts +22 -1
  62. package/dist/stakeholder/__tests__/validate.test.js +49 -47
  63. package/dist/stakeholder/index.d.ts +1 -0
  64. package/dist/stakeholder/types.d.ts +3 -4
  65. package/dist/stakeholder/types.js +2 -3
  66. package/dist/stakeholder/validate.d.ts +6 -1
  67. package/dist/stakeholder/validate.js +71 -50
  68. package/dist/target-state/__tests__/validate.test.js +43 -27
  69. package/dist/target-state/index.d.ts +2 -0
  70. package/dist/target-state/index.js +1 -0
  71. package/dist/target-state/types.d.ts +6 -1
  72. package/dist/target-state/types.js +8 -7
  73. package/dist/target-state/validate.d.ts +6 -1
  74. package/dist/target-state/validate.js +69 -45
  75. package/dist/theme/themes.js +69 -69
  76. package/dist/webview/__tests__/entry.test.js +11 -11
  77. package/dist/webview/render-activities.d.ts +3 -1
  78. package/dist/webview/render-activities.js +2 -2
  79. package/dist/webview/render-activity-card.js +104 -77
  80. package/dist/webview/render-fgca.d.ts +4 -0
  81. package/dist/webview/render-fgca.js +2 -5
  82. package/dist/webview/render-goals.d.ts +1 -0
  83. package/dist/webview/render-goals.js +15 -15
  84. package/dist/webview/render-process-blueprint.js +10 -10
  85. package/package.json +40 -40
  86. package/src/__tests__/edge-path.test.ts +58 -46
  87. package/src/__tests__/scope.test.ts +27 -27
  88. package/src/__tests__/yaml-normalize.test.ts +158 -158
  89. package/src/activities/__tests__/cpm.test.ts +164 -164
  90. package/src/activities/__tests__/gantt.test.ts +245 -245
  91. package/src/activities/__tests__/layout.test.ts +112 -112
  92. package/src/activities/__tests__/validate.test.ts +409 -409
  93. package/src/activities/cpm.ts +95 -95
  94. package/src/activities/gantt.ts +276 -276
  95. package/src/activities/index.ts +28 -28
  96. package/src/activities/layout.ts +112 -112
  97. package/src/activities/types.ts +148 -148
  98. package/src/activities/validate.ts +357 -357
  99. package/src/activity-card/__tests__/example.test.ts +85 -66
  100. package/src/activity-card/__tests__/layout.test.ts +169 -63
  101. package/src/activity-card/__tests__/resolver.test.ts +307 -161
  102. package/src/activity-card/__tests__/validate.test.ts +131 -131
  103. package/src/activity-card/index.ts +38 -32
  104. package/src/activity-card/layout.ts +324 -194
  105. package/src/activity-card/resolver.ts +415 -337
  106. package/src/activity-card/types.ts +376 -237
  107. package/src/activity-card/validate.ts +162 -158
  108. package/src/actor/__tests__/validate.test.ts +70 -31
  109. package/src/actor/index.ts +1 -1
  110. package/src/actor/types.ts +21 -7
  111. package/src/actor/validate.ts +59 -27
  112. package/src/applications/__tests__/validate.test.ts +256 -256
  113. package/src/applications/index.ts +11 -11
  114. package/src/applications/types.ts +43 -43
  115. package/src/applications/validate.ts +131 -131
  116. package/src/assertion/__tests__/example.test.ts +37 -37
  117. package/src/assertion/__tests__/validate.test.ts +142 -142
  118. package/src/assertion/index.ts +12 -12
  119. package/src/assertion/types.ts +60 -58
  120. package/src/assertion/validate.ts +145 -145
  121. package/src/blocks/__tests__/layout.test.ts +169 -169
  122. package/src/blocks/__tests__/validate.test.ts +260 -260
  123. package/src/blocks/index.ts +17 -17
  124. package/src/blocks/layout.ts +186 -186
  125. package/src/blocks/types.ts +62 -62
  126. package/src/blocks/validate.ts +206 -206
  127. package/src/capability-map/__tests__/validate.test.ts +238 -238
  128. package/src/capability-map/index.ts +8 -8
  129. package/src/capability-map/types.ts +29 -29
  130. package/src/capability-map/validate.ts +127 -127
  131. package/src/change/__tests__/validate.test.ts +43 -34
  132. package/src/change/index.ts +1 -0
  133. package/src/change/types.ts +5 -3
  134. package/src/change/validate.ts +75 -43
  135. package/src/compliance/__tests__/classify.test.ts +64 -27
  136. package/src/compliance/__tests__/confidence.test.ts +98 -98
  137. package/src/compliance/__tests__/coverage-metric.test.ts +300 -188
  138. package/src/compliance/__tests__/demo-scenario.test.ts +273 -273
  139. package/src/compliance/__tests__/example.test.ts +57 -57
  140. package/src/compliance/__tests__/gap-report.test.ts +128 -128
  141. package/src/compliance/__tests__/html.test.ts +141 -141
  142. package/src/compliance/__tests__/impact.test.ts +840 -481
  143. package/src/compliance/__tests__/markdown.test.ts +94 -94
  144. package/src/compliance/__tests__/views.test.ts +64 -64
  145. package/src/compliance/classify.ts +100 -84
  146. package/src/compliance/confidence.ts +69 -69
  147. package/src/compliance/coverage-metric.ts +404 -263
  148. package/src/compliance/gap-report.ts +94 -94
  149. package/src/compliance/html.ts +298 -295
  150. package/src/compliance/impact.ts +861 -677
  151. package/src/compliance/index.ts +57 -56
  152. package/src/compliance/markdown.ts +136 -135
  153. package/src/compliance/reverse-index.ts +33 -33
  154. package/src/compliance/types.ts +136 -120
  155. package/src/compliance/views.ts +46 -46
  156. package/src/compliance-matrix/__tests__/build.test.ts +200 -200
  157. package/src/compliance-matrix/__tests__/example.test.ts +57 -57
  158. package/src/compliance-matrix/index.ts +11 -11
  159. package/src/compliance-matrix/types.ts +102 -102
  160. package/src/edge-path.ts +50 -44
  161. package/src/factor/__tests__/validate.test.ts +80 -45
  162. package/src/factor/index.ts +3 -7
  163. package/src/factor/types.ts +14 -18
  164. package/src/factor/validate.ts +77 -51
  165. package/src/fgca/__tests__/chain-table.test.ts +172 -172
  166. package/src/fgca/__tests__/layout.test.ts +179 -179
  167. package/src/fgca/__tests__/parse-canonical.test.ts +248 -248
  168. package/src/fgca/__tests__/preview-layout.test.ts +124 -124
  169. package/src/fgca/__tests__/resolver.test.ts +209 -0
  170. package/src/fgca/__tests__/validate.test.ts +142 -142
  171. package/src/fgca/chain-table.ts +225 -225
  172. package/src/fgca/index.ts +47 -45
  173. package/src/fgca/layout.ts +204 -204
  174. package/src/fgca/nodeTypes.ts +11 -11
  175. package/src/fgca/nodes/FGCAActivityNode.tsx +65 -65
  176. package/src/fgca/nodes/FGCAChangeNode.tsx +64 -64
  177. package/src/fgca/nodes/FGCAFactorNode.tsx +64 -64
  178. package/src/fgca/nodes/FGCAGoalNode.tsx +72 -72
  179. package/src/fgca/parse-canonical.ts +400 -400
  180. package/src/fgca/preview-layout.ts +217 -217
  181. package/src/fgca/resolver.ts +157 -0
  182. package/src/fgca/types.ts +50 -50
  183. package/src/fgca/validate.ts +126 -126
  184. package/src/geometry.ts +9 -9
  185. package/src/goals/__tests__/layout.test.ts +175 -175
  186. package/src/goals/__tests__/mutations.test.ts +111 -111
  187. package/src/goals/__tests__/parse-canonical.test.ts +145 -145
  188. package/src/goals/__tests__/validate.test.ts +202 -202
  189. package/src/goals/index.ts +20 -21
  190. package/src/goals/layout.ts +172 -172
  191. package/src/goals/mutations.ts +123 -123
  192. package/src/goals/parse-canonical.ts +286 -286
  193. package/src/goals/types.ts +85 -85
  194. package/src/goals/validate.ts +136 -136
  195. package/src/index.ts +28 -23
  196. package/src/process-blueprint/__tests__/compliance-lane.test.ts +320 -320
  197. package/src/process-blueprint/__tests__/layout.test.ts +240 -195
  198. package/src/process-blueprint/__tests__/validate.test.ts +310 -310
  199. package/src/process-blueprint/index.ts +31 -31
  200. package/src/process-blueprint/layout.ts +535 -531
  201. package/src/process-blueprint/types.ts +263 -242
  202. package/src/process-blueprint/validate.ts +181 -181
  203. package/src/process-map/__tests__/validate.test.ts +223 -223
  204. package/src/process-map/index.ts +10 -10
  205. package/src/process-map/types.ts +36 -36
  206. package/src/process-map/validate.ts +135 -135
  207. package/src/products/__tests__/validate.test.ts +239 -239
  208. package/src/products/index.ts +9 -9
  209. package/src/products/types.ts +31 -31
  210. package/src/products/validate.ts +112 -112
  211. package/src/repo-validate/__tests__/validate-repo.test.ts +149 -149
  212. package/src/repo-validate/index.ts +2 -2
  213. package/src/repo-validate/types.ts +50 -50
  214. package/src/repo-validate/validate-repo.ts +196 -196
  215. package/src/requirement/__tests__/example.test.ts +24 -24
  216. package/src/requirement/__tests__/validate.test.ts +96 -96
  217. package/src/requirement/index.ts +4 -4
  218. package/src/requirement/types.ts +35 -35
  219. package/src/requirement/validate.ts +92 -92
  220. package/src/scenarios/__tests__/validate.test.ts +209 -209
  221. package/src/scenarios/index.ts +15 -15
  222. package/src/scenarios/types.ts +37 -37
  223. package/src/scenarios/validate.ts +122 -122
  224. package/src/schema-version.ts +16 -16
  225. package/src/scope.ts +46 -46
  226. package/src/stakeholder/__tests__/validate.test.ts +51 -47
  227. package/src/stakeholder/index.ts +1 -0
  228. package/src/stakeholder/types.ts +7 -7
  229. package/src/stakeholder/validate.ts +79 -52
  230. package/src/target-state/__tests__/validate.test.ts +48 -27
  231. package/src/target-state/index.ts +2 -0
  232. package/src/target-state/types.ts +11 -7
  233. package/src/target-state/validate.ts +76 -46
  234. package/src/theme/index.ts +4 -4
  235. package/src/theme/themes.ts +237 -237
  236. package/src/theme/tokens.ts +133 -133
  237. package/src/typed-id.ts +56 -56
  238. package/src/validation-types.ts +36 -36
  239. package/src/webview/__tests__/entry-notations.test.ts +77 -77
  240. package/src/webview/__tests__/entry.test.ts +64 -64
  241. package/src/webview/__tests__/render-goals.test.ts +72 -72
  242. package/src/webview/entry.ts +292 -292
  243. package/src/webview/render-activities.ts +5 -3
  244. package/src/webview/render-activity-card.ts +266 -234
  245. package/src/webview/render-fgca.ts +6 -5
  246. package/src/webview/render-goals.ts +111 -110
  247. package/src/webview/render-process-blueprint.ts +207 -207
  248. package/src/webview/styles.css +322 -323
  249. package/src/yaml-normalize.ts +56 -56
  250. package/dist/element-envelope.d.ts +0 -15
  251. package/dist/element-envelope.js +0 -55
  252. package/dist/issues/__tests__/layout.test.js +0 -62
  253. package/dist/issues/__tests__/validate.test.d.ts +0 -1
  254. package/dist/issues/__tests__/validate.test.js +0 -82
  255. package/dist/issues/index.d.ts +0 -4
  256. package/dist/issues/index.js +0 -2
  257. package/dist/issues/layout.d.ts +0 -10
  258. package/dist/issues/layout.js +0 -82
  259. package/dist/issues/types.d.ts +0 -61
  260. package/dist/issues/types.js +0 -6
  261. package/dist/issues/validate.d.ts +0 -3
  262. package/dist/issues/validate.js +0 -143
  263. package/dist/webview/transitrix-render.css +0 -323
  264. package/dist/webview/transitrix-render.js +0 -6452
  265. package/dist/webview/transitrix-render.js.map +0 -7
  266. package/src/element-envelope.ts +0 -76
  267. /package/dist/{issues/__tests__/layout.test.d.ts → fgca/__tests__/resolver.test.d.ts} +0 -0
@@ -1,102 +1,102 @@
1
- // Compliance matrix model (vkgeorgia/strategy#84 Phase 2).
2
- //
3
- // The matrix is Products × Requirements. Each cell is the status of the
4
- // (Product, Requirement) ASSERTION when one exists, or a gap when none does.
5
- // Pure data — the layout is computed by `build.ts`; the Studio preview renders
6
- // it. Non-product-subject assertions (CAPABILITY / PROCESS) belong to the
7
- // single-product / single-law views (Phase 3) and are ignored here.
8
-
9
- import type { AssertionStatus } from '../assertion/types.js';
10
-
11
- /** A row of the matrix — a discovered PRODUCT element. */
12
- export interface MatrixProduct {
13
- id: string;
14
- name: string;
15
- /** True when the product was discovered only as an assertion subject (no
16
- * product element file resolves it) — a dangling subject reference. */
17
- unresolved?: boolean;
18
- }
19
-
20
- /** A column of the matrix — a discovered REQUIREMENT. */
21
- export interface MatrixRequirement {
22
- id: string;
23
- name: string;
24
- /** Organisation-defined priority (high | medium | low), used by the filter. */
25
- severity?: string;
26
- /** Typed IDs of the codex sources this requirement derives from (verbatim
27
- * from `derived_from`). Carried so the renderer can surface provenance and
28
- * resolve jurisdictions without re-scanning. */
29
- derived_from?: string[];
30
- /** Jurisdictions resolved by walking `derived_from` → codex artefacts and
31
- * collecting their `jurisdiction` (Codex §1.1). Unique, sorted. Empty when
32
- * no codex source resolves with a jurisdiction (internal-only requirements,
33
- * or codex artefacts the scan did not find / has no jurisdiction). The
34
- * toolbar jurisdiction filter (F16) keys on this set. */
35
- jurisdictions?: string[];
36
- }
37
-
38
- /** A codex source document the matrix uses to resolve `Requirement.jurisdictions`. */
39
- export interface MatrixCodexDoc {
40
- id: string;
41
- jurisdiction?: string;
42
- }
43
-
44
- /** The assertion fields the matrix needs (a projection of the full Assertion). */
45
- export interface MatrixAssertionRef {
46
- id: string;
47
- about: string;
48
- subject: string;
49
- status: AssertionStatus;
50
- assessed_at?: string;
51
- next_review_at?: string;
52
- }
53
-
54
- /** One grid cell. `status === undefined` is a gap (no assertion for the pair). */
55
- export interface MatrixCell {
56
- productId: string;
57
- requirementId: string;
58
- /** The assertion filling this cell, when one exists. */
59
- assertionId?: string;
60
- status?: AssertionStatus;
61
- assessed_at?: string;
62
- next_review_at?: string;
63
- }
64
-
65
- export interface ComplianceMatrix {
66
- /** Rows, sorted by id. */
67
- products: MatrixProduct[];
68
- /** Columns, sorted by id. */
69
- requirements: MatrixRequirement[];
70
- /** `cells[rowIndex][colIndex]`, aligned to `products` × `requirements`. */
71
- cells: MatrixCell[][];
72
- /** Counts for the summary strip. */
73
- summary: {
74
- products: number;
75
- requirements: number;
76
- assertions: number;
77
- /** Cells with no assertion (compliance gaps). */
78
- gaps: number;
79
- };
80
- }
81
-
82
- export interface ComplianceMatrixInput {
83
- products: MatrixProduct[];
84
- requirements: MatrixRequirement[];
85
- assertions: MatrixAssertionRef[];
86
- /** Codex source documents — used to resolve each Requirement's jurisdictions
87
- * by walking `derived_from`. Optional: when omitted the builder still works
88
- * and every requirement's `jurisdictions` is left empty. */
89
- codex?: MatrixCodexDoc[];
90
- }
91
-
92
- /** Toolbar filter — empty/omitted fields mean "no filter on that dimension". */
93
- export interface MatrixFilter {
94
- /** Keep only requirement columns whose severity is in this set. */
95
- severities?: string[];
96
- /** Keep only the cell status badges in this set; others render as filtered. */
97
- statuses?: AssertionStatus[];
98
- /** Keep only requirement columns whose resolved jurisdictions intersect this
99
- * set. Requirements with no resolved jurisdiction are filtered out when this
100
- * is non-empty (F16, epic #84). */
101
- jurisdictions?: string[];
102
- }
1
+ // Compliance matrix model (vkgeorgia/strategy#84 Phase 2).
2
+ //
3
+ // The matrix is Products × Requirements. Each cell is the status of the
4
+ // (Product, Requirement) ASSERTION when one exists, or a gap when none does.
5
+ // Pure data — the layout is computed by `build.ts`; the Studio preview renders
6
+ // it. Non-product-subject assertions (CAPABILITY / PROCESS) belong to the
7
+ // single-product / single-law views (Phase 3) and are ignored here.
8
+
9
+ import type { AssertionStatus } from '../assertion/types.js';
10
+
11
+ /** A row of the matrix — a discovered PRODUCT element. */
12
+ export interface MatrixProduct {
13
+ id: string;
14
+ name: string;
15
+ /** True when the product was discovered only as an assertion subject (no
16
+ * product element file resolves it) — a dangling subject reference. */
17
+ unresolved?: boolean;
18
+ }
19
+
20
+ /** A column of the matrix — a discovered REQUIREMENT. */
21
+ export interface MatrixRequirement {
22
+ id: string;
23
+ name: string;
24
+ /** Organisation-defined priority (high | medium | low), used by the filter. */
25
+ severity?: string;
26
+ /** Typed IDs of the codex sources this requirement derives from (verbatim
27
+ * from `derived_from`). Carried so the renderer can surface provenance and
28
+ * resolve jurisdictions without re-scanning. */
29
+ derived_from?: string[];
30
+ /** Jurisdictions resolved by walking `derived_from` → codex artefacts and
31
+ * collecting their `jurisdiction` (Codex §1.1). Unique, sorted. Empty when
32
+ * no codex source resolves with a jurisdiction (internal-only requirements,
33
+ * or codex artefacts the scan did not find / has no jurisdiction). The
34
+ * toolbar jurisdiction filter (F16) keys on this set. */
35
+ jurisdictions?: string[];
36
+ }
37
+
38
+ /** A codex source document the matrix uses to resolve `Requirement.jurisdictions`. */
39
+ export interface MatrixCodexDoc {
40
+ id: string;
41
+ jurisdiction?: string;
42
+ }
43
+
44
+ /** The assertion fields the matrix needs (a projection of the full Assertion). */
45
+ export interface MatrixAssertionRef {
46
+ id: string;
47
+ about: string;
48
+ subject: string;
49
+ status: AssertionStatus;
50
+ assessed_at?: string;
51
+ next_review_at?: string;
52
+ }
53
+
54
+ /** One grid cell. `status === undefined` is a gap (no assertion for the pair). */
55
+ export interface MatrixCell {
56
+ productId: string;
57
+ requirementId: string;
58
+ /** The assertion filling this cell, when one exists. */
59
+ assertionId?: string;
60
+ status?: AssertionStatus;
61
+ assessed_at?: string;
62
+ next_review_at?: string;
63
+ }
64
+
65
+ export interface ComplianceMatrix {
66
+ /** Rows, sorted by id. */
67
+ products: MatrixProduct[];
68
+ /** Columns, sorted by id. */
69
+ requirements: MatrixRequirement[];
70
+ /** `cells[rowIndex][colIndex]`, aligned to `products` × `requirements`. */
71
+ cells: MatrixCell[][];
72
+ /** Counts for the summary strip. */
73
+ summary: {
74
+ products: number;
75
+ requirements: number;
76
+ assertions: number;
77
+ /** Cells with no assertion (compliance gaps). */
78
+ gaps: number;
79
+ };
80
+ }
81
+
82
+ export interface ComplianceMatrixInput {
83
+ products: MatrixProduct[];
84
+ requirements: MatrixRequirement[];
85
+ assertions: MatrixAssertionRef[];
86
+ /** Codex source documents — used to resolve each Requirement's jurisdictions
87
+ * by walking `derived_from`. Optional: when omitted the builder still works
88
+ * and every requirement's `jurisdictions` is left empty. */
89
+ codex?: MatrixCodexDoc[];
90
+ }
91
+
92
+ /** Toolbar filter — empty/omitted fields mean "no filter on that dimension". */
93
+ export interface MatrixFilter {
94
+ /** Keep only requirement columns whose severity is in this set. */
95
+ severities?: string[];
96
+ /** Keep only the cell status badges in this set; others render as filtered. */
97
+ statuses?: AssertionStatus[];
98
+ /** Keep only requirement columns whose resolved jurisdictions intersect this
99
+ * set. Requirements with no resolved jurisdiction are filtered out when this
100
+ * is non-empty (F16, epic #84). */
101
+ jurisdictions?: string[];
102
+ }
package/src/edge-path.ts CHANGED
@@ -1,44 +1,50 @@
1
- // Shared edge geometry for the static notation previews (Goals, FGCA, FGA,
2
- // Activities). All four render dependency/relationship edges as a single cubic
3
- // Bézier with horizontal control handles: each control point shares its
4
- // endpoint's Y, so the tangent is horizontal at both ends and the marker-end
5
- // arrow reads as perpendicular to the node's vertical edge.
6
- //
7
- // Pulling the path math here (rather than duplicating it inline in each
8
- // preview) makes the configurable-curvature behaviour (vkgeorgia/strategy#76)
9
- // unit-testable — the extension has no test harness.
10
-
11
- /**
12
- * Base handle length floor + span factors. The handle grows with both the
13
- * horizontal and vertical span so the curve stays visibly horizontal long
14
- * enough for the arrowhead to sit flush against the line.
15
- */
16
- export const EDGE_MIN_HANDLE = 64;
17
- const DX_FACTOR = 0.5;
18
- const DY_FACTOR = 0.8;
19
-
20
- /** Multiplier applied to the base handle length. 1 = historical appearance. */
21
- export const DEFAULT_EDGE_CURVATURE = 1;
22
-
23
- /**
24
- * Builds the SVG `d` for a horizontal-tangent cubic Bézier from (sx,sy) to
25
- * (tx,ty).
26
- *
27
- * `curvature` scales the control-handle length:
28
- * - 0 → control points collapse onto the endpoints ⇒ a straight line.
29
- * - 1 → the historical curve (no visual change from before #76).
30
- * - >1 → progressively stronger arc.
31
- */
32
- export function horizontalCubicEdgePath(
33
- sx: number,
34
- sy: number,
35
- tx: number,
36
- ty: number,
37
- curvature: number = DEFAULT_EDGE_CURVATURE,
38
- ): string {
39
- const dx = tx - sx;
40
- const dy = ty - sy;
41
- const baseHandle = Math.max(EDGE_MIN_HANDLE, Math.abs(dx) * DX_FACTOR, Math.abs(dy) * DY_FACTOR);
42
- const handle = baseHandle * curvature;
43
- return `M${sx},${sy} C${sx + handle},${sy} ${tx - handle},${ty} ${tx},${ty}`;
44
- }
1
+ // Shared edge geometry for the static notation previews (Goals, FGCA, FGA,
2
+ // Activities). All four render dependency/relationship edges as a single cubic
3
+ // Bézier with horizontal control handles: each control point shares its
4
+ // endpoint's Y, so the tangent is horizontal at both ends and the marker-end
5
+ // arrow reads as perpendicular to the node's vertical edge.
6
+ //
7
+ // Pulling the path math here (rather than duplicating it inline in each
8
+ // preview) makes the configurable-curvature behaviour (vkgeorgia/strategy#76)
9
+ // unit-testable — the extension has no test harness.
10
+
11
+ /**
12
+ * Base handle length floor + span factors. The handle grows with both the
13
+ * horizontal and vertical span so the curve stays visibly horizontal long
14
+ * enough for the arrowhead to sit flush against the line.
15
+ */
16
+ export const EDGE_MIN_HANDLE = 64;
17
+ const DX_FACTOR = 0.5;
18
+ const DY_FACTOR = 0.8;
19
+
20
+ /** Multiplier applied to the base handle length. 1 = historical appearance. */
21
+ export const DEFAULT_EDGE_CURVATURE = 1;
22
+
23
+ /**
24
+ * Builds the SVG `d` for a horizontal-tangent cubic Bézier from (sx,sy) to
25
+ * (tx,ty).
26
+ *
27
+ * `curvature` scales the exit control-handle (departure from source).
28
+ * `entryCurvature` scales the entry control-handle (arrival at target);
29
+ * defaults to `curvature` when omitted so callers that pass one value get
30
+ * symmetric handles (the historical behaviour).
31
+ *
32
+ * - 0 → handle collapses onto its endpoint ⇒ straight at that end.
33
+ * - 1 → the historical curve (no visual change from before #76).
34
+ * - >1 → progressively stronger arc at that end.
35
+ */
36
+ export function horizontalCubicEdgePath(
37
+ sx: number,
38
+ sy: number,
39
+ tx: number,
40
+ ty: number,
41
+ curvature: number = DEFAULT_EDGE_CURVATURE,
42
+ entryCurvature?: number,
43
+ ): string {
44
+ const dx = tx - sx;
45
+ const dy = ty - sy;
46
+ const baseHandle = Math.max(EDGE_MIN_HANDLE, Math.abs(dx) * DX_FACTOR, Math.abs(dy) * DY_FACTOR);
47
+ const exitHandle = baseHandle * curvature;
48
+ const entryHandle = baseHandle * (entryCurvature ?? curvature);
49
+ return `M${sx},${sy} C${sx + exitHandle},${sy} ${tx - entryHandle},${ty} ${tx},${ty}`;
50
+ }
@@ -1,11 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { validateFactor } from '../validate.js';
3
+ import type { CanonCatalog } from '../../typed-id.js';
3
4
 
4
- function valid(): Record<string, unknown> {
5
+ function validExternal(): Record<string, unknown> {
5
6
  return {
6
7
  notation: 'factor',
7
8
  id: 'FACTOR-EU-REG-1',
8
- name: 'EU regulatory window for market entry',
9
+ name: 'EU regulatory window',
9
10
  type: 'external',
10
11
  category: 'legal',
11
12
  description: 'Standing external driver — the EU regulatory regime.',
@@ -19,80 +20,114 @@ function valid(): Record<string, unknown> {
19
20
  };
20
21
  }
21
22
 
22
- const codes = (input: unknown): string[] => validateFactor(input).errors.map(e => e.code);
23
- const warns = (input: unknown): string[] => validateFactor(input).warnings.map(w => w.code);
23
+ function validInternal(): Record<string, unknown> {
24
+ return {
25
+ notation: 'factor',
26
+ id: 'FACTOR-COMP-1',
27
+ name: 'Support response time',
28
+ type: 'internal',
29
+ description: 'Standing internal driver.',
30
+ zone: 'canon',
31
+ admitted_at: '2026-05-29',
32
+ admitted_by: 'v.korobeinikov',
33
+ gate_checks: { uniqueness: 'pass', consistency: 'pass', completeness: 'pass' },
34
+ valid_from: '2026-05-26',
35
+ valid_to: null,
36
+ };
37
+ }
38
+
39
+ const codes = (input: unknown, opts?: Parameters<typeof validateFactor>[1]): string[] =>
40
+ validateFactor(input, opts).errors.map(e => e.code);
24
41
 
25
42
  describe('validateFactor — positive', () => {
26
- it('accepts the worked-example FACTOR-EU-REG-1', () => {
27
- const r = validateFactor(valid());
43
+ it('accepts a well-formed external PESTLE factor', () => {
44
+ const r = validateFactor(validExternal());
28
45
  expect(r.valid, JSON.stringify(r.errors)).toBe(true);
29
- expect(r.errors).toHaveLength(0);
30
46
  });
31
47
 
32
- it('accepts a minimal factor (no type/category/references)', () => {
33
- const r = valid();
34
- delete r.type;
35
- delete r.category;
36
- delete r.references_constraint;
37
- expect(validateFactor(r).valid).toBe(true);
48
+ it('accepts a well-formed internal factor (no PESTLE category)', () => {
49
+ expect(validateFactor(validInternal()).valid).toBe(true);
38
50
  });
39
- });
40
51
 
41
- describe('validateFactor — FACTOR-001 (envelope / shape)', () => {
42
- it('flags a non-object', () => {
43
- expect(codes(null)).toEqual(['FACTOR-001']);
44
- expect(codes(['x'])).toEqual(['FACTOR-001']);
52
+ it('accepts a minimal factor with no optional fields', () => {
53
+ const f = validInternal();
54
+ delete f.type;
55
+ delete f.description;
56
+ expect(validateFactor(f).valid).toBe(true);
45
57
  });
58
+ });
46
59
 
47
- it('flags a wrong notation tag', () => {
48
- expect(codes({ ...valid(), notation: 'driver' })).toContain('FACTOR-001');
60
+ describe('validateFactor — FACTOR-001 (shape / id grammar)', () => {
61
+ it('flags a missing required envelope field', () => {
62
+ const f = validExternal();
63
+ delete f.admitted_by;
64
+ expect(codes(f)).toContain('FACTOR-001');
49
65
  });
50
66
 
51
- it('flags an id with the wrong TYPE prefix', () => {
52
- expect(codes({ ...valid(), id: 'GOAL-EU-1' })).toContain('FACTOR-001');
67
+ it('flags an id that violates the grammar', () => {
68
+ expect(codes({ ...validExternal(), id: 'FACTOR-EU-REG-001' })).toContain('FACTOR-001');
69
+ expect(codes({ ...validExternal(), id: 'FAC-1' })).toContain('FACTOR-001');
70
+ expect(codes({ ...validExternal(), id: 'factor-1' })).toContain('FACTOR-001');
53
71
  });
54
72
 
55
- it('flags an id with a leading-zero terminal', () => {
56
- expect(codes({ ...valid(), id: 'FACTOR-EU-REG-01' })).toContain('FACTOR-001');
73
+ it('flags a wrong notation tag and a non-canon zone', () => {
74
+ expect(codes({ ...validExternal(), notation: 'driver' })).toContain('FACTOR-001');
75
+ expect(codes({ ...validExternal(), zone: 'sandbox' })).toContain('FACTOR-001');
57
76
  });
58
77
 
59
78
  it('flags a missing valid_to key (distinct from a null value)', () => {
60
- const r = valid();
61
- delete r.valid_to;
62
- expect(codes(r)).toContain('FACTOR-001');
63
- expect(validateFactor({ ...valid(), valid_to: null }).valid).toBe(true);
79
+ const f = validExternal();
80
+ delete f.valid_to;
81
+ expect(codes(f)).toContain('FACTOR-001');
64
82
  });
65
83
 
66
- it('flags references_constraint entries that are not CONSTRAINT typed ids', () => {
67
- expect(codes({ ...valid(), references_constraint: ['GOAL-EU-1'] })).toContain('FACTOR-001');
68
- expect(codes({ ...valid(), references_constraint: 'CONSTRAINT-X-1' })).toContain('FACTOR-001');
84
+ it('rejects a non-object', () => {
85
+ expect(codes(null)).toEqual(['FACTOR-001']);
86
+ expect(codes(['a'])).toEqual(['FACTOR-001']);
69
87
  });
70
88
  });
71
89
 
72
90
  describe('validateFactor — FACTOR-002 (type enum)', () => {
73
- it('flags a type outside external/internal', () => {
74
- expect(codes({ ...valid(), type: 'driver' })).toContain('FACTOR-002');
91
+ it('flags a type value outside {external, internal}', () => {
92
+ expect(codes({ ...validExternal(), type: 'driver' })).toContain('FACTOR-002');
93
+ });
94
+ });
95
+
96
+ describe('validateFactor — FACTOR-003 (PESTLE category)', () => {
97
+ it('flags a category outside PESTLE', () => {
98
+ expect(codes({ ...validExternal(), category: 'cosmic' })).toContain('FACTOR-003');
75
99
  });
76
100
 
77
- it('accepts each permitted type', () => {
78
- for (const t of ['external', 'internal']) {
79
- expect(codes({ ...valid(), type: t, category: undefined })).not.toContain('FACTOR-002');
101
+ it('flags PESTLE category on an internal factor', () => {
102
+ const f = validInternal();
103
+ f.category = 'legal';
104
+ expect(codes(f)).toContain('FACTOR-003');
105
+ });
106
+
107
+ it('accepts each permitted PESTLE category on an external factor', () => {
108
+ for (const cat of ['political', 'economic', 'social', 'technological', 'legal', 'environmental']) {
109
+ const f = validExternal();
110
+ f.category = cat;
111
+ expect(codes(f)).not.toContain('FACTOR-003');
80
112
  }
81
113
  });
82
114
  });
83
115
 
84
- describe('validateFactor — FACTOR-003 (PESTLE category)', () => {
85
- it('flags a category outside the PESTLE classes', () => {
86
- expect(codes({ ...valid(), category: 'cultural' })).toContain('FACTOR-003');
116
+ describe('validateFactor — FACTOR-004 (references_constraint)', () => {
117
+ it('flags a malformed references_constraint entry', () => {
118
+ expect(codes({ ...validExternal(), references_constraint: ['not an id'] })).toContain('FACTOR-004');
87
119
  });
88
120
 
89
- it('warns when category is set on an internal factor', () => {
90
- expect(warns({ ...valid(), type: 'internal', category: 'economic' })).toContain('FACTOR-003');
121
+ it('flags a references_constraint entry of the wrong TYPE', () => {
122
+ expect(codes({ ...validExternal(), references_constraint: ['GOAL-X-1'] })).toContain('FACTOR-004');
91
123
  });
92
124
 
93
- it('accepts each permitted PESTLE class', () => {
94
- for (const c of ['political', 'economic', 'social', 'technological', 'legal', 'environmental']) {
95
- expect(codes({ ...valid(), category: c })).not.toContain('FACTOR-003');
96
- }
125
+ it('flags an unresolved id when a catalog is supplied', () => {
126
+ const catalog: CanonCatalog = { typeOf: () => undefined };
127
+ expect(codes(validExternal(), { catalog })).toContain('FACTOR-004');
128
+ });
129
+
130
+ it('does not flag without a catalog for a well-formed CONSTRAINT id', () => {
131
+ expect(codes(validExternal())).not.toContain('FACTOR-004');
97
132
  });
98
133
  });
@@ -1,8 +1,4 @@
1
- // The `Factor` type is re-exported under the alias `FactorPrimitive` at the
2
- // package surface to avoid a name collision with the legacy FGCA-inline
3
- // `Factor` shape exported from `./goals/index` (a v0.x view shape with
4
- // numeric ids — distinct from this element primitive). Both types remain
5
- // importable from their module subpaths.
6
- export type { Factor as FactorPrimitive, FactorType, FactorCategory } from './types.js';
7
- export { FACTOR_TYPES, FACTOR_CATEGORIES } from './types.js';
1
+ export type { Factor, FactorType, FactorPestleCategory } from './types.js';
2
+ export { FACTOR_PESTLE_CATEGORIES } from './types.js';
8
3
  export { validateFactor } from './validate.js';
4
+ export type { FactorValidateOptions } from './validate.js';
@@ -1,41 +1,37 @@
1
- // FACTOR — motivation-layer driver primitive.
2
- // Schema: methodology notations/ELEMENT_PRIMITIVES.md §7.1 + envelope §3.
1
+ // FACTOR — motivation-layer Driver (ArchiMate).
2
+ // Schema: methodology notations/ELEMENT_PRIMITIVES.md §7.1.
3
3
 
4
4
  import type { GateChecks } from '../requirement/types.js';
5
5
 
6
- /** Optional subtype — internal/external organisation force. */
7
6
  export type FactorType = 'external' | 'internal';
8
7
 
9
- export const FACTOR_TYPES = ['external', 'internal'] as const;
10
-
11
- /** PESTLE sub-classification — external factors only. Open-ended in practice
12
- * but v1 fixes the six standard classes; an unknown category is flagged. */
13
- export type FactorCategory =
14
- | 'political'
15
- | 'economic'
16
- | 'social'
17
- | 'technological'
18
- | 'legal'
19
- | 'environmental';
20
-
21
- export const FACTOR_CATEGORIES = [
22
- 'political', 'economic', 'social', 'technological', 'legal', 'environmental',
8
+ /** PESTLE sub-classification — external factors only (§7.1). */
9
+ export const FACTOR_PESTLE_CATEGORIES = [
10
+ 'political',
11
+ 'economic',
12
+ 'social',
13
+ 'technological',
14
+ 'legal',
15
+ 'environmental',
23
16
  ] as const;
17
+ export type FactorPestleCategory = typeof FACTOR_PESTLE_CATEGORIES[number];
24
18
 
25
19
  export interface Factor {
26
20
  notation: 'factor';
27
21
  id: string;
28
22
  name: string;
29
23
  type?: FactorType;
30
- category?: FactorCategory;
24
+ category?: FactorPestleCategory;
31
25
  description?: string;
32
26
  references_constraint?: string[];
33
27
 
28
+ // Admission record (CONTRACT.md §6).
34
29
  zone: 'canon';
35
30
  admitted_at: string;
36
31
  admitted_by: string;
37
32
  gate_checks: GateChecks;
38
33
 
34
+ // Primitive lifecycle (CONTRACT.md §7).
39
35
  valid_from: string;
40
36
  valid_to: string | null;
41
37
  }