@unified-product-graph/core 0.7.4 → 0.7.5
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/CHANGELOG.md +33 -0
- package/dist/index.cjs +433 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +130 -5
- package/dist/index.d.ts +130 -5
- package/dist/index.js +425 -125
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -49,6 +49,7 @@ __export(index_exports, {
|
|
|
49
49
|
UPG_ANTI_PATTERNS: () => UPG_ANTI_PATTERNS,
|
|
50
50
|
UPG_APPROACHES: () => UPG_APPROACHES,
|
|
51
51
|
UPG_APPROACHES_BY_ID: () => UPG_APPROACHES_BY_ID,
|
|
52
|
+
UPG_CANONICAL_FORMAT_VERSION: () => UPG_CANONICAL_FORMAT_VERSION,
|
|
52
53
|
UPG_CONTAINMENT_FREE_TYPES: () => UPG_CONTAINMENT_FREE_TYPES,
|
|
53
54
|
UPG_COUNT_BENCHMARKS: () => UPG_COUNT_BENCHMARKS,
|
|
54
55
|
UPG_CROSS_EDGE_TYPES: () => UPG_CROSS_EDGE_TYPES,
|
|
@@ -112,8 +113,10 @@ __export(index_exports, {
|
|
|
112
113
|
coerceProductStage: () => coerceProductStage,
|
|
113
114
|
collectSlugsForType: () => collectSlugsForType,
|
|
114
115
|
compareVersions: () => compareVersions,
|
|
116
|
+
computeBodyChecksum: () => computeBodyChecksum,
|
|
115
117
|
evaluateAntiPatterns: () => evaluateAntiPatterns,
|
|
116
118
|
findInvalidStatusMigrationTargets: () => findInvalidStatusMigrationTargets,
|
|
119
|
+
formatUpgText: () => formatUpgText,
|
|
117
120
|
generateSlug: () => generateSlug,
|
|
118
121
|
getAnchorEntity: () => getAnchorEntity,
|
|
119
122
|
getAntiPatternById: () => getAntiPatternById,
|
|
@@ -156,6 +159,7 @@ __export(index_exports, {
|
|
|
156
159
|
getValidChildren: () => getValidChildren,
|
|
157
160
|
getVisibleTypes: () => getVisibleTypes,
|
|
158
161
|
hasStatusMigration: () => hasStatusMigration,
|
|
162
|
+
isCanonical: () => isCanonical,
|
|
159
163
|
isCanonicalProductStage: () => isCanonicalProductStage,
|
|
160
164
|
isContainmentFreeType: () => isContainmentFreeType,
|
|
161
165
|
isDeprecatedType: () => isDeprecatedType,
|
|
@@ -166,6 +170,7 @@ __export(index_exports, {
|
|
|
166
170
|
isLifecycleFreeType: () => isLifecycleFreeType,
|
|
167
171
|
isLifecyclePlannedType: () => isLifecyclePlannedType,
|
|
168
172
|
isPolymorphicEdge: () => isPolymorphicEdge,
|
|
173
|
+
isPortfolio: () => isPortfolio,
|
|
169
174
|
isRegisteredPolymorphicEdge: () => isRegisteredPolymorphicEdge,
|
|
170
175
|
isSubSequenceStep: () => isSubSequenceStep,
|
|
171
176
|
isUPGDocument: () => isUPGDocument,
|
|
@@ -175,6 +180,8 @@ __export(index_exports, {
|
|
|
175
180
|
migrateNodeProperties: () => migrateNodeProperties,
|
|
176
181
|
migrateProductStage: () => migrateProductStage,
|
|
177
182
|
migrateStatusValue: () => migrateStatusValue,
|
|
183
|
+
normalizeDocument: () => normalizeDocument,
|
|
184
|
+
parseUpg: () => parseUpg,
|
|
178
185
|
pickCanonicalEdge: () => pickCanonicalEdge,
|
|
179
186
|
resolveAllEdges: () => resolveAllEdges,
|
|
180
187
|
resolveContainmentEdge: () => resolveContainmentEdge,
|
|
@@ -182,6 +189,7 @@ __export(index_exports, {
|
|
|
182
189
|
resolveLabel: () => resolveLabel,
|
|
183
190
|
resolveSlugCollision: () => resolveSlugCollision,
|
|
184
191
|
rotateSlug: () => rotateSlug,
|
|
192
|
+
serializeCanonical: () => serializeCanonical,
|
|
185
193
|
validateProductStageStrict: () => validateProductStageStrict,
|
|
186
194
|
validateUPGDocument: () => validateUPGDocument,
|
|
187
195
|
validateUPGFramework: () => validateUPGFramework,
|
|
@@ -12821,26 +12829,6 @@ var UPG_FRAMEWORKS = [
|
|
|
12821
12829
|
"label": "Items to score",
|
|
12822
12830
|
"entityTypeId": "feature",
|
|
12823
12831
|
"description": "Features, opportunities, or solutions being evaluated"
|
|
12824
|
-
},
|
|
12825
|
-
{
|
|
12826
|
-
"label": "Reach",
|
|
12827
|
-
"entityTypeId": "feature",
|
|
12828
|
-
"description": "How many users does this affect per quarter?"
|
|
12829
|
-
},
|
|
12830
|
-
{
|
|
12831
|
-
"label": "Impact",
|
|
12832
|
-
"entityTypeId": "feature",
|
|
12833
|
-
"description": "How much does this move the target outcome?"
|
|
12834
|
-
},
|
|
12835
|
-
{
|
|
12836
|
-
"label": "Confidence",
|
|
12837
|
-
"entityTypeId": "feature",
|
|
12838
|
-
"description": "How sure are we about these estimates?"
|
|
12839
|
-
},
|
|
12840
|
-
{
|
|
12841
|
-
"label": "Effort",
|
|
12842
|
-
"entityTypeId": "feature",
|
|
12843
|
-
"description": "How many person-months to build?"
|
|
12844
12832
|
}
|
|
12845
12833
|
],
|
|
12846
12834
|
"data": {
|
|
@@ -12854,31 +12842,35 @@ var UPG_FRAMEWORKS = [
|
|
|
12854
12842
|
"feature": [
|
|
12855
12843
|
{
|
|
12856
12844
|
"property": "reach",
|
|
12857
|
-
"type": "
|
|
12845
|
+
"type": "assessment",
|
|
12846
|
+
"scale_id": "reach_5",
|
|
12858
12847
|
"required": true,
|
|
12859
12848
|
"label": "Reach",
|
|
12860
12849
|
"description": "How many users will this impact per quarter?"
|
|
12861
12850
|
},
|
|
12862
12851
|
{
|
|
12863
12852
|
"property": "impact",
|
|
12864
|
-
"type": "
|
|
12853
|
+
"type": "assessment",
|
|
12854
|
+
"scale_id": "impact_5",
|
|
12865
12855
|
"required": true,
|
|
12866
12856
|
"label": "Impact",
|
|
12867
|
-
"description": "How much will this impact each user
|
|
12857
|
+
"description": "How much will this impact each user, on the impact scale?"
|
|
12868
12858
|
},
|
|
12869
12859
|
{
|
|
12870
12860
|
"property": "confidence",
|
|
12871
|
-
"type": "
|
|
12861
|
+
"type": "assessment",
|
|
12862
|
+
"scale_id": "confidence_5",
|
|
12872
12863
|
"required": true,
|
|
12873
12864
|
"label": "Confidence",
|
|
12874
|
-
"description": "How confident are you in the
|
|
12865
|
+
"description": "How confident are you in the reach, impact, and effort estimates?"
|
|
12875
12866
|
},
|
|
12876
12867
|
{
|
|
12877
12868
|
"property": "effort",
|
|
12878
|
-
"type": "
|
|
12869
|
+
"type": "assessment",
|
|
12870
|
+
"scale_id": "effort_5",
|
|
12879
12871
|
"required": true,
|
|
12880
12872
|
"label": "Effort",
|
|
12881
|
-
"description": "
|
|
12873
|
+
"description": "How much work is required to build and ship this, on the effort scale?"
|
|
12882
12874
|
}
|
|
12883
12875
|
]
|
|
12884
12876
|
},
|
|
@@ -13072,7 +13064,7 @@ var UPG_FRAMEWORKS = [
|
|
|
13072
13064
|
{
|
|
13073
13065
|
"label": "Performance",
|
|
13074
13066
|
"entityTypeId": "feature",
|
|
13075
|
-
"description": "More is better
|
|
13067
|
+
"description": "More is better: linear satisfaction increase"
|
|
13076
13068
|
},
|
|
13077
13069
|
{
|
|
13078
13070
|
"label": "Delighters",
|
|
@@ -13321,24 +13313,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13321
13313
|
],
|
|
13322
13314
|
"slots": [
|
|
13323
13315
|
{
|
|
13324
|
-
"label": "
|
|
13325
|
-
"entityTypeId": "feature",
|
|
13326
|
-
"description": "Non-negotiable requirements for this release"
|
|
13327
|
-
},
|
|
13328
|
-
{
|
|
13329
|
-
"label": "Should Have",
|
|
13330
|
-
"entityTypeId": "feature",
|
|
13331
|
-
"description": "Important but not critical; a workaround exists"
|
|
13332
|
-
},
|
|
13333
|
-
{
|
|
13334
|
-
"label": "Could Have",
|
|
13335
|
-
"entityTypeId": "feature",
|
|
13336
|
-
"description": "Nice to have; include if time permits"
|
|
13337
|
-
},
|
|
13338
|
-
{
|
|
13339
|
-
"label": "Won't Have",
|
|
13316
|
+
"label": "Requirements to categorise",
|
|
13340
13317
|
"entityTypeId": "feature",
|
|
13341
|
-
"description": "
|
|
13318
|
+
"description": "Features or requirements sorted into the four MoSCoW buckets"
|
|
13342
13319
|
}
|
|
13343
13320
|
],
|
|
13344
13321
|
"data": {
|
|
@@ -13348,7 +13325,23 @@ var UPG_FRAMEWORKS = [
|
|
|
13348
13325
|
"role": "scored_item"
|
|
13349
13326
|
}
|
|
13350
13327
|
],
|
|
13351
|
-
"required_properties": {
|
|
13328
|
+
"required_properties": {
|
|
13329
|
+
"feature": [
|
|
13330
|
+
{
|
|
13331
|
+
"property": "moscow",
|
|
13332
|
+
"type": "enum",
|
|
13333
|
+
"required": true,
|
|
13334
|
+
"label": "MoSCoW priority",
|
|
13335
|
+
"description": "Which scope bucket this requirement falls into for the current release",
|
|
13336
|
+
"enum_values": [
|
|
13337
|
+
"must",
|
|
13338
|
+
"should",
|
|
13339
|
+
"could",
|
|
13340
|
+
"wont"
|
|
13341
|
+
]
|
|
13342
|
+
}
|
|
13343
|
+
]
|
|
13344
|
+
},
|
|
13352
13345
|
"computed_properties": [
|
|
13353
13346
|
{
|
|
13354
13347
|
"property": "must_have_ratio",
|
|
@@ -13368,28 +13361,19 @@ var UPG_FRAMEWORKS = [
|
|
|
13368
13361
|
"columns": [
|
|
13369
13362
|
{
|
|
13370
13363
|
"property": "title",
|
|
13371
|
-
"label": "
|
|
13372
|
-
"sortable": true
|
|
13373
|
-
},
|
|
13374
|
-
{
|
|
13375
|
-
"property": "title",
|
|
13376
|
-
"label": "Should Have",
|
|
13377
|
-
"sortable": true
|
|
13378
|
-
},
|
|
13379
|
-
{
|
|
13380
|
-
"property": "title",
|
|
13381
|
-
"label": "Could Have",
|
|
13364
|
+
"label": "Requirement",
|
|
13382
13365
|
"sortable": true
|
|
13383
13366
|
},
|
|
13384
13367
|
{
|
|
13385
|
-
"property": "
|
|
13386
|
-
"label": "
|
|
13387
|
-
"sortable": true
|
|
13368
|
+
"property": "moscow",
|
|
13369
|
+
"label": "Priority",
|
|
13370
|
+
"sortable": true,
|
|
13371
|
+
"format": "badge"
|
|
13388
13372
|
}
|
|
13389
13373
|
]
|
|
13390
13374
|
},
|
|
13391
13375
|
"sort_by": {
|
|
13392
|
-
"property": "
|
|
13376
|
+
"property": "moscow",
|
|
13393
13377
|
"direction": "asc"
|
|
13394
13378
|
},
|
|
13395
13379
|
"colour_by": "type",
|
|
@@ -13940,24 +13924,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13940
13924
|
],
|
|
13941
13925
|
"slots": [
|
|
13942
13926
|
{
|
|
13943
|
-
"label": "
|
|
13927
|
+
"label": "Delivery metric",
|
|
13944
13928
|
"entityTypeId": "metric",
|
|
13945
|
-
"description": "
|
|
13946
|
-
},
|
|
13947
|
-
{
|
|
13948
|
-
"label": "Lead Time for Changes",
|
|
13949
|
-
"entityTypeId": "metric",
|
|
13950
|
-
"description": "Time from commit to production"
|
|
13951
|
-
},
|
|
13952
|
-
{
|
|
13953
|
-
"label": "Change Failure Rate",
|
|
13954
|
-
"entityTypeId": "metric",
|
|
13955
|
-
"description": "Percentage of deployments causing failures"
|
|
13956
|
-
},
|
|
13957
|
-
{
|
|
13958
|
-
"label": "Time to Restore",
|
|
13959
|
-
"entityTypeId": "metric",
|
|
13960
|
-
"description": "How long to recover from a failure"
|
|
13929
|
+
"description": "One of the four DORA software-delivery performance metrics"
|
|
13961
13930
|
}
|
|
13962
13931
|
],
|
|
13963
13932
|
"data": {
|
|
@@ -13967,7 +13936,36 @@ var UPG_FRAMEWORKS = [
|
|
|
13967
13936
|
"role": "item"
|
|
13968
13937
|
}
|
|
13969
13938
|
],
|
|
13970
|
-
"required_properties": {
|
|
13939
|
+
"required_properties": {
|
|
13940
|
+
"metric": [
|
|
13941
|
+
{
|
|
13942
|
+
"property": "dora_metric",
|
|
13943
|
+
"type": "enum",
|
|
13944
|
+
"required": true,
|
|
13945
|
+
"label": "DORA metric",
|
|
13946
|
+
"description": "Which of the four DORA metrics this measures",
|
|
13947
|
+
"enum_values": [
|
|
13948
|
+
"deployment_frequency",
|
|
13949
|
+
"lead_time_for_changes",
|
|
13950
|
+
"change_failure_rate",
|
|
13951
|
+
"time_to_restore"
|
|
13952
|
+
]
|
|
13953
|
+
},
|
|
13954
|
+
{
|
|
13955
|
+
"property": "performance_tier",
|
|
13956
|
+
"type": "enum",
|
|
13957
|
+
"required": false,
|
|
13958
|
+
"label": "Performance tier",
|
|
13959
|
+
"description": "Where this metric sits on the DORA elite-to-low benchmark",
|
|
13960
|
+
"enum_values": [
|
|
13961
|
+
"elite",
|
|
13962
|
+
"high",
|
|
13963
|
+
"medium",
|
|
13964
|
+
"low"
|
|
13965
|
+
]
|
|
13966
|
+
}
|
|
13967
|
+
]
|
|
13968
|
+
},
|
|
13971
13969
|
"computed_properties": [
|
|
13972
13970
|
{
|
|
13973
13971
|
"property": "stability_index",
|
|
@@ -14123,29 +14121,9 @@ var UPG_FRAMEWORKS = [
|
|
|
14123
14121
|
],
|
|
14124
14122
|
"slots": [
|
|
14125
14123
|
{
|
|
14126
|
-
"label": "
|
|
14124
|
+
"label": "Lifecycle metric",
|
|
14127
14125
|
"entityTypeId": "metric",
|
|
14128
|
-
"description": "
|
|
14129
|
-
},
|
|
14130
|
-
{
|
|
14131
|
-
"label": "Activation",
|
|
14132
|
-
"entityTypeId": "metric",
|
|
14133
|
-
"description": "Do users have a great first experience?"
|
|
14134
|
-
},
|
|
14135
|
-
{
|
|
14136
|
-
"label": "Retention",
|
|
14137
|
-
"entityTypeId": "metric",
|
|
14138
|
-
"description": "Do users come back?"
|
|
14139
|
-
},
|
|
14140
|
-
{
|
|
14141
|
-
"label": "Revenue",
|
|
14142
|
-
"entityTypeId": "metric",
|
|
14143
|
-
"description": "Can you monetise the behaviour?"
|
|
14144
|
-
},
|
|
14145
|
-
{
|
|
14146
|
-
"label": "Referral",
|
|
14147
|
-
"entityTypeId": "metric",
|
|
14148
|
-
"description": "Do users tell others?"
|
|
14126
|
+
"description": "A metric tracking one stage of the customer lifecycle funnel"
|
|
14149
14127
|
}
|
|
14150
14128
|
],
|
|
14151
14129
|
"data": {
|
|
@@ -14155,7 +14133,24 @@ var UPG_FRAMEWORKS = [
|
|
|
14155
14133
|
"role": "item"
|
|
14156
14134
|
}
|
|
14157
14135
|
],
|
|
14158
|
-
"required_properties": {
|
|
14136
|
+
"required_properties": {
|
|
14137
|
+
"metric": [
|
|
14138
|
+
{
|
|
14139
|
+
"property": "lifecycle_stage",
|
|
14140
|
+
"type": "enum",
|
|
14141
|
+
"required": true,
|
|
14142
|
+
"label": "Lifecycle stage",
|
|
14143
|
+
"description": "Which AARRR funnel stage this metric measures",
|
|
14144
|
+
"enum_values": [
|
|
14145
|
+
"acquisition",
|
|
14146
|
+
"activation",
|
|
14147
|
+
"retention",
|
|
14148
|
+
"revenue",
|
|
14149
|
+
"referral"
|
|
14150
|
+
]
|
|
14151
|
+
}
|
|
14152
|
+
]
|
|
14153
|
+
},
|
|
14159
14154
|
"computed_properties": [
|
|
14160
14155
|
{
|
|
14161
14156
|
"property": "conversion_rate",
|
|
@@ -14167,7 +14162,39 @@ var UPG_FRAMEWORKS = [
|
|
|
14167
14162
|
]
|
|
14168
14163
|
},
|
|
14169
14164
|
"structure": {
|
|
14170
|
-
"pattern": "funnel"
|
|
14165
|
+
"pattern": "funnel",
|
|
14166
|
+
"stages": [
|
|
14167
|
+
{
|
|
14168
|
+
"id": "acquisition",
|
|
14169
|
+
"label": "Acquisition",
|
|
14170
|
+
"order": 0,
|
|
14171
|
+
"entity_type": "metric"
|
|
14172
|
+
},
|
|
14173
|
+
{
|
|
14174
|
+
"id": "activation",
|
|
14175
|
+
"label": "Activation",
|
|
14176
|
+
"order": 1,
|
|
14177
|
+
"entity_type": "metric"
|
|
14178
|
+
},
|
|
14179
|
+
{
|
|
14180
|
+
"id": "retention",
|
|
14181
|
+
"label": "Retention",
|
|
14182
|
+
"order": 2,
|
|
14183
|
+
"entity_type": "metric"
|
|
14184
|
+
},
|
|
14185
|
+
{
|
|
14186
|
+
"id": "revenue",
|
|
14187
|
+
"label": "Revenue",
|
|
14188
|
+
"order": 3,
|
|
14189
|
+
"entity_type": "metric"
|
|
14190
|
+
},
|
|
14191
|
+
{
|
|
14192
|
+
"id": "referral",
|
|
14193
|
+
"label": "Referral",
|
|
14194
|
+
"order": 4,
|
|
14195
|
+
"entity_type": "metric"
|
|
14196
|
+
}
|
|
14197
|
+
]
|
|
14171
14198
|
},
|
|
14172
14199
|
"presentation": {
|
|
14173
14200
|
"layout": {
|
|
@@ -14214,24 +14241,9 @@ var UPG_FRAMEWORKS = [
|
|
|
14214
14241
|
],
|
|
14215
14242
|
"slots": [
|
|
14216
14243
|
{
|
|
14217
|
-
"label": "
|
|
14218
|
-
"entityTypeId": "metric",
|
|
14219
|
-
"description": "The single metric reflecting core value delivered"
|
|
14220
|
-
},
|
|
14221
|
-
{
|
|
14222
|
-
"label": "Input Metric 1",
|
|
14223
|
-
"entityTypeId": "metric",
|
|
14224
|
-
"description": "A driver metric you can directly influence"
|
|
14225
|
-
},
|
|
14226
|
-
{
|
|
14227
|
-
"label": "Input Metric 2",
|
|
14228
|
-
"entityTypeId": "metric",
|
|
14229
|
-
"description": "A driver metric you can directly influence"
|
|
14230
|
-
},
|
|
14231
|
-
{
|
|
14232
|
-
"label": "Input Metric 3",
|
|
14244
|
+
"label": "Metric",
|
|
14233
14245
|
"entityTypeId": "metric",
|
|
14234
|
-
"description": "
|
|
14246
|
+
"description": "The North Star metric or one of its 3-5 input (driver) metrics"
|
|
14235
14247
|
}
|
|
14236
14248
|
],
|
|
14237
14249
|
"data": {
|
|
@@ -14241,7 +14253,28 @@ var UPG_FRAMEWORKS = [
|
|
|
14241
14253
|
"role": "item"
|
|
14242
14254
|
}
|
|
14243
14255
|
],
|
|
14244
|
-
"required_properties": {
|
|
14256
|
+
"required_properties": {
|
|
14257
|
+
"metric": [
|
|
14258
|
+
{
|
|
14259
|
+
"property": "metric_role",
|
|
14260
|
+
"type": "enum",
|
|
14261
|
+
"required": true,
|
|
14262
|
+
"label": "Metric role",
|
|
14263
|
+
"description": "Whether this is the single North Star or a driver that feeds it",
|
|
14264
|
+
"enum_values": [
|
|
14265
|
+
"north_star",
|
|
14266
|
+
"input"
|
|
14267
|
+
]
|
|
14268
|
+
},
|
|
14269
|
+
{
|
|
14270
|
+
"property": "leverage",
|
|
14271
|
+
"type": "number",
|
|
14272
|
+
"required": false,
|
|
14273
|
+
"label": "Leverage",
|
|
14274
|
+
"description": "How strongly this input metric moves the North Star (input metrics only)"
|
|
14275
|
+
}
|
|
14276
|
+
]
|
|
14277
|
+
},
|
|
14245
14278
|
"computed_properties": [
|
|
14246
14279
|
{
|
|
14247
14280
|
"property": "nsm_impact",
|
|
@@ -14541,7 +14574,7 @@ var UPG_FRAMEWORKS = [
|
|
|
14541
14574
|
]
|
|
14542
14575
|
},
|
|
14543
14576
|
"education": {
|
|
14544
|
-
"purpose": "Design the product itself as the primary growth driver (free tier, self-serve onboarding, in-product virality)
|
|
14577
|
+
"purpose": "Design the product itself as the primary growth driver (free tier, self-serve onboarding, in-product virality) to reduce dependence on sales-led acquisition.",
|
|
14545
14578
|
"core_question": "Can our product acquire, activate, and expand users without human intervention, and where in the loop do we still need sales?",
|
|
14546
14579
|
"when_to_use": [
|
|
14547
14580
|
"You are launching a new product, feature, or entering a new market",
|
|
@@ -14951,7 +14984,7 @@ var UPG_FRAMEWORKS = [
|
|
|
14951
14984
|
{
|
|
14952
14985
|
"label": "Team",
|
|
14953
14986
|
"entityTypeId": "team",
|
|
14954
|
-
"description": "Team conducting the health check
|
|
14987
|
+
"description": "Team conducting the health check; all members vote anonymously on each dimension"
|
|
14955
14988
|
},
|
|
14956
14989
|
{
|
|
14957
14990
|
"label": "Retrospective",
|
|
@@ -15040,7 +15073,7 @@ var UPG_FRAMEWORKS = [
|
|
|
15040
15073
|
},
|
|
15041
15074
|
"education": {
|
|
15042
15075
|
"purpose": "Regularly assess team health across dimensions (psychological safety, autonomy, mission clarity, fun, speed, learning) to catch problems early and celebrate strengths.",
|
|
15043
|
-
"core_question": "How is the team really doing
|
|
15076
|
+
"core_question": "How is the team really doing: where do they feel strong, where do they feel stuck, and what has changed since last check?",
|
|
15044
15077
|
"when_to_use": [
|
|
15045
15078
|
"You need to improve team collaboration, clarity, or effectiveness",
|
|
15046
15079
|
"Roles and responsibilities are unclear or causing friction",
|
|
@@ -15056,7 +15089,7 @@ var UPG_FRAMEWORKS = [
|
|
|
15056
15089
|
"id": "raid-log",
|
|
15057
15090
|
"name": "RAID Log",
|
|
15058
15091
|
"version": "1.0.0",
|
|
15059
|
-
"description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies
|
|
15092
|
+
"description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies, the four categories most likely to derail a project if left unmanaged.",
|
|
15060
15093
|
"category": "program_mgmt",
|
|
15061
15094
|
"origin": {
|
|
15062
15095
|
"type": "practitioner",
|
|
@@ -23507,6 +23540,273 @@ function validateExpression(expr) {
|
|
|
23507
23540
|
return null;
|
|
23508
23541
|
}
|
|
23509
23542
|
|
|
23543
|
+
// src/format/canonical.ts
|
|
23544
|
+
var import_node_crypto = require("crypto");
|
|
23545
|
+
var UPG_CANONICAL_FORMAT_VERSION = "1.0.0";
|
|
23546
|
+
var INTEGRITY_HASH_PRIMITIVE = "sha256";
|
|
23547
|
+
var INTEGRITY_DIGEST_HEX = 32;
|
|
23548
|
+
var INTEGRITY_ALGORITHM = "sha256-128";
|
|
23549
|
+
function canonicalizeOpen(value) {
|
|
23550
|
+
if (Array.isArray(value)) return value.map(canonicalizeOpen);
|
|
23551
|
+
if (value !== null && typeof value === "object") {
|
|
23552
|
+
const out = {};
|
|
23553
|
+
for (const key of Object.keys(value).sort(byCodeUnit)) {
|
|
23554
|
+
out[key] = canonicalizeOpen(value[key]);
|
|
23555
|
+
}
|
|
23556
|
+
return out;
|
|
23557
|
+
}
|
|
23558
|
+
return value;
|
|
23559
|
+
}
|
|
23560
|
+
function byCodeUnit(a, b) {
|
|
23561
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
23562
|
+
}
|
|
23563
|
+
function isEmpty(value) {
|
|
23564
|
+
if (value === null || value === void 0) return true;
|
|
23565
|
+
if (typeof value === "string") return value.length === 0;
|
|
23566
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
23567
|
+
if (typeof value === "object") return Object.keys(value).length === 0;
|
|
23568
|
+
return false;
|
|
23569
|
+
}
|
|
23570
|
+
function orderedObject(source, keyOrder, opts = {}) {
|
|
23571
|
+
const force = new Set(opts.forceKeys ?? []);
|
|
23572
|
+
const open = new Set(opts.openKeys ?? []);
|
|
23573
|
+
const out = {};
|
|
23574
|
+
const emit = (key) => {
|
|
23575
|
+
if (!(key in source)) return;
|
|
23576
|
+
const raw = source[key];
|
|
23577
|
+
if (!force.has(key) && isEmpty(raw)) return;
|
|
23578
|
+
out[key] = open.has(key) ? canonicalizeOpen(raw) : raw;
|
|
23579
|
+
};
|
|
23580
|
+
for (const key of keyOrder) emit(key);
|
|
23581
|
+
for (const key of Object.keys(source).sort(byCodeUnit)) {
|
|
23582
|
+
if (!keyOrder.includes(key)) emit(key);
|
|
23583
|
+
}
|
|
23584
|
+
return out;
|
|
23585
|
+
}
|
|
23586
|
+
function repairNodeDrift(node) {
|
|
23587
|
+
const out = { ...node };
|
|
23588
|
+
if (typeof out.properties === "string") {
|
|
23589
|
+
out.properties = parseDriftString(out.properties, "object", `node ${node.id}.properties`);
|
|
23590
|
+
}
|
|
23591
|
+
if (typeof out.tags === "string") {
|
|
23592
|
+
out.tags = parseDriftString(out.tags, "array", `node ${node.id}.tags`);
|
|
23593
|
+
}
|
|
23594
|
+
return out;
|
|
23595
|
+
}
|
|
23596
|
+
function parseDriftString(raw, expect, where) {
|
|
23597
|
+
let parsed;
|
|
23598
|
+
try {
|
|
23599
|
+
parsed = JSON.parse(raw);
|
|
23600
|
+
} catch {
|
|
23601
|
+
throw new Error(`[upg fmt] ${where} is a string that is not valid JSON: ${truncate(raw)}`);
|
|
23602
|
+
}
|
|
23603
|
+
const ok = expect === "array" ? Array.isArray(parsed) : parsed !== null && typeof parsed === "object" && !Array.isArray(parsed);
|
|
23604
|
+
if (!ok) throw new Error(`[upg fmt] ${where} is a string that does not parse to a JSON ${expect}: ${truncate(raw)}`);
|
|
23605
|
+
return parsed;
|
|
23606
|
+
}
|
|
23607
|
+
function truncate(s) {
|
|
23608
|
+
return s.length > 60 ? s.slice(0, 57) + "..." : s;
|
|
23609
|
+
}
|
|
23610
|
+
function deriveSummary(description) {
|
|
23611
|
+
if (!description) return void 0;
|
|
23612
|
+
const firstLine = description.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
|
|
23613
|
+
if (!firstLine) return void 0;
|
|
23614
|
+
return firstLine.length > 200 ? firstLine.slice(0, 197) + "..." : firstLine;
|
|
23615
|
+
}
|
|
23616
|
+
function sortNodes(nodes) {
|
|
23617
|
+
return [...nodes].sort(
|
|
23618
|
+
(a, b) => byCodeUnit(a.type ?? "", b.type ?? "") || byCodeUnit(a.slug ?? a.title ?? "", b.slug ?? b.title ?? "") || byCodeUnit(a.id ?? "", b.id ?? "")
|
|
23619
|
+
);
|
|
23620
|
+
}
|
|
23621
|
+
function sortEdges(edges) {
|
|
23622
|
+
return [...edges].sort(
|
|
23623
|
+
(a, b) => byCodeUnit(a.source ?? "", b.source ?? "") || byCodeUnit(a.target ?? "", b.target ?? "") || byCodeUnit(a.type ?? "", b.type ?? "") || byCodeUnit(a.id ?? "", b.id ?? "")
|
|
23624
|
+
);
|
|
23625
|
+
}
|
|
23626
|
+
var NODE_KEY_ORDER = [
|
|
23627
|
+
"id",
|
|
23628
|
+
"type",
|
|
23629
|
+
"title",
|
|
23630
|
+
"slug",
|
|
23631
|
+
"aliases",
|
|
23632
|
+
"description",
|
|
23633
|
+
"tags",
|
|
23634
|
+
"status",
|
|
23635
|
+
"lifecycle_status",
|
|
23636
|
+
"source_id",
|
|
23637
|
+
"source_type",
|
|
23638
|
+
"mapping_confidence",
|
|
23639
|
+
"external_tool",
|
|
23640
|
+
"external_ref",
|
|
23641
|
+
"external_id",
|
|
23642
|
+
"sort_order",
|
|
23643
|
+
"properties"
|
|
23644
|
+
];
|
|
23645
|
+
var EDGE_KEY_ORDER = ["id", "source", "target", "type", "mapping_confidence"];
|
|
23646
|
+
var CROSS_EDGE_KEY_ORDER = ["id", "source", "target", "type", "source_product_id", "target_product_id", "mapping_confidence"];
|
|
23647
|
+
var PRODUCT_KEY_ORDER = ["id", "title", "description", "stage", "properties"];
|
|
23648
|
+
function canonicalNode(node) {
|
|
23649
|
+
const repaired = repairNodeDrift(node);
|
|
23650
|
+
if (Array.isArray(repaired.tags)) {
|
|
23651
|
+
repaired.tags = [...new Set(repaired.tags)].sort(byCodeUnit);
|
|
23652
|
+
}
|
|
23653
|
+
return orderedObject(repaired, NODE_KEY_ORDER, { forceKeys: ["id", "type", "title"], openKeys: ["properties"] });
|
|
23654
|
+
}
|
|
23655
|
+
function canonicalEdge(edge) {
|
|
23656
|
+
return orderedObject(edge, EDGE_KEY_ORDER, {
|
|
23657
|
+
forceKeys: ["id", "source", "target", "type"]
|
|
23658
|
+
});
|
|
23659
|
+
}
|
|
23660
|
+
function canonicalCrossEdge(edge) {
|
|
23661
|
+
return orderedObject(edge, CROSS_EDGE_KEY_ORDER, {
|
|
23662
|
+
forceKeys: ["id", "source", "target", "type"]
|
|
23663
|
+
});
|
|
23664
|
+
}
|
|
23665
|
+
function canonicalProduct(product) {
|
|
23666
|
+
return orderedObject(product, PRODUCT_KEY_ORDER, { forceKeys: ["id", "title"], openKeys: ["properties"] });
|
|
23667
|
+
}
|
|
23668
|
+
function singleBody(doc) {
|
|
23669
|
+
return {
|
|
23670
|
+
product: canonicalProduct(doc.product),
|
|
23671
|
+
nodes: sortNodes(doc.nodes ?? []).map(canonicalNode),
|
|
23672
|
+
edges: sortEdges(doc.edges ?? []).map(canonicalEdge)
|
|
23673
|
+
};
|
|
23674
|
+
}
|
|
23675
|
+
function portfolioBody(doc) {
|
|
23676
|
+
const products = [...doc.products ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((p) => {
|
|
23677
|
+
const { nodes, edges, ...rest } = p;
|
|
23678
|
+
return {
|
|
23679
|
+
...canonicalProduct(rest),
|
|
23680
|
+
nodes: sortNodes(nodes ?? []).map(canonicalNode),
|
|
23681
|
+
edges: sortEdges(edges ?? []).map(canonicalEdge)
|
|
23682
|
+
};
|
|
23683
|
+
});
|
|
23684
|
+
return {
|
|
23685
|
+
organization: orderedObject(doc.organization, ["id", "title", "description", "logo_url", "industry"], { forceKeys: ["id", "title"] }),
|
|
23686
|
+
product_areas: [...doc.product_areas ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((a) => orderedObject(a, ["id", "title", "description", "parent_area_id", "strategic_priority", "products"], { forceKeys: ["id", "title"] })),
|
|
23687
|
+
portfolios: [...doc.portfolios ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((p) => orderedObject(p, ["id", "title", "description", "parent_portfolio_id", "hierarchy_model", "products"], { forceKeys: ["id", "title"] })),
|
|
23688
|
+
products,
|
|
23689
|
+
cross_edges: sortEdges(doc.cross_edges ?? []).map(canonicalCrossEdge)
|
|
23690
|
+
};
|
|
23691
|
+
}
|
|
23692
|
+
function computeBodyChecksum(doc) {
|
|
23693
|
+
const body = isPortfolio(doc) ? portfolioBody(doc) : singleBody(doc);
|
|
23694
|
+
const content = JSON.stringify(body);
|
|
23695
|
+
return (0, import_node_crypto.createHash)(INTEGRITY_HASH_PRIMITIVE).update(content).digest("hex").slice(0, INTEGRITY_DIGEST_HEX);
|
|
23696
|
+
}
|
|
23697
|
+
function isPortfolio(doc) {
|
|
23698
|
+
return doc.type === "portfolio" || "cross_edges" in doc;
|
|
23699
|
+
}
|
|
23700
|
+
function serializeCanonical(doc, opts = {}) {
|
|
23701
|
+
if (isPortfolio(doc)) return serializePortfolioWithHeader(doc, opts);
|
|
23702
|
+
return serializeSingleWithHeader(doc, opts);
|
|
23703
|
+
}
|
|
23704
|
+
function buildProvenance(doc, opts) {
|
|
23705
|
+
const source = opts.source ?? doc.source ?? { tool: "unknown" };
|
|
23706
|
+
return orderedObject(
|
|
23707
|
+
{
|
|
23708
|
+
tool: source.tool,
|
|
23709
|
+
tool_version: source.tool_version,
|
|
23710
|
+
workspace_id: source.workspace_id,
|
|
23711
|
+
exported_at: opts.exportedAt ?? doc.exported_at
|
|
23712
|
+
},
|
|
23713
|
+
["tool", "tool_version", "workspace_id", "exported_at"],
|
|
23714
|
+
{ forceKeys: ["tool"] }
|
|
23715
|
+
);
|
|
23716
|
+
}
|
|
23717
|
+
function serializeSingleWithHeader(doc, opts) {
|
|
23718
|
+
const body = singleBody(doc);
|
|
23719
|
+
const product = doc.product;
|
|
23720
|
+
const header = {
|
|
23721
|
+
format_version: UPG_CANONICAL_FORMAT_VERSION,
|
|
23722
|
+
spec_version: doc.upg_version,
|
|
23723
|
+
product: orderedObject(
|
|
23724
|
+
{ id: product.id, title: product.title, stage: product.stage },
|
|
23725
|
+
["id", "title", "stage"],
|
|
23726
|
+
{ forceKeys: ["id", "title"] }
|
|
23727
|
+
)
|
|
23728
|
+
};
|
|
23729
|
+
const summary = deriveSummary(product.description);
|
|
23730
|
+
if (summary) header.summary = summary;
|
|
23731
|
+
header.counts = { nodes: doc.nodes?.length ?? 0, edges: doc.edges?.length ?? 0 };
|
|
23732
|
+
header.provenance = buildProvenance(doc, opts);
|
|
23733
|
+
header.integrity = { algorithm: INTEGRITY_ALGORITHM, body: computeBodyChecksum(doc) };
|
|
23734
|
+
return JSON.stringify({ $upg: header, ...body }, null, 2) + "\n";
|
|
23735
|
+
}
|
|
23736
|
+
function serializePortfolioWithHeader(doc, opts) {
|
|
23737
|
+
const body = portfolioBody(doc);
|
|
23738
|
+
const org = doc.organization;
|
|
23739
|
+
const header = {
|
|
23740
|
+
format_version: UPG_CANONICAL_FORMAT_VERSION,
|
|
23741
|
+
spec_version: doc.upg_version,
|
|
23742
|
+
kind: "portfolio",
|
|
23743
|
+
organization: orderedObject(
|
|
23744
|
+
{ id: org.id, title: org.title },
|
|
23745
|
+
["id", "title"],
|
|
23746
|
+
{ forceKeys: ["id", "title"] }
|
|
23747
|
+
)
|
|
23748
|
+
};
|
|
23749
|
+
const summary = deriveSummary(org.description);
|
|
23750
|
+
if (summary) header.summary = summary;
|
|
23751
|
+
header.counts = {
|
|
23752
|
+
products: doc.products?.length ?? 0,
|
|
23753
|
+
product_areas: doc.product_areas?.length ?? 0,
|
|
23754
|
+
portfolios: doc.portfolios?.length ?? 0,
|
|
23755
|
+
cross_edges: doc.cross_edges?.length ?? 0
|
|
23756
|
+
};
|
|
23757
|
+
header.provenance = buildProvenance(doc, opts);
|
|
23758
|
+
header.integrity = { algorithm: INTEGRITY_ALGORITHM, body: computeBodyChecksum(doc) };
|
|
23759
|
+
return JSON.stringify({ $upg: header, ...body }, null, 2) + "\n";
|
|
23760
|
+
}
|
|
23761
|
+
function parseUpg(text) {
|
|
23762
|
+
return normalizeDocument(JSON.parse(text));
|
|
23763
|
+
}
|
|
23764
|
+
function normalizeDocument(obj) {
|
|
23765
|
+
const raw = obj;
|
|
23766
|
+
const header = raw.$upg;
|
|
23767
|
+
const upg_version = header?.spec_version ?? raw.upg_version;
|
|
23768
|
+
const provenance = header?.provenance;
|
|
23769
|
+
const exported_at = provenance?.exported_at ?? raw.exported_at;
|
|
23770
|
+
const source = provenance ? { tool: provenance.tool, tool_version: provenance.tool_version, workspace_id: provenance.workspace_id } : raw.source;
|
|
23771
|
+
const _integrity = header ? void 0 : raw._integrity;
|
|
23772
|
+
const isPortfolioDoc = raw.type === "portfolio" || header?.kind === "portfolio" || "cross_edges" in raw;
|
|
23773
|
+
if (isPortfolioDoc) {
|
|
23774
|
+
const out2 = {
|
|
23775
|
+
upg_version,
|
|
23776
|
+
type: "portfolio",
|
|
23777
|
+
exported_at: exported_at ?? "",
|
|
23778
|
+
source: source ?? { tool: "unknown" },
|
|
23779
|
+
organization: raw.organization,
|
|
23780
|
+
product_areas: raw.product_areas ?? [],
|
|
23781
|
+
portfolios: raw.portfolios ?? [],
|
|
23782
|
+
products: (raw.products ?? []).map((p) => ({
|
|
23783
|
+
...p,
|
|
23784
|
+
nodes: (p.nodes ?? []).map((n) => repairNodeDrift(n)),
|
|
23785
|
+
edges: p.edges ?? []
|
|
23786
|
+
})),
|
|
23787
|
+
cross_edges: raw.cross_edges ?? []
|
|
23788
|
+
};
|
|
23789
|
+
return out2;
|
|
23790
|
+
}
|
|
23791
|
+
const out = {
|
|
23792
|
+
upg_version,
|
|
23793
|
+
exported_at: exported_at ?? "",
|
|
23794
|
+
source: source ?? { tool: "unknown" },
|
|
23795
|
+
product: raw.product,
|
|
23796
|
+
nodes: (raw.nodes ?? []).map((n) => repairNodeDrift(n)),
|
|
23797
|
+
edges: raw.edges ?? []
|
|
23798
|
+
};
|
|
23799
|
+
if (_integrity) out._integrity = _integrity;
|
|
23800
|
+
return out;
|
|
23801
|
+
}
|
|
23802
|
+
function formatUpgText(text, opts = {}) {
|
|
23803
|
+
return serializeCanonical(parseUpg(text), opts);
|
|
23804
|
+
}
|
|
23805
|
+
function isCanonical(text) {
|
|
23806
|
+
const parsed = parseUpg(text);
|
|
23807
|
+
return text === serializeCanonical(parsed);
|
|
23808
|
+
}
|
|
23809
|
+
|
|
23510
23810
|
// src/index.ts
|
|
23511
23811
|
var UPG_VERSION = "0.7.3";
|
|
23512
23812
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
@@ -23602,6 +23902,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
23602
23902
|
UPG_ANTI_PATTERNS,
|
|
23603
23903
|
UPG_APPROACHES,
|
|
23604
23904
|
UPG_APPROACHES_BY_ID,
|
|
23905
|
+
UPG_CANONICAL_FORMAT_VERSION,
|
|
23605
23906
|
UPG_CONTAINMENT_FREE_TYPES,
|
|
23606
23907
|
UPG_COUNT_BENCHMARKS,
|
|
23607
23908
|
UPG_CROSS_EDGE_TYPES,
|
|
@@ -23665,8 +23966,10 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
23665
23966
|
coerceProductStage,
|
|
23666
23967
|
collectSlugsForType,
|
|
23667
23968
|
compareVersions,
|
|
23969
|
+
computeBodyChecksum,
|
|
23668
23970
|
evaluateAntiPatterns,
|
|
23669
23971
|
findInvalidStatusMigrationTargets,
|
|
23972
|
+
formatUpgText,
|
|
23670
23973
|
generateSlug,
|
|
23671
23974
|
getAnchorEntity,
|
|
23672
23975
|
getAntiPatternById,
|
|
@@ -23709,6 +24012,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
23709
24012
|
getValidChildren,
|
|
23710
24013
|
getVisibleTypes,
|
|
23711
24014
|
hasStatusMigration,
|
|
24015
|
+
isCanonical,
|
|
23712
24016
|
isCanonicalProductStage,
|
|
23713
24017
|
isContainmentFreeType,
|
|
23714
24018
|
isDeprecatedType,
|
|
@@ -23719,6 +24023,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
23719
24023
|
isLifecycleFreeType,
|
|
23720
24024
|
isLifecyclePlannedType,
|
|
23721
24025
|
isPolymorphicEdge,
|
|
24026
|
+
isPortfolio,
|
|
23722
24027
|
isRegisteredPolymorphicEdge,
|
|
23723
24028
|
isSubSequenceStep,
|
|
23724
24029
|
isUPGDocument,
|
|
@@ -23728,6 +24033,8 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
23728
24033
|
migrateNodeProperties,
|
|
23729
24034
|
migrateProductStage,
|
|
23730
24035
|
migrateStatusValue,
|
|
24036
|
+
normalizeDocument,
|
|
24037
|
+
parseUpg,
|
|
23731
24038
|
pickCanonicalEdge,
|
|
23732
24039
|
resolveAllEdges,
|
|
23733
24040
|
resolveContainmentEdge,
|
|
@@ -23735,6 +24042,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
23735
24042
|
resolveLabel,
|
|
23736
24043
|
resolveSlugCollision,
|
|
23737
24044
|
rotateSlug,
|
|
24045
|
+
serializeCanonical,
|
|
23738
24046
|
validateProductStageStrict,
|
|
23739
24047
|
validateUPGDocument,
|
|
23740
24048
|
validateUPGFramework,
|