@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.
- package/LICENSE +21 -21
- package/README.md +47 -47
- package/dist/__tests__/edge-path.test.js +8 -0
- package/dist/activity-card/__tests__/example.test.js +20 -1
- package/dist/activity-card/__tests__/layout.test.js +102 -11
- package/dist/activity-card/__tests__/resolver.test.js +139 -6
- package/dist/activity-card/index.d.ts +1 -1
- package/dist/activity-card/layout.d.ts +1 -1
- package/dist/activity-card/layout.js +220 -103
- package/dist/activity-card/resolver.js +107 -26
- package/dist/activity-card/types.d.ts +150 -27
- package/dist/activity-card/validate.js +3 -0
- package/dist/actor/__tests__/validate.test.js +63 -31
- package/dist/actor/index.d.ts +1 -1
- package/dist/actor/index.js +1 -1
- package/dist/actor/types.d.ts +7 -3
- package/dist/actor/types.js +17 -3
- package/dist/actor/validate.js +55 -26
- package/dist/assertion/types.d.ts +3 -1
- package/dist/assertion/types.js +1 -1
- package/dist/change/__tests__/validate.test.js +40 -34
- package/dist/change/index.d.ts +1 -0
- package/dist/change/types.d.ts +1 -1
- package/dist/change/types.js +2 -2
- package/dist/change/validate.d.ts +6 -1
- package/dist/change/validate.js +68 -41
- package/dist/compliance/__tests__/classify.test.js +34 -0
- package/dist/compliance/__tests__/coverage-metric.test.js +146 -46
- package/dist/compliance/__tests__/html.test.js +2 -2
- package/dist/compliance/__tests__/impact.test.js +311 -6
- package/dist/compliance/classify.d.ts +6 -0
- package/dist/compliance/classify.js +9 -1
- package/dist/compliance/coverage-metric.d.ts +31 -11
- package/dist/compliance/coverage-metric.js +149 -39
- package/dist/compliance/html.js +77 -74
- package/dist/compliance/impact.d.ts +77 -16
- package/dist/compliance/impact.js +193 -25
- package/dist/compliance/index.d.ts +2 -2
- package/dist/compliance/index.js +1 -1
- package/dist/compliance/markdown.js +1 -0
- package/dist/compliance/types.d.ts +25 -6
- package/dist/edge-path.d.ts +8 -4
- package/dist/edge-path.js +11 -6
- package/dist/factor/__tests__/validate.test.js +74 -45
- package/dist/factor/index.d.ts +3 -2
- package/dist/factor/index.js +1 -1
- package/dist/factor/types.d.ts +4 -7
- package/dist/factor/types.js +10 -5
- package/dist/factor/validate.d.ts +6 -1
- package/dist/factor/validate.js +68 -50
- package/dist/fgca/__tests__/resolver.test.js +185 -0
- package/dist/fgca/index.d.ts +2 -0
- package/dist/fgca/index.js +1 -0
- package/dist/fgca/resolver.d.ts +49 -0
- package/dist/fgca/resolver.js +124 -0
- package/dist/goals/index.d.ts +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/process-blueprint/__tests__/layout.test.js +37 -0
- package/dist/process-blueprint/layout.js +5 -1
- package/dist/process-blueprint/types.d.ts +22 -1
- package/dist/stakeholder/__tests__/validate.test.js +49 -47
- package/dist/stakeholder/index.d.ts +1 -0
- package/dist/stakeholder/types.d.ts +3 -4
- package/dist/stakeholder/types.js +2 -3
- package/dist/stakeholder/validate.d.ts +6 -1
- package/dist/stakeholder/validate.js +71 -50
- package/dist/target-state/__tests__/validate.test.js +43 -27
- package/dist/target-state/index.d.ts +2 -0
- package/dist/target-state/index.js +1 -0
- package/dist/target-state/types.d.ts +6 -1
- package/dist/target-state/types.js +8 -7
- package/dist/target-state/validate.d.ts +6 -1
- package/dist/target-state/validate.js +69 -45
- package/dist/theme/themes.js +69 -69
- package/dist/webview/__tests__/entry.test.js +11 -11
- package/dist/webview/render-activities.d.ts +3 -1
- package/dist/webview/render-activities.js +2 -2
- package/dist/webview/render-activity-card.js +104 -77
- package/dist/webview/render-fgca.d.ts +4 -0
- package/dist/webview/render-fgca.js +2 -5
- package/dist/webview/render-goals.d.ts +1 -0
- package/dist/webview/render-goals.js +15 -15
- package/dist/webview/render-process-blueprint.js +10 -10
- package/package.json +40 -40
- package/src/__tests__/edge-path.test.ts +58 -46
- package/src/__tests__/scope.test.ts +27 -27
- package/src/__tests__/yaml-normalize.test.ts +158 -158
- package/src/activities/__tests__/cpm.test.ts +164 -164
- package/src/activities/__tests__/gantt.test.ts +245 -245
- package/src/activities/__tests__/layout.test.ts +112 -112
- package/src/activities/__tests__/validate.test.ts +409 -409
- package/src/activities/cpm.ts +95 -95
- package/src/activities/gantt.ts +276 -276
- package/src/activities/index.ts +28 -28
- package/src/activities/layout.ts +112 -112
- package/src/activities/types.ts +148 -148
- package/src/activities/validate.ts +357 -357
- package/src/activity-card/__tests__/example.test.ts +85 -66
- package/src/activity-card/__tests__/layout.test.ts +169 -63
- package/src/activity-card/__tests__/resolver.test.ts +307 -161
- package/src/activity-card/__tests__/validate.test.ts +131 -131
- package/src/activity-card/index.ts +38 -32
- package/src/activity-card/layout.ts +324 -194
- package/src/activity-card/resolver.ts +415 -337
- package/src/activity-card/types.ts +376 -237
- package/src/activity-card/validate.ts +162 -158
- package/src/actor/__tests__/validate.test.ts +70 -31
- package/src/actor/index.ts +1 -1
- package/src/actor/types.ts +21 -7
- package/src/actor/validate.ts +59 -27
- package/src/applications/__tests__/validate.test.ts +256 -256
- package/src/applications/index.ts +11 -11
- package/src/applications/types.ts +43 -43
- package/src/applications/validate.ts +131 -131
- package/src/assertion/__tests__/example.test.ts +37 -37
- package/src/assertion/__tests__/validate.test.ts +142 -142
- package/src/assertion/index.ts +12 -12
- package/src/assertion/types.ts +60 -58
- package/src/assertion/validate.ts +145 -145
- package/src/blocks/__tests__/layout.test.ts +169 -169
- package/src/blocks/__tests__/validate.test.ts +260 -260
- package/src/blocks/index.ts +17 -17
- package/src/blocks/layout.ts +186 -186
- package/src/blocks/types.ts +62 -62
- package/src/blocks/validate.ts +206 -206
- package/src/capability-map/__tests__/validate.test.ts +238 -238
- package/src/capability-map/index.ts +8 -8
- package/src/capability-map/types.ts +29 -29
- package/src/capability-map/validate.ts +127 -127
- package/src/change/__tests__/validate.test.ts +43 -34
- package/src/change/index.ts +1 -0
- package/src/change/types.ts +5 -3
- package/src/change/validate.ts +75 -43
- package/src/compliance/__tests__/classify.test.ts +64 -27
- package/src/compliance/__tests__/confidence.test.ts +98 -98
- package/src/compliance/__tests__/coverage-metric.test.ts +300 -188
- package/src/compliance/__tests__/demo-scenario.test.ts +273 -273
- package/src/compliance/__tests__/example.test.ts +57 -57
- package/src/compliance/__tests__/gap-report.test.ts +128 -128
- package/src/compliance/__tests__/html.test.ts +141 -141
- package/src/compliance/__tests__/impact.test.ts +840 -481
- package/src/compliance/__tests__/markdown.test.ts +94 -94
- package/src/compliance/__tests__/views.test.ts +64 -64
- package/src/compliance/classify.ts +100 -84
- package/src/compliance/confidence.ts +69 -69
- package/src/compliance/coverage-metric.ts +404 -263
- package/src/compliance/gap-report.ts +94 -94
- package/src/compliance/html.ts +298 -295
- package/src/compliance/impact.ts +861 -677
- package/src/compliance/index.ts +57 -56
- package/src/compliance/markdown.ts +136 -135
- package/src/compliance/reverse-index.ts +33 -33
- package/src/compliance/types.ts +136 -120
- package/src/compliance/views.ts +46 -46
- package/src/compliance-matrix/__tests__/build.test.ts +200 -200
- package/src/compliance-matrix/__tests__/example.test.ts +57 -57
- package/src/compliance-matrix/index.ts +11 -11
- package/src/compliance-matrix/types.ts +102 -102
- package/src/edge-path.ts +50 -44
- package/src/factor/__tests__/validate.test.ts +80 -45
- package/src/factor/index.ts +3 -7
- package/src/factor/types.ts +14 -18
- package/src/factor/validate.ts +77 -51
- package/src/fgca/__tests__/chain-table.test.ts +172 -172
- package/src/fgca/__tests__/layout.test.ts +179 -179
- package/src/fgca/__tests__/parse-canonical.test.ts +248 -248
- package/src/fgca/__tests__/preview-layout.test.ts +124 -124
- package/src/fgca/__tests__/resolver.test.ts +209 -0
- package/src/fgca/__tests__/validate.test.ts +142 -142
- package/src/fgca/chain-table.ts +225 -225
- package/src/fgca/index.ts +47 -45
- package/src/fgca/layout.ts +204 -204
- package/src/fgca/nodeTypes.ts +11 -11
- package/src/fgca/nodes/FGCAActivityNode.tsx +65 -65
- package/src/fgca/nodes/FGCAChangeNode.tsx +64 -64
- package/src/fgca/nodes/FGCAFactorNode.tsx +64 -64
- package/src/fgca/nodes/FGCAGoalNode.tsx +72 -72
- package/src/fgca/parse-canonical.ts +400 -400
- package/src/fgca/preview-layout.ts +217 -217
- package/src/fgca/resolver.ts +157 -0
- package/src/fgca/types.ts +50 -50
- package/src/fgca/validate.ts +126 -126
- package/src/geometry.ts +9 -9
- package/src/goals/__tests__/layout.test.ts +175 -175
- package/src/goals/__tests__/mutations.test.ts +111 -111
- package/src/goals/__tests__/parse-canonical.test.ts +145 -145
- package/src/goals/__tests__/validate.test.ts +202 -202
- package/src/goals/index.ts +20 -21
- package/src/goals/layout.ts +172 -172
- package/src/goals/mutations.ts +123 -123
- package/src/goals/parse-canonical.ts +286 -286
- package/src/goals/types.ts +85 -85
- package/src/goals/validate.ts +136 -136
- package/src/index.ts +28 -23
- package/src/process-blueprint/__tests__/compliance-lane.test.ts +320 -320
- package/src/process-blueprint/__tests__/layout.test.ts +240 -195
- package/src/process-blueprint/__tests__/validate.test.ts +310 -310
- package/src/process-blueprint/index.ts +31 -31
- package/src/process-blueprint/layout.ts +535 -531
- package/src/process-blueprint/types.ts +263 -242
- package/src/process-blueprint/validate.ts +181 -181
- package/src/process-map/__tests__/validate.test.ts +223 -223
- package/src/process-map/index.ts +10 -10
- package/src/process-map/types.ts +36 -36
- package/src/process-map/validate.ts +135 -135
- package/src/products/__tests__/validate.test.ts +239 -239
- package/src/products/index.ts +9 -9
- package/src/products/types.ts +31 -31
- package/src/products/validate.ts +112 -112
- package/src/repo-validate/__tests__/validate-repo.test.ts +149 -149
- package/src/repo-validate/index.ts +2 -2
- package/src/repo-validate/types.ts +50 -50
- package/src/repo-validate/validate-repo.ts +196 -196
- package/src/requirement/__tests__/example.test.ts +24 -24
- package/src/requirement/__tests__/validate.test.ts +96 -96
- package/src/requirement/index.ts +4 -4
- package/src/requirement/types.ts +35 -35
- package/src/requirement/validate.ts +92 -92
- package/src/scenarios/__tests__/validate.test.ts +209 -209
- package/src/scenarios/index.ts +15 -15
- package/src/scenarios/types.ts +37 -37
- package/src/scenarios/validate.ts +122 -122
- package/src/schema-version.ts +16 -16
- package/src/scope.ts +46 -46
- package/src/stakeholder/__tests__/validate.test.ts +51 -47
- package/src/stakeholder/index.ts +1 -0
- package/src/stakeholder/types.ts +7 -7
- package/src/stakeholder/validate.ts +79 -52
- package/src/target-state/__tests__/validate.test.ts +48 -27
- package/src/target-state/index.ts +2 -0
- package/src/target-state/types.ts +11 -7
- package/src/target-state/validate.ts +76 -46
- package/src/theme/index.ts +4 -4
- package/src/theme/themes.ts +237 -237
- package/src/theme/tokens.ts +133 -133
- package/src/typed-id.ts +56 -56
- package/src/validation-types.ts +36 -36
- package/src/webview/__tests__/entry-notations.test.ts +77 -77
- package/src/webview/__tests__/entry.test.ts +64 -64
- package/src/webview/__tests__/render-goals.test.ts +72 -72
- package/src/webview/entry.ts +292 -292
- package/src/webview/render-activities.ts +5 -3
- package/src/webview/render-activity-card.ts +266 -234
- package/src/webview/render-fgca.ts +6 -5
- package/src/webview/render-goals.ts +111 -110
- package/src/webview/render-process-blueprint.ts +207 -207
- package/src/webview/styles.css +322 -323
- package/src/yaml-normalize.ts +56 -56
- package/dist/element-envelope.d.ts +0 -15
- package/dist/element-envelope.js +0 -55
- package/dist/issues/__tests__/layout.test.js +0 -62
- package/dist/issues/__tests__/validate.test.d.ts +0 -1
- package/dist/issues/__tests__/validate.test.js +0 -82
- package/dist/issues/index.d.ts +0 -4
- package/dist/issues/index.js +0 -2
- package/dist/issues/layout.d.ts +0 -10
- package/dist/issues/layout.js +0 -82
- package/dist/issues/types.d.ts +0 -61
- package/dist/issues/types.js +0 -6
- package/dist/issues/validate.d.ts +0 -3
- package/dist/issues/validate.js +0 -143
- package/dist/webview/transitrix-render.css +0 -323
- package/dist/webview/transitrix-render.js +0 -6452
- package/dist/webview/transitrix-render.js.map +0 -7
- package/src/element-envelope.ts +0 -76
- /package/dist/{issues/__tests__/layout.test.d.ts → fgca/__tests__/resolver.test.d.ts} +0 -0
|
@@ -1,337 +1,415 @@
|
|
|
1
|
-
// Activity Card — cross-document resolver (the novel piece of this notation).
|
|
2
|
-
//
|
|
3
|
-
// Unlike every other Studio preview, the Activity Card is assembled from
|
|
4
|
-
// MULTIPLE documents. The card YAML names a project Activity; the resolver
|
|
5
|
-
// pulls the rest of the card's content BY REFERENCE from the canonical
|
|
6
|
-
// ELEMENT and RELATION store — never from other view documents (view-purity:
|
|
7
|
-
// a view is a projection over elements + relations, methodology
|
|
8
|
-
// ELEMENT_PRIMITIVES.md §1):
|
|
9
|
-
//
|
|
10
|
-
// canon/elements/** → one element per file. The project ACTIVITY (name,
|
|
11
|
-
// lifecycle + schedule dates, delivers_changes[],
|
|
12
|
-
// parent) and its child ACTIVITY elements (parent =
|
|
13
|
-
// project id); the FACTOR / GOAL / CHANGE elements that
|
|
14
|
-
// the motivation chain expands (goal.factors,
|
|
15
|
-
// change.goals carried inline on the elements).
|
|
16
|
-
// canon/relations/** → `notation: relation` files. The project's goals come
|
|
17
|
-
// from the first-class `activity_goal` REL (from =
|
|
18
|
-
// project id), preferred over the activity element's
|
|
19
|
-
// transitional inline `goals[]` when present
|
|
20
|
-
// (07-activities.md §"Time-aware relations").
|
|
21
|
-
//
|
|
22
|
-
// The extension reads + parses the element and relation files (walking the
|
|
23
|
-
// org's `canon/elements` and `canon/relations` trees) and hands the parsed
|
|
24
|
-
// docs in. This resolver is filesystem-free and pure so it stays unit-testable.
|
|
25
|
-
//
|
|
26
|
-
// Cross-document validation lives here (the rules that need the sibling docs):
|
|
27
|
-
// PC-001 project does not resolve to an admitted Activity
|
|
28
|
-
// PC-002 resolved Activity has activity_type other than "Project"
|
|
29
|
-
// PC-003 milestone.delivers_changes[] entry not in the project's own
|
|
30
|
-
// delivers_changes[]
|
|
31
|
-
// PC-004 (warning) milestone.date outside [valid_from, valid_to]
|
|
32
|
-
// LIFECYCLE-001 project valid_from missing / not a parseable ISO date
|
|
33
|
-
// LIFECYCLE-002 project valid_to present, not null, not a parseable date
|
|
34
|
-
// LIFECYCLE-003 project valid_to earlier than valid_from
|
|
35
|
-
//
|
|
36
|
-
// LIFECYCLE-* are scoped to the RESOLVED PROJECT Activity only — the card is
|
|
37
|
-
// built around the project's initiation date, so a missing valid_from there is
|
|
38
|
-
// a hard error. Child activities and FGCA elements are not lifecycle-checked
|
|
39
|
-
// here (out of this notation's scope; today's Studio activities docs may
|
|
40
|
-
// legitimately omit lifecycle fields).
|
|
41
|
-
|
|
42
|
-
import type {
|
|
43
|
-
ActivityCardDoc,
|
|
44
|
-
ActivityCardSources,
|
|
45
|
-
ResolvedActivityCard,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
*
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
if (str(doc['
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
const
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
errors.push({
|
|
174
|
-
code: '
|
|
175
|
-
message: `
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
1
|
+
// Activity Card — cross-document resolver (the novel piece of this notation).
|
|
2
|
+
//
|
|
3
|
+
// Unlike every other Studio preview, the Activity Card is assembled from
|
|
4
|
+
// MULTIPLE documents. The card YAML names a project Activity; the resolver
|
|
5
|
+
// pulls the rest of the card's content BY REFERENCE from the canonical
|
|
6
|
+
// ELEMENT and RELATION store — never from other view documents (view-purity:
|
|
7
|
+
// a view is a projection over elements + relations, methodology
|
|
8
|
+
// ELEMENT_PRIMITIVES.md §1):
|
|
9
|
+
//
|
|
10
|
+
// canon/elements/** → one element per file. The project ACTIVITY (name,
|
|
11
|
+
// lifecycle + schedule dates, delivers_changes[],
|
|
12
|
+
// parent) and its child ACTIVITY elements (parent =
|
|
13
|
+
// project id); the FACTOR / GOAL / CHANGE elements that
|
|
14
|
+
// the motivation chain expands (goal.factors,
|
|
15
|
+
// change.goals carried inline on the elements).
|
|
16
|
+
// canon/relations/** → `notation: relation` files. The project's goals come
|
|
17
|
+
// from the first-class `activity_goal` REL (from =
|
|
18
|
+
// project id), preferred over the activity element's
|
|
19
|
+
// transitional inline `goals[]` when present
|
|
20
|
+
// (07-activities.md §"Time-aware relations").
|
|
21
|
+
//
|
|
22
|
+
// The extension reads + parses the element and relation files (walking the
|
|
23
|
+
// org's `canon/elements` and `canon/relations` trees) and hands the parsed
|
|
24
|
+
// docs in. This resolver is filesystem-free and pure so it stays unit-testable.
|
|
25
|
+
//
|
|
26
|
+
// Cross-document validation lives here (the rules that need the sibling docs):
|
|
27
|
+
// PC-001 project does not resolve to an admitted Activity
|
|
28
|
+
// PC-002 resolved Activity has activity_type other than "Project"
|
|
29
|
+
// PC-003 milestone.delivers_changes[] entry not in the project's own
|
|
30
|
+
// delivers_changes[]
|
|
31
|
+
// PC-004 (warning) milestone.date outside [valid_from, valid_to]
|
|
32
|
+
// LIFECYCLE-001 project valid_from missing / not a parseable ISO date
|
|
33
|
+
// LIFECYCLE-002 project valid_to present, not null, not a parseable date
|
|
34
|
+
// LIFECYCLE-003 project valid_to earlier than valid_from
|
|
35
|
+
//
|
|
36
|
+
// LIFECYCLE-* are scoped to the RESOLVED PROJECT Activity only — the card is
|
|
37
|
+
// built around the project's initiation date, so a missing valid_from there is
|
|
38
|
+
// a hard error. Child activities and FGCA elements are not lifecycle-checked
|
|
39
|
+
// here (out of this notation's scope; today's Studio activities docs may
|
|
40
|
+
// legitimately omit lifecycle fields).
|
|
41
|
+
|
|
42
|
+
import type {
|
|
43
|
+
ActivityCardDoc,
|
|
44
|
+
ActivityCardSources,
|
|
45
|
+
ResolvedActivityCard,
|
|
46
|
+
ResolvedAssessment,
|
|
47
|
+
ResolvedChange,
|
|
48
|
+
ResolvedChildActivity,
|
|
49
|
+
ResolvedDriver,
|
|
50
|
+
ResolvedGoal,
|
|
51
|
+
ResolvedMilestone,
|
|
52
|
+
ResolvedProject,
|
|
53
|
+
ResolvedStakeholder,
|
|
54
|
+
} from './types.js';
|
|
55
|
+
import type { ValidationError, ValidationWarning, ValidationResult } from '../validation-types.js';
|
|
56
|
+
|
|
57
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
58
|
+
|
|
59
|
+
export interface ActivityCardResolution extends ValidationResult {
|
|
60
|
+
/** Present only when resolution succeeded (project found + valid_from OK). */
|
|
61
|
+
resolved?: ResolvedActivityCard;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isObject(v: unknown): v is Record<string, unknown> {
|
|
65
|
+
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function str(v: unknown): string | undefined {
|
|
69
|
+
return typeof v === 'string' && v.trim().length > 0 ? v : undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function strArray(v: unknown): string[] {
|
|
73
|
+
if (!Array.isArray(v)) return [];
|
|
74
|
+
return v.filter((x): x is string => typeof x === 'string' && x.trim().length > 0);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A parseable quoted ISO 8601 date, calendar-checked (rejects 2026-13-40). */
|
|
78
|
+
function isIsoDate(v: unknown): v is string {
|
|
79
|
+
if (typeof v !== 'string' || !DATE_RE.test(v)) return false;
|
|
80
|
+
const t = Date.parse(`${v}T00:00:00Z`);
|
|
81
|
+
if (Number.isNaN(t)) return false;
|
|
82
|
+
// Round-trip guard so 2026-02-30 (which Date.parse may roll forward) is rejected.
|
|
83
|
+
return new Date(t).toISOString().slice(0, 10) === v;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Index canon element documents of one `notation` by their `id`. Each element
|
|
88
|
+
* is its own single-element file (`notation: activity|factor|goal|change|…`
|
|
89
|
+
* with a top-level `id`); first definition wins.
|
|
90
|
+
*/
|
|
91
|
+
function collectByNotation(
|
|
92
|
+
docs: unknown[],
|
|
93
|
+
notation: string,
|
|
94
|
+
): Map<string, Record<string, unknown>> {
|
|
95
|
+
const out = new Map<string, Record<string, unknown>>();
|
|
96
|
+
for (const doc of docs) {
|
|
97
|
+
if (!isObject(doc)) continue;
|
|
98
|
+
if (str(doc['notation']) !== notation) continue;
|
|
99
|
+
const id = str(doc['id']);
|
|
100
|
+
if (!id) continue;
|
|
101
|
+
if (!out.has(id)) out.set(id, doc); // first definition wins
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* GOAL ids reached by an *active* `activity_goal` relation originating at
|
|
108
|
+
* `fromId`. A relation with a non-null `valid_to` has ended (the activity was
|
|
109
|
+
* re-aimed) and is excluded — the card shows the currently-served goals.
|
|
110
|
+
*/
|
|
111
|
+
function activeActivityGoals(relations: unknown[], fromId: string): string[] {
|
|
112
|
+
const out: string[] = [];
|
|
113
|
+
for (const doc of relations) {
|
|
114
|
+
if (!isObject(doc)) continue;
|
|
115
|
+
if (str(doc['notation']) !== 'relation') continue;
|
|
116
|
+
if (str(doc['type']) !== 'activity_goal') continue;
|
|
117
|
+
if (str(doc['from']) !== fromId) continue;
|
|
118
|
+
const to = str(doc['to']);
|
|
119
|
+
if (!to) continue;
|
|
120
|
+
const validTo = doc['valid_to'];
|
|
121
|
+
if (validTo !== undefined && validTo !== null) continue; // ended relation
|
|
122
|
+
if (!out.includes(to)) out.push(to);
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* STAKEHOLDER ids + roles reached by an *active* `activity_stakeholder`
|
|
129
|
+
* relation originating at `fromId`. A relation with a non-null `valid_to` has
|
|
130
|
+
* ended and is excluded, so the card shows the project's currently-engaged
|
|
131
|
+
* stakeholders. The optional `role` field on the relation carries the
|
|
132
|
+
* project-role (initiator | owner | sponsor | project_manager).
|
|
133
|
+
*/
|
|
134
|
+
function activeActivityStakeholders(
|
|
135
|
+
relations: unknown[],
|
|
136
|
+
fromId: string,
|
|
137
|
+
): Array<{ id: string; role?: string }> {
|
|
138
|
+
const out: Array<{ id: string; role?: string }> = [];
|
|
139
|
+
for (const doc of relations) {
|
|
140
|
+
if (!isObject(doc)) continue;
|
|
141
|
+
if (str(doc['notation']) !== 'relation') continue;
|
|
142
|
+
if (str(doc['type']) !== 'activity_stakeholder') continue;
|
|
143
|
+
if (str(doc['from']) !== fromId) continue;
|
|
144
|
+
const to = str(doc['to']);
|
|
145
|
+
if (!to) continue;
|
|
146
|
+
const validTo = doc['valid_to'];
|
|
147
|
+
if (validTo !== undefined && validTo !== null) continue; // ended relation
|
|
148
|
+
if (!out.some((s) => s.id === to)) out.push({ id: to, role: str(doc['role']) });
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function resolveActivityCard(
|
|
154
|
+
doc: ActivityCardDoc,
|
|
155
|
+
sources: ActivityCardSources,
|
|
156
|
+
): ActivityCardResolution {
|
|
157
|
+
const errors: ValidationError[] = [];
|
|
158
|
+
const warnings: ValidationWarning[] = [];
|
|
159
|
+
|
|
160
|
+
const card = doc?.activity_card;
|
|
161
|
+
const projectId = str(card?.project);
|
|
162
|
+
if (!projectId) {
|
|
163
|
+
// Structural validator already flags this; nothing to resolve.
|
|
164
|
+
errors.push({ code: 'PC-001', message: 'activity_card.project is missing' });
|
|
165
|
+
return { valid: false, errors, warnings };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const activities = collectByNotation(sources.elements, 'activity');
|
|
169
|
+
const projectRec = activities.get(projectId);
|
|
170
|
+
|
|
171
|
+
// PC-001 — project must resolve to an admitted ACTIVITY element in canon.
|
|
172
|
+
if (!projectRec) {
|
|
173
|
+
errors.push({
|
|
174
|
+
code: 'PC-001',
|
|
175
|
+
message: `activity_card.project "${projectId}" does not resolve to an ACTIVITY element in the canon element store`,
|
|
176
|
+
});
|
|
177
|
+
return { valid: false, errors, warnings };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// LIFECYCLE-001..003 on the project Activity.
|
|
181
|
+
const validFrom = projectRec['valid_from'];
|
|
182
|
+
const validToRaw = projectRec['valid_to'];
|
|
183
|
+
let validFromOk = false;
|
|
184
|
+
if (validFrom === undefined || validFrom === null) {
|
|
185
|
+
errors.push({ code: 'LIFECYCLE-001', message: `Activity "${projectId}" valid_from is required` });
|
|
186
|
+
} else if (!isIsoDate(validFrom)) {
|
|
187
|
+
errors.push({
|
|
188
|
+
code: 'LIFECYCLE-001',
|
|
189
|
+
message: `Activity "${projectId}" valid_from "${String(validFrom)}" is not a parseable ISO 8601 date`,
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
validFromOk = true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let validToOk = false;
|
|
196
|
+
const validToPresent = validToRaw !== undefined && validToRaw !== null;
|
|
197
|
+
if (validToPresent) {
|
|
198
|
+
if (!isIsoDate(validToRaw)) {
|
|
199
|
+
errors.push({
|
|
200
|
+
code: 'LIFECYCLE-002',
|
|
201
|
+
message: `Activity "${projectId}" valid_to "${String(validToRaw)}" is not a parseable ISO 8601 date`,
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
validToOk = true;
|
|
205
|
+
if (validFromOk && (validToRaw as string) < (validFrom as string)) {
|
|
206
|
+
errors.push({
|
|
207
|
+
code: 'LIFECYCLE-003',
|
|
208
|
+
message: `Activity "${projectId}" valid_to "${String(validToRaw)}" is earlier than valid_from "${String(validFrom)}"`,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// A failed lifecycle/PC-001 stops us producing a card view-model, but we still
|
|
215
|
+
// return every error collected so the panel lists them all at once.
|
|
216
|
+
const projectChanges = new Set(strArray(projectRec['delivers_changes']));
|
|
217
|
+
// Project goals: prefer the first-class `activity_goal` relations (the
|
|
218
|
+
// canonical, time-aware home for an Activity→Goal link); fall back to the
|
|
219
|
+
// activity element's transitional inline `goals[]` when no relation exists
|
|
220
|
+
// (07-activities.md §"Time-aware relations").
|
|
221
|
+
const relGoalIds = activeActivityGoals(sources.relations, projectId);
|
|
222
|
+
const projectGoalIds = relGoalIds.length > 0 ? relGoalIds : strArray(projectRec['goals']);
|
|
223
|
+
|
|
224
|
+
// PC-003 — each milestone change must be a subset of the project's changes.
|
|
225
|
+
const rawMilestones = Array.isArray(card.milestones) ? card.milestones : [];
|
|
226
|
+
const milestones: ResolvedMilestone[] = [];
|
|
227
|
+
for (const m of rawMilestones) {
|
|
228
|
+
if (!isObject(m)) continue;
|
|
229
|
+
const mid = str(m['id']);
|
|
230
|
+
const mname = str(m['name']);
|
|
231
|
+
const mdate = str(m['date']);
|
|
232
|
+
if (!mid || !mname || !mdate) continue; // structural validator already flagged
|
|
233
|
+
const deliversChanges = strArray(m['delivers_changes']);
|
|
234
|
+
for (const c of deliversChanges) {
|
|
235
|
+
if (!projectChanges.has(c)) {
|
|
236
|
+
errors.push({
|
|
237
|
+
code: 'PC-003',
|
|
238
|
+
message: `Milestone "${mid}" delivers_changes "${c}" is not in project "${projectId}" delivers_changes`,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// PC-004 — milestone date should fall within the project lifecycle window.
|
|
243
|
+
if (validFromOk && mdate < (validFrom as string)) {
|
|
244
|
+
warnings.push({
|
|
245
|
+
code: 'PC-004',
|
|
246
|
+
message: `Milestone "${mid}" date "${mdate}" is before project valid_from "${String(validFrom)}"`,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (validToOk && mdate > (validToRaw as string)) {
|
|
250
|
+
warnings.push({
|
|
251
|
+
code: 'PC-004',
|
|
252
|
+
message: `Milestone "${mid}" date "${mdate}" is after project valid_to "${String(validToRaw)}"`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
milestones.push({
|
|
256
|
+
id: mid,
|
|
257
|
+
name: mname,
|
|
258
|
+
date: mdate,
|
|
259
|
+
description: str(m['description']),
|
|
260
|
+
deliversChanges,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
milestones.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0));
|
|
264
|
+
|
|
265
|
+
// If we have hard errors, surface them without an assembled card.
|
|
266
|
+
if (errors.length > 0) {
|
|
267
|
+
return { valid: false, errors, warnings };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ── Motivation chain — expand against canon DRIVER / GOAL / CHANGE elements ──
|
|
271
|
+
// Note: YAML corpus still uses `notation: factor` and `goal.factors` field names;
|
|
272
|
+
// the YAML keys are stable. The resolved TypeScript API uses "driver/driverIds".
|
|
273
|
+
const driverElems = collectByNotation(sources.elements, 'factor');
|
|
274
|
+
const goalElems = collectByNotation(sources.elements, 'goal');
|
|
275
|
+
const changeElems = collectByNotation(sources.elements, 'change');
|
|
276
|
+
|
|
277
|
+
const changes: ResolvedChange[] = [];
|
|
278
|
+
for (const cid of projectChanges) {
|
|
279
|
+
const rec = changeElems.get(cid);
|
|
280
|
+
if (!rec) {
|
|
281
|
+
warnings.push({
|
|
282
|
+
code: 'PC-001',
|
|
283
|
+
message: `Project change "${cid}" not found as a CHANGE element in canon; omitted from the motivation chain`,
|
|
284
|
+
});
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
changes.push({ id: cid, name: str(rec['name']) ?? cid, goalIds: strArray(rec['goals']) });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Goals shown = the project's declared goals ∪ goals referenced by in-scope
|
|
291
|
+
// changes (so the D→G→C chain stays connected).
|
|
292
|
+
const goalIdSet = new Set<string>(projectGoalIds);
|
|
293
|
+
for (const ch of changes) for (const g of ch.goalIds) goalIdSet.add(g);
|
|
294
|
+
|
|
295
|
+
const goals: ResolvedGoal[] = [];
|
|
296
|
+
for (const gid of goalIdSet) {
|
|
297
|
+
const rec = goalElems.get(gid);
|
|
298
|
+
if (!rec) {
|
|
299
|
+
warnings.push({
|
|
300
|
+
code: 'PC-001',
|
|
301
|
+
message: `Goal "${gid}" not found as a GOAL element in canon; omitted from the motivation chain`,
|
|
302
|
+
});
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
goals.push({ id: gid, name: str(rec['name']) ?? gid, driverIds: strArray(rec['factors']) });
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Drivers shown = those referenced by the in-scope goals.
|
|
309
|
+
const driverIdSet = new Set<string>();
|
|
310
|
+
for (const g of goals) for (const d of g.driverIds) driverIdSet.add(d);
|
|
311
|
+
|
|
312
|
+
const drivers: ResolvedDriver[] = [];
|
|
313
|
+
for (const did of driverIdSet) {
|
|
314
|
+
const rec = driverElems.get(did);
|
|
315
|
+
drivers.push({ id: did, name: rec ? str(rec['name']) ?? did : did });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ── Assessments — findings that assess an in-scope Driver ────────────────────
|
|
319
|
+
// Assessments answer "what specific problem are we solving?" They are resolved
|
|
320
|
+
// from ASSESSMENT elements whose `assesses` field matches a Driver id that
|
|
321
|
+
// appeared in the motivation chain above. Empty when none exist — the gap is
|
|
322
|
+
// intentionally visible on the card so the author knows it needs filling.
|
|
323
|
+
const driverIdLookup = new Set(drivers.map((d) => d.id));
|
|
324
|
+
const assessmentElems = collectByNotation(sources.elements, 'assessment');
|
|
325
|
+
const assessments: ResolvedAssessment[] = [];
|
|
326
|
+
for (const [aid, rec] of assessmentElems) {
|
|
327
|
+
const driverId = str(rec['assesses']);
|
|
328
|
+
if (!driverId || !driverIdLookup.has(driverId)) continue;
|
|
329
|
+
assessments.push({
|
|
330
|
+
id: aid,
|
|
331
|
+
name: str(rec['name']) ?? aid,
|
|
332
|
+
driverId,
|
|
333
|
+
description: str(rec['description']),
|
|
334
|
+
observed_at: str(rec['observed_at']),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
assessments.sort((a, b) => {
|
|
338
|
+
if (!a.observed_at && !b.observed_at) return 0;
|
|
339
|
+
if (!a.observed_at) return 1;
|
|
340
|
+
if (!b.observed_at) return -1;
|
|
341
|
+
return a.observed_at < b.observed_at ? -1 : a.observed_at > b.observed_at ? 1 : 0;
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// ── Child activities — parent = project id ──────────────────────────────────
|
|
345
|
+
const childActivities: ResolvedChildActivity[] = [];
|
|
346
|
+
for (const [id, rec] of activities) {
|
|
347
|
+
if (str(rec['parent']) !== projectId) continue;
|
|
348
|
+
childActivities.push({
|
|
349
|
+
id,
|
|
350
|
+
name: str(rec['name']) ?? id,
|
|
351
|
+
activity_type: str(rec['activity_type']),
|
|
352
|
+
start_date: str(rec['start_date']),
|
|
353
|
+
end_date: str(rec['end_date']),
|
|
354
|
+
owner: str(rec['owner']),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
childActivities.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
358
|
+
|
|
359
|
+
// ── Project goal text field — names of the DIRECTLY-served goals ────────────
|
|
360
|
+
// The "Project goal" field shows the goals the project itself serves (the
|
|
361
|
+
// `activity_goal` targets), not the wider F→G→C chain (which also pulls in
|
|
362
|
+
// goals referenced only by in-scope changes). Resolve each id to its GOAL
|
|
363
|
+
// element name, falling back to the id when the element is absent.
|
|
364
|
+
const goalNames = projectGoalIds.map((gid) => str(goalElems.get(gid)?.['name']) ?? gid);
|
|
365
|
+
|
|
366
|
+
// ── Stakeholders — active `activity_stakeholder` relations from the project ──
|
|
367
|
+
const stakeholderElems = collectByNotation(sources.elements, 'stakeholder');
|
|
368
|
+
const stakeholders: ResolvedStakeholder[] = [];
|
|
369
|
+
for (const { id: sid, role } of activeActivityStakeholders(sources.relations, projectId)) {
|
|
370
|
+
const rec = stakeholderElems.get(sid);
|
|
371
|
+
if (!rec) {
|
|
372
|
+
warnings.push({
|
|
373
|
+
code: 'PC-001',
|
|
374
|
+
message: `Stakeholder "${sid}" not found as a STAKEHOLDER element in canon; shown by id`,
|
|
375
|
+
});
|
|
376
|
+
stakeholders.push({ id: sid, name: sid, role });
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
stakeholders.push({
|
|
380
|
+
id: sid,
|
|
381
|
+
name: str(rec['name']) ?? sid,
|
|
382
|
+
type: str(rec['type']),
|
|
383
|
+
interest: str(rec['interest']),
|
|
384
|
+
influence: str(rec['influence']),
|
|
385
|
+
role,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const project: ResolvedProject = {
|
|
390
|
+
id: projectId,
|
|
391
|
+
name: str(projectRec['name']) ?? projectId,
|
|
392
|
+
description: str(projectRec['description']),
|
|
393
|
+
valid_from: validFromOk ? (validFrom as string) : undefined,
|
|
394
|
+
valid_to: validToOk ? (validToRaw as string) : undefined,
|
|
395
|
+
start_date: str(projectRec['start_date']),
|
|
396
|
+
end_date: str(projectRec['end_date']),
|
|
397
|
+
activity_type: str(projectRec['activity_type']),
|
|
398
|
+
status: str(projectRec['status']),
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const resolved: ResolvedActivityCard = {
|
|
402
|
+
cardId: str(card.id) ?? '',
|
|
403
|
+
cardDescription: str(card.description),
|
|
404
|
+
project,
|
|
405
|
+
milestones,
|
|
406
|
+
motivation: { drivers, goals, changes },
|
|
407
|
+
assessments,
|
|
408
|
+
childActivities,
|
|
409
|
+
goalNames,
|
|
410
|
+
stakeholders,
|
|
411
|
+
notes: str(card.notes),
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
return { valid: true, errors, warnings, resolved };
|
|
415
|
+
}
|