@unified-product-graph/core 0.8.1 → 0.8.4

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/dist/index.cjs CHANGED
@@ -104,6 +104,7 @@ __export(index_exports, {
104
104
  collectSlugsForType: () => collectSlugsForType,
105
105
  compareVersions: () => compareVersions,
106
106
  computeBodyChecksum: () => computeBodyChecksum,
107
+ edgeCarriesProperties: () => edgeCarriesProperties,
107
108
  evaluateAntiPatterns: () => evaluateAntiPatterns,
108
109
  findInvalidStatusMigrationTargets: () => findInvalidStatusMigrationTargets,
109
110
  formatUpgText: () => formatUpgText,
@@ -676,8 +677,8 @@ var UPG_DOMAINS = [
676
677
  {
677
678
  id: "workspace",
678
679
  label: "Workspace",
679
- description: "Spatial thinking spaces for arranging entities, debating decisions, and committing to the graph. Workspaces are transient canvases that sit alongside all other domains, letting you compose and explore relationships before they become permanent graph structure.",
680
- types: ["workspace"]
680
+ description: "Spatial thinking spaces for arranging entities, debating decisions, and committing to the graph. Workspaces are transient canvases that sit alongside all other domains, letting you compose and explore relationships before they become permanent graph structure. A framework exercise is a structured workspace: one run of a framework (MoSCoW, RICE, Kano, \u2026) applied to a chosen set of entities, with each entity's result recorded on the exercise-to-entity edge rather than the entity itself.",
681
+ types: ["workspace", "framework_exercise"]
681
682
  }
682
683
  ];
683
684
  var UPG_ENTITY_TO_DOMAIN = Object.freeze(
@@ -1103,7 +1104,8 @@ var UPG_ENTITY_META = [
1103
1104
  { name: "integration_partner", type_id: "ent_311", maturity: "stable", since: "0.1.0" },
1104
1105
  { name: "partner_revenue_share", type_id: "ent_312", maturity: "stable", since: "0.1.0" },
1105
1106
  // ── Workspace ──
1106
- { name: "workspace", type_id: "ent_328", maturity: "proposed", since: "0.2.0" }
1107
+ { name: "workspace", type_id: "ent_328", maturity: "proposed", since: "0.2.0" },
1108
+ { name: "framework_exercise", type_id: "ent_350", maturity: "proposed", since: "0.8.4" }
1107
1109
  ];
1108
1110
  var UPG_ENTITY_META_BY_NAME = new Map(
1109
1111
  UPG_ENTITY_META.map((m) => [m.name, m])
@@ -1136,7 +1138,7 @@ var UPG_EDGE_CATALOG = {
1136
1138
  // product → persona is the most fundamental relationship in the
1137
1139
  // user domain ("who is this product for?") and was the only way to
1138
1140
  // anchor a fresh persona to its product before this edge existed. Without
1139
- // it, the natural agent path through `/upg-persona` produced an orphan
1141
+ // it, the natural agent path through `/upg-new-persona` produced an orphan
1140
1142
  // persona only attached laterally via `ideal_customer_profile_maps_to_persona`
1141
1143
  // or `positioning_resonates_with_persona`. Semantic, not hierarchy: a
1142
1144
  // product doesn't "contain" personas; it targets them.
@@ -2256,6 +2258,18 @@ var UPG_EDGE_CATALOG = {
2256
2258
  // universal across the DDD type family. Verbs follow DDD literature:
2257
2259
  // aggregate belongs_to context, context contains aggregates.
2258
2260
  node_belongs_to_bounded_context: { forward_verb: "belongs_to", reverse_verb: "contains", classification: "cross-domain", source_type: "node", target_type: "bounded_context" },
2261
+ // ── Cross-domain: Framework exercises ────────────────────────────────────────
2262
+ // A framework_exercise is one run of a framework (MoSCoW, RICE, Kano, …) over a
2263
+ // set of entities. It `includes` each entity it touches; the framework's
2264
+ // per-entity result — a MoSCoW bucket, a RICE score, a canvas slot, a funnel
2265
+ // stage — rides on this edge's `properties`, NOT on the entity node. A value
2266
+ // that exists only within a specific exercise is a fact about the relationship,
2267
+ // not the entity, so it lives on the edge (same principle as owner-as-edge).
2268
+ // Polymorphic (`target_type: 'node'`): an exercise can include any entity type,
2269
+ // which closes the feature-only limitation structurally. `carries_properties`
2270
+ // opts this edge into the gated edge-property model. See ADR
2271
+ // 2026-06-02-framework-exercises.
2272
+ framework_exercise_includes_node: { forward_verb: "includes", reverse_verb: "included_in", classification: "cross-domain", source_type: "framework_exercise", target_type: "node", carries_properties: true },
2259
2273
  // ── New edges replacing deleted string properties ────────────────────
2260
2274
  // Marketing
2261
2275
  marketing_strategy_pursues_outcome: { forward_verb: "pursues", reverse_verb: "pursued_by", classification: "cross-domain", source_type: "marketing_strategy", target_type: "outcome" },
@@ -2351,6 +2365,13 @@ var UPG_EDGE_CATALOG = {
2351
2365
  // release strategy to the concrete deployments it governs.
2352
2366
  incident_affects_feature: { forward_verb: "affects", reverse_verb: "affected_by", classification: "cross-domain", source_type: "incident", target_type: "feature" },
2353
2367
  release_strategy_used_by_deployment: { forward_verb: "used_by", reverse_verb: "uses", classification: "cross-domain", source_type: "release_strategy", target_type: "deployment" },
2368
+ // v0.8.2 (UPG-615): ITIL/ITSM incident management explicitly links an incident
2369
+ // to the customer-facing support tickets/cases it spawns — when a service
2370
+ // breaks, customers raise tickets; the incident is the cause, the ticket the
2371
+ // effect. Closes the otherwise-unmediated "Customer Support" island in
2372
+ // `playbook:operations-quality`, binding `support_ticket` to the incident/ops
2373
+ // spine. Source: ITIL v4 incident management; DORA/SRE customer-facing impact.
2374
+ incident_generates_support_ticket: { forward_verb: "generates", reverse_verb: "generated_by", classification: "cross-domain", source_type: "incident", target_type: "support_ticket" },
2354
2375
  // Cluster C: User Research linkage matrix.
2355
2376
  // research_study → {participant, research_question, survey_response,
2356
2377
  // interview_guide} containment edges already exist (49/49 in Wave 4).
@@ -2878,7 +2899,9 @@ var UPG_POLYMORPHIC_EDGE_KEYS = [
2878
2899
  "node_owned_by_department",
2879
2900
  "node_owned_by_person",
2880
2901
  // Universal architecture references
2881
- "node_belongs_to_bounded_context"
2902
+ "node_belongs_to_bounded_context",
2903
+ // Framework exercises (an exercise can include any entity type)
2904
+ "framework_exercise_includes_node"
2882
2905
  ];
2883
2906
  var _POLY_KEY_SET = new Set(UPG_POLYMORPHIC_EDGE_KEYS);
2884
2907
  function isPolymorphicEdge(key) {
@@ -2888,6 +2911,10 @@ function isPolymorphicEdge(key) {
2888
2911
  function isRegisteredPolymorphicEdge(key) {
2889
2912
  return _POLY_KEY_SET.has(key);
2890
2913
  }
2914
+ function edgeCarriesProperties(type) {
2915
+ const def = UPG_EDGE_CATALOG[type];
2916
+ return def?.carries_properties === true;
2917
+ }
2891
2918
 
2892
2919
  // src/catalog/legacy-product-stages.ts
2893
2920
  var LEGACY_PRODUCT_STAGES = Object.freeze({
@@ -3539,7 +3566,13 @@ function canBeChildOf(childType, parentType) {
3539
3566
  return getValidChildren(parentType).includes(childType);
3540
3567
  }
3541
3568
  var UPG_CONTAINMENT_FREE_TYPES = /* @__PURE__ */ new Set([
3542
- "person"
3569
+ "person",
3570
+ // A framework_exercise has no structural parent: it anchors to the entities
3571
+ // it scores via the `framework_exercise_includes_node` edge, not by
3572
+ // containment. Modelling it as a product child would force a containment edge
3573
+ // and read against the whole point of the exercise model (relational, not
3574
+ // hierarchical). Same posture as `person`. See ADR 2026-06-02-framework-exercises.
3575
+ "framework_exercise"
3543
3576
  ]);
3544
3577
  function isContainmentFreeType(entityType) {
3545
3578
  return UPG_CONTAINMENT_FREE_TYPES.has(entityType);
@@ -5841,6 +5874,31 @@ var TEMPLATE_LIFECYCLES = [
5841
5874
  // ── Phase B: SALES_DEAL (qualified → proposal → negotiation → won/lost) ─────
5842
5875
  fromTemplate("deal", SALES_DEAL_TEMPLATE)
5843
5876
  ];
5877
+ var FRAMEWORK_EXERCISE_LIFECYCLE = {
5878
+ entity_type: "framework_exercise",
5879
+ initial_phase: "draft",
5880
+ terminal_phases: ["archived"],
5881
+ phases: [
5882
+ {
5883
+ id: "draft",
5884
+ label: "Draft",
5885
+ description: "The exercise has been created and entities pulled into scope, but the framework's inputs have not all been filled in yet.",
5886
+ transitions_to: ["active", "archived"]
5887
+ },
5888
+ {
5889
+ id: "active",
5890
+ label: "Active",
5891
+ description: "The current, authoritative run of its framework. Its include edges carry the live results consumers read, rank, and render by.",
5892
+ transitions_to: ["archived"]
5893
+ },
5894
+ {
5895
+ id: "archived",
5896
+ label: "Archived",
5897
+ description: "A past run, retained for provenance. Still queryable but superseded by a newer exercise and hidden from default views. Can be revived to active.",
5898
+ transitions_to: ["active"]
5899
+ }
5900
+ ]
5901
+ };
5844
5902
  var UPG_LIFECYCLES = [
5845
5903
  // Product (root)
5846
5904
  PRODUCT_LIFECYCLE,
@@ -5942,6 +6000,8 @@ var UPG_LIFECYCLES = [
5942
6000
  // Product & Growth
5943
6001
  VARIANT_LIFECYCLE,
5944
6002
  GROWTH_CAMPAIGN_LIFECYCLE,
6003
+ // Workspace
6004
+ FRAMEWORK_EXERCISE_LIFECYCLE,
5945
6005
  // ── Template-generated lifecycles ─────────────────────────────────
5946
6006
  ...TEMPLATE_LIFECYCLES
5947
6007
  ];
@@ -10131,6 +10191,11 @@ var UPG_PROPERTY_SCHEMA = {
10131
10191
  },
10132
10192
  methodology: { type: "string", description: "Forecasting methodology used" }
10133
10193
  },
10194
+ // FrameworkExerciseProperties: Framework exercise: one run of a framework over a set of entities.
10195
+ framework_exercise: {
10196
+ framework_id: { type: "string", description: "Which framework this exercise runs: a framework id (e.g. 'moscow', 'rice-scoring', 'kano-model'). Resolves against the framework catalog." },
10197
+ inputs_snapshot: { type: "object", description: "Optional frozen copy of the framework's input spec at apply time, so a historical exercise still renders correctly if the framework definition later evolves (inputs added, removed, or rescaled)." }
10198
+ },
10134
10199
  // FunnelProperties: Funnel entity.
10135
10200
  funnel: {
10136
10201
  funnel_type: { type: "string", enum: ["acquisition", "activation", "retention", "revenue", "referral", "custom"], description: "Which stage of the customer lifecycle this funnel measures" },
@@ -12158,6 +12223,9 @@ var UPG_FRAMEWORKS = [
12158
12223
  },
12159
12224
  {
12160
12225
  "id": "value-proposition-canvas",
12226
+ "approach_ids": [
12227
+ "trace"
12228
+ ],
12161
12229
  "name": "Value Proposition Canvas",
12162
12230
  "version": "1.0.0",
12163
12231
  "description": "Map customer jobs, pains, and gains on one side, then align product features, pain relievers, and gain creators on the other to achieve product-market fit.",
@@ -12262,6 +12330,9 @@ var UPG_FRAMEWORKS = [
12262
12330
  },
12263
12331
  {
12264
12332
  "id": "persona-canvas",
12333
+ "approach_ids": [
12334
+ "trace"
12335
+ ],
12265
12336
  "name": "Persona Canvas",
12266
12337
  "version": "1.0.0",
12267
12338
  "description": "Demographics, goals, frustrations, JTBD: a structured template for creating research-backed personas.",
@@ -12374,6 +12445,9 @@ var UPG_FRAMEWORKS = [
12374
12445
  },
12375
12446
  {
12376
12447
  "id": "empathy-map",
12448
+ "approach_ids": [
12449
+ "trace"
12450
+ ],
12377
12451
  "name": "Empathy Map",
12378
12452
  "version": "1.0.0",
12379
12453
  "description": "Visualise what a user says, thinks, does, and feels to build deeper empathy and uncover hidden needs.",
@@ -12534,6 +12608,7 @@ var UPG_FRAMEWORKS = [
12534
12608
  "property": "evolution_stage",
12535
12609
  "type": "enum",
12536
12610
  "required": true,
12611
+ "scope": "framework",
12537
12612
  "label": "Evolution Stage",
12538
12613
  "description": "Where this component sits on the evolution axis",
12539
12614
  "enum_values": [
@@ -12547,6 +12622,7 @@ var UPG_FRAMEWORKS = [
12547
12622
  "property": "visibility",
12548
12623
  "type": "number",
12549
12624
  "required": true,
12625
+ "scope": "framework",
12550
12626
  "label": "Visibility",
12551
12627
  "description": "Y-axis position (0=infrastructure, 1=anchor/user)"
12552
12628
  }
@@ -12556,6 +12632,7 @@ var UPG_FRAMEWORKS = [
12556
12632
  "property": "evolution_stage",
12557
12633
  "type": "enum",
12558
12634
  "required": true,
12635
+ "scope": "framework",
12559
12636
  "label": "Evolution Stage",
12560
12637
  "description": "Where this component sits on the evolution axis",
12561
12638
  "enum_values": [
@@ -12569,6 +12646,7 @@ var UPG_FRAMEWORKS = [
12569
12646
  "property": "visibility",
12570
12647
  "type": "number",
12571
12648
  "required": true,
12649
+ "scope": "framework",
12572
12650
  "label": "Visibility",
12573
12651
  "description": "Y-axis position (0=infrastructure, 1=anchor/user)"
12574
12652
  }
@@ -12578,6 +12656,7 @@ var UPG_FRAMEWORKS = [
12578
12656
  "property": "evolution_stage",
12579
12657
  "type": "enum",
12580
12658
  "required": true,
12659
+ "scope": "framework",
12581
12660
  "label": "Evolution Stage",
12582
12661
  "description": "Where this component sits on the evolution axis",
12583
12662
  "enum_values": [
@@ -12591,6 +12670,7 @@ var UPG_FRAMEWORKS = [
12591
12670
  "property": "visibility",
12592
12671
  "type": "number",
12593
12672
  "required": true,
12673
+ "scope": "framework",
12594
12674
  "label": "Visibility",
12595
12675
  "description": "Y-axis position (0=infrastructure, 1=anchor/user)"
12596
12676
  }
@@ -12600,6 +12680,7 @@ var UPG_FRAMEWORKS = [
12600
12680
  "property": "evolution_stage",
12601
12681
  "type": "enum",
12602
12682
  "required": true,
12683
+ "scope": "framework",
12603
12684
  "label": "Evolution Stage",
12604
12685
  "description": 'Where this component sits on the evolution axis (need anchor is usually at "product" or "commodity")',
12605
12686
  "enum_values": [
@@ -12613,6 +12694,7 @@ var UPG_FRAMEWORKS = [
12613
12694
  "property": "visibility",
12614
12695
  "type": "number",
12615
12696
  "required": true,
12697
+ "scope": "framework",
12616
12698
  "label": "Visibility",
12617
12699
  "description": "Y-axis position (0=infrastructure, 1=anchor/user); needs sit at 1.0"
12618
12700
  }
@@ -12653,6 +12735,9 @@ var UPG_FRAMEWORKS = [
12653
12735
  },
12654
12736
  {
12655
12737
  "id": "business-model-canvas",
12738
+ "approach_ids": [
12739
+ "plan"
12740
+ ],
12656
12741
  "name": "Business Model Canvas",
12657
12742
  "version": "1.0.0",
12658
12743
  "description": "Nine building blocks that describe how an organisation creates, delivers, and captures value.",
@@ -12792,6 +12877,9 @@ var UPG_FRAMEWORKS = [
12792
12877
  },
12793
12878
  {
12794
12879
  "id": "porter-five-forces",
12880
+ "approach_ids": [
12881
+ "inspect"
12882
+ ],
12795
12883
  "name": "Porter Five Forces",
12796
12884
  "version": "1.0.0",
12797
12885
  "description": "Analyse industry competitiveness through five forces: rivalry, new entrants, substitutes, buyer power, and supplier power.",
@@ -12881,6 +12969,9 @@ var UPG_FRAMEWORKS = [
12881
12969
  },
12882
12970
  {
12883
12971
  "id": "swot-analysis",
12972
+ "approach_ids": [
12973
+ "inspect"
12974
+ ],
12884
12975
  "name": "SWOT Analysis",
12885
12976
  "version": "1.0.0",
12886
12977
  "description": "Map Strengths, Weaknesses, Opportunities, and Threats in a 2x2 grid. Internal vs external, helpful vs harmful.",
@@ -13207,6 +13298,7 @@ var UPG_FRAMEWORKS = [
13207
13298
  "type": "assessment",
13208
13299
  "scale_id": "reach_5",
13209
13300
  "required": true,
13301
+ "scope": "framework",
13210
13302
  "label": "Reach",
13211
13303
  "description": "How many users will this impact per quarter?"
13212
13304
  },
@@ -13215,6 +13307,7 @@ var UPG_FRAMEWORKS = [
13215
13307
  "type": "assessment",
13216
13308
  "scale_id": "impact_5",
13217
13309
  "required": true,
13310
+ "scope": "framework",
13218
13311
  "label": "Impact",
13219
13312
  "description": "How much will this impact each user, on the impact scale?"
13220
13313
  },
@@ -13223,6 +13316,7 @@ var UPG_FRAMEWORKS = [
13223
13316
  "type": "assessment",
13224
13317
  "scale_id": "confidence_5",
13225
13318
  "required": true,
13319
+ "scope": "framework",
13226
13320
  "label": "Confidence",
13227
13321
  "description": "How confident are you in the reach, impact, and effort estimates?"
13228
13322
  },
@@ -13231,6 +13325,7 @@ var UPG_FRAMEWORKS = [
13231
13325
  "type": "assessment",
13232
13326
  "scale_id": "effort_5",
13233
13327
  "required": true,
13328
+ "scope": "framework",
13234
13329
  "label": "Effort",
13235
13330
  "description": "How much work is required to build and ship this, on the effort scale?"
13236
13331
  }
@@ -13317,6 +13412,9 @@ var UPG_FRAMEWORKS = [
13317
13412
  },
13318
13413
  {
13319
13414
  "id": "build-measure-learn",
13415
+ "approach_ids": [
13416
+ "reflect"
13417
+ ],
13320
13418
  "name": "Build-Measure-Learn",
13321
13419
  "version": "1.0.0",
13322
13420
  "description": "The core Lean Startup feedback loop: build a minimum viable product, measure its impact with actionable metrics, and learn whether to pivot or persevere.",
@@ -13452,6 +13550,7 @@ var UPG_FRAMEWORKS = [
13452
13550
  "property": "functional_response",
13453
13551
  "type": "enum",
13454
13552
  "required": true,
13553
+ "scope": "framework",
13455
13554
  "label": "Functional Response",
13456
13555
  "description": "How users feel when the feature IS present",
13457
13556
  "enum_values": [
@@ -13466,6 +13565,7 @@ var UPG_FRAMEWORKS = [
13466
13565
  "property": "dysfunctional_response",
13467
13566
  "type": "enum",
13468
13567
  "required": true,
13568
+ "scope": "framework",
13469
13569
  "label": "Dysfunctional Response",
13470
13570
  "description": "How users feel when the feature IS NOT present",
13471
13571
  "enum_values": [
@@ -13480,6 +13580,7 @@ var UPG_FRAMEWORKS = [
13480
13580
  "property": "delighter_count",
13481
13581
  "type": "number",
13482
13582
  "required": false,
13583
+ "scope": "framework",
13483
13584
  "label": "Delighter classifications",
13484
13585
  "description": "Count of survey responses classifying this feature as a delighter (attractive)"
13485
13586
  },
@@ -13487,6 +13588,7 @@ var UPG_FRAMEWORKS = [
13487
13588
  "property": "performance_count",
13488
13589
  "type": "number",
13489
13590
  "required": false,
13591
+ "scope": "framework",
13490
13592
  "label": "Performance classifications",
13491
13593
  "description": "Count of survey responses classifying this feature as performance (one-dimensional)"
13492
13594
  },
@@ -13494,6 +13596,7 @@ var UPG_FRAMEWORKS = [
13494
13596
  "property": "must_be_count",
13495
13597
  "type": "number",
13496
13598
  "required": false,
13599
+ "scope": "framework",
13497
13600
  "label": "Must-be classifications",
13498
13601
  "description": "Count of survey responses classifying this feature as must-be (basic)"
13499
13602
  },
@@ -13501,6 +13604,7 @@ var UPG_FRAMEWORKS = [
13501
13604
  "property": "indifferent_count",
13502
13605
  "type": "number",
13503
13606
  "required": false,
13607
+ "scope": "framework",
13504
13608
  "label": "Indifferent classifications",
13505
13609
  "description": "Count of survey responses classifying this feature as indifferent"
13506
13610
  }
@@ -13693,6 +13797,7 @@ var UPG_FRAMEWORKS = [
13693
13797
  "property": "moscow",
13694
13798
  "type": "enum",
13695
13799
  "required": true,
13800
+ "scope": "framework",
13696
13801
  "label": "MoSCoW priority",
13697
13802
  "description": "Which scope bucket this requirement falls into for the current release",
13698
13803
  "enum_values": [
@@ -13703,16 +13808,7 @@ var UPG_FRAMEWORKS = [
13703
13808
  ]
13704
13809
  }
13705
13810
  ]
13706
- },
13707
- "computed_properties": [
13708
- {
13709
- "property": "must_have_ratio",
13710
- "expression": "must_count / total_count",
13711
- "entity_type": "feature",
13712
- "label": "Must-have Ratio",
13713
- "format": "percentage"
13714
- }
13715
- ]
13811
+ }
13716
13812
  },
13717
13813
  "structure": {
13718
13814
  "pattern": "table"
@@ -13876,6 +13972,9 @@ var UPG_FRAMEWORKS = [
13876
13972
  },
13877
13973
  {
13878
13974
  "id": "c4-model",
13975
+ "approach_ids": [
13976
+ "trace"
13977
+ ],
13879
13978
  "name": "C4 Model",
13880
13979
  "version": "1.0.0",
13881
13980
  "description": "Visualise software architecture at four levels of abstraction: System Context, Container, Component, and Code. Each level zooms in to reveal more detail.",
@@ -13968,6 +14067,9 @@ var UPG_FRAMEWORKS = [
13968
14067
  },
13969
14068
  {
13970
14069
  "id": "adr-log",
14070
+ "approach_ids": [
14071
+ "inspect"
14072
+ ],
13971
14073
  "name": "ADR Log",
13972
14074
  "version": "1.0.0",
13973
14075
  "description": "Architecture Decision Records: log decisions with context, options, and rationale.",
@@ -14081,6 +14183,9 @@ var UPG_FRAMEWORKS = [
14081
14183
  },
14082
14184
  {
14083
14185
  "id": "atomic-design",
14186
+ "approach_ids": [
14187
+ "trace"
14188
+ ],
14084
14189
  "name": "Atomic Design",
14085
14190
  "version": "1.0.0",
14086
14191
  "description": "Atoms, Molecules, Organisms, Templates, Pages: a methodology for creating design systems from the smallest elements up.",
@@ -14178,6 +14283,9 @@ var UPG_FRAMEWORKS = [
14178
14283
  },
14179
14284
  {
14180
14285
  "id": "double-diamond",
14286
+ "approach_ids": [
14287
+ "plan"
14288
+ ],
14181
14289
  "name": "Double Diamond",
14182
14290
  "version": "1.0.0",
14183
14291
  "description": "Discover, Define, Develop, Deliver: a four-phase divergent/convergent design process.",
@@ -14268,6 +14376,9 @@ var UPG_FRAMEWORKS = [
14268
14376
  },
14269
14377
  {
14270
14378
  "id": "dora-metrics",
14379
+ "approach_ids": [
14380
+ "inspect"
14381
+ ],
14271
14382
  "name": "DORA Metrics",
14272
14383
  "version": "1.0.0",
14273
14384
  "description": "Four key metrics for software delivery performance: deployment frequency, lead time, change failure rate, and time to restore.",
@@ -14327,16 +14438,7 @@ var UPG_FRAMEWORKS = [
14327
14438
  ]
14328
14439
  }
14329
14440
  ]
14330
- },
14331
- "computed_properties": [
14332
- {
14333
- "property": "stability_index",
14334
- "expression": "change_failure_rate * mean_time_to_recovery",
14335
- "entity_type": "metric",
14336
- "label": "Stability Index",
14337
- "format": "number"
14338
- }
14339
- ]
14441
+ }
14340
14442
  },
14341
14443
  "structure": {
14342
14444
  "pattern": "collection"
@@ -14465,6 +14567,9 @@ var UPG_FRAMEWORKS = [
14465
14567
  },
14466
14568
  {
14467
14569
  "id": "pirate-metrics-aarrr",
14570
+ "approach_ids": [
14571
+ "trace"
14572
+ ],
14468
14573
  "name": "Pirate Metrics AARRR",
14469
14574
  "version": "1.0.0",
14470
14575
  "description": "Track user lifecycle across five stages: Acquisition, Activation, Retention, Revenue, and Referral.",
@@ -14498,7 +14603,7 @@ var UPG_FRAMEWORKS = [
14498
14603
  "required_properties": {
14499
14604
  "metric": [
14500
14605
  {
14501
- "property": "lifecycle_stage",
14606
+ "property": "metric_category",
14502
14607
  "type": "enum",
14503
14608
  "required": true,
14504
14609
  "label": "Lifecycle stage",
@@ -14512,16 +14617,7 @@ var UPG_FRAMEWORKS = [
14512
14617
  ]
14513
14618
  }
14514
14619
  ]
14515
- },
14516
- "computed_properties": [
14517
- {
14518
- "property": "conversion_rate",
14519
- "expression": "(stage_exits / stage_entries) * 100",
14520
- "entity_type": "metric",
14521
- "label": "Stage Conversion",
14522
- "format": "percentage"
14523
- }
14524
- ]
14620
+ }
14525
14621
  },
14526
14622
  "structure": {
14527
14623
  "pattern": "funnel",
@@ -14585,6 +14681,9 @@ var UPG_FRAMEWORKS = [
14585
14681
  },
14586
14682
  {
14587
14683
  "id": "north-star-metric",
14684
+ "approach_ids": [
14685
+ "plan"
14686
+ ],
14588
14687
  "name": "North Star Metric",
14589
14688
  "version": "1.0.0",
14590
14689
  "description": "One metric that best captures the core value you deliver. Supported by 3-5 input metrics that drive it.",
@@ -14618,7 +14717,7 @@ var UPG_FRAMEWORKS = [
14618
14717
  "required_properties": {
14619
14718
  "metric": [
14620
14719
  {
14621
- "property": "metric_role",
14720
+ "property": "designation",
14622
14721
  "type": "enum",
14623
14722
  "required": true,
14624
14723
  "label": "Metric role",
@@ -14636,16 +14735,7 @@ var UPG_FRAMEWORKS = [
14636
14735
  "description": "How strongly this input metric moves the North Star (input metrics only)"
14637
14736
  }
14638
14737
  ]
14639
- },
14640
- "computed_properties": [
14641
- {
14642
- "property": "nsm_impact",
14643
- "expression": "input_metric_value * leverage",
14644
- "entity_type": "metric",
14645
- "label": "NSM Impact",
14646
- "format": "number"
14647
- }
14648
- ]
14738
+ }
14649
14739
  },
14650
14740
  "structure": {
14651
14741
  "pattern": "collection"
@@ -14681,6 +14771,9 @@ var UPG_FRAMEWORKS = [
14681
14771
  },
14682
14772
  {
14683
14773
  "id": "marketing-mix-4ps",
14774
+ "approach_ids": [
14775
+ "plan"
14776
+ ],
14684
14777
  "name": "Marketing Mix 4Ps",
14685
14778
  "version": "1.0.0",
14686
14779
  "description": "The foundational marketing framework. Every marketing strategy must address four decisions: what to sell (Product), what to charge (Price), where to sell (Place), and how to promote (Promotion).",
@@ -14774,6 +14867,9 @@ var UPG_FRAMEWORKS = [
14774
14867
  },
14775
14868
  {
14776
14869
  "id": "bullseye-framework",
14870
+ "approach_ids": [
14871
+ "plan"
14872
+ ],
14777
14873
  "name": "Bullseye Framework",
14778
14874
  "version": "1.0.0",
14779
14875
  "description": "Test 19 traction channels systematically. Start with the outer ring (what's possible), narrow to the middle ring (what's probable), then focus on the inner ring (what's working). Run cheap tests across all channels to find your bullseye.",
@@ -14860,6 +14956,9 @@ var UPG_FRAMEWORKS = [
14860
14956
  },
14861
14957
  {
14862
14958
  "id": "product-led-growth-framework",
14959
+ "approach_ids": [
14960
+ "plan"
14961
+ ],
14863
14962
  "name": "PLG Framework",
14864
14963
  "version": "1.0.0",
14865
14964
  "description": "Product-led go-to-market motion. Free entry gives users access, the aha moment hooks them, they expand usage within their team, and monetisation captures value from power users.",
@@ -15042,6 +15141,9 @@ var UPG_FRAMEWORKS = [
15042
15141
  },
15043
15142
  {
15044
15143
  "id": "raci-matrix",
15144
+ "approach_ids": [
15145
+ "inspect"
15146
+ ],
15045
15147
  "name": "RACI Matrix",
15046
15148
  "version": "1.0.0",
15047
15149
  "description": "Assign roles: Responsible, Accountable, Consulted, Informed for each activity.",
@@ -15093,16 +15195,7 @@ var UPG_FRAMEWORKS = [
15093
15195
  "role": "item"
15094
15196
  }
15095
15197
  ],
15096
- "required_properties": {},
15097
- "computed_properties": [
15098
- {
15099
- "property": "coverage_score",
15100
- "expression": "assigned_count / total_activities",
15101
- "entity_type": "team",
15102
- "label": "RACI Coverage",
15103
- "format": "percentage"
15104
- }
15105
- ]
15198
+ "required_properties": {}
15106
15199
  },
15107
15200
  "structure": {
15108
15201
  "pattern": "matrix"
@@ -15326,6 +15419,9 @@ var UPG_FRAMEWORKS = [
15326
15419
  },
15327
15420
  {
15328
15421
  "id": "team-health-check",
15422
+ "approach_ids": [
15423
+ "inspect"
15424
+ ],
15329
15425
  "name": "Team Health Check",
15330
15426
  "version": "1.0.0",
15331
15427
  "description": "A facilitated team self-assessment across dimensions like mission, fun, learning, speed, and support, using traffic-light voting to surface strengths and improvement areas in a safe format.",
@@ -15383,16 +15479,7 @@ var UPG_FRAMEWORKS = [
15383
15479
  "role": "item"
15384
15480
  }
15385
15481
  ],
15386
- "required_properties": {},
15387
- "computed_properties": [
15388
- {
15389
- "property": "health_index",
15390
- "expression": "(green_count - red_count) / total_indicators",
15391
- "entity_type": "team",
15392
- "label": "Health Index",
15393
- "format": "number"
15394
- }
15395
- ]
15482
+ "required_properties": {}
15396
15483
  },
15397
15484
  "structure": {
15398
15485
  "pattern": "table"
@@ -15449,6 +15536,9 @@ var UPG_FRAMEWORKS = [
15449
15536
  },
15450
15537
  {
15451
15538
  "id": "raid-log",
15539
+ "approach_ids": [
15540
+ "inspect"
15541
+ ],
15452
15542
  "name": "RAID Log",
15453
15543
  "version": "1.0.0",
15454
15544
  "description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies, the four categories most likely to derail a project if left unmanaged.",
@@ -15468,6 +15558,11 @@ var UPG_FRAMEWORKS = [
15468
15558
  "entityTypeId": "risk_register",
15469
15559
  "description": "Risk or assumption logged with likelihood, impact, owner, and mitigation strategy"
15470
15560
  },
15561
+ {
15562
+ "label": "Risk",
15563
+ "entityTypeId": "risk",
15564
+ "description": "Individual risk scored by probability and impact; severity = probability * impact"
15565
+ },
15471
15566
  {
15472
15567
  "label": "Dependency",
15473
15568
  "entityTypeId": "dependency",
@@ -15490,6 +15585,10 @@ var UPG_FRAMEWORKS = [
15490
15585
  "type": "risk_register",
15491
15586
  "role": "item"
15492
15587
  },
15588
+ {
15589
+ "type": "risk",
15590
+ "role": "scored_item"
15591
+ },
15493
15592
  {
15494
15593
  "type": "dependency",
15495
15594
  "role": "item"
@@ -15503,7 +15602,24 @@ var UPG_FRAMEWORKS = [
15503
15602
  "role": "item"
15504
15603
  }
15505
15604
  ],
15506
- "required_properties": {},
15605
+ "required_properties": {
15606
+ "risk": [
15607
+ {
15608
+ "property": "probability",
15609
+ "type": "number",
15610
+ "required": true,
15611
+ "label": "Probability",
15612
+ "description": "Likelihood the risk materialises (risk.probability assessment)"
15613
+ },
15614
+ {
15615
+ "property": "impact",
15616
+ "type": "number",
15617
+ "required": true,
15618
+ "label": "Impact",
15619
+ "description": "Consequence severity if the risk materialises (risk.impact assessment)"
15620
+ }
15621
+ ]
15622
+ },
15507
15623
  "computed_properties": [
15508
15624
  {
15509
15625
  "property": "severity",
@@ -15564,126 +15680,1028 @@ var UPG_FRAMEWORKS = [
15564
15680
  "Formal programme management would create overhead without value"
15565
15681
  ]
15566
15682
  }
15567
- }
15568
- ];
15569
- var UPG_FRAMEWORKS_BY_ID = Object.fromEntries(
15570
- UPG_FRAMEWORKS.map((fw) => [fw.id, fw])
15571
- );
15572
- var UPG_FRAMEWORKS_BY_CATEGORY = {};
15573
- for (const fw of UPG_FRAMEWORKS) {
15574
- if (!UPG_FRAMEWORKS_BY_CATEGORY[fw.category]) UPG_FRAMEWORKS_BY_CATEGORY[fw.category] = [];
15575
- UPG_FRAMEWORKS_BY_CATEGORY[fw.category].push(fw);
15576
- }
15577
-
15578
- // src/presentation/labels.ts
15579
- var PRIORITY_LABELS = [
15580
- // ── need (CONSOLIDATED: replaces pain_point + user_need) ─────────────────────
15581
- {
15582
- id: "need",
15583
- canonical_label: "Need",
15584
- alt_labels: [
15585
- "pain point",
15586
- "pain",
15587
- "user need",
15588
- "customer need",
15589
- "problem",
15590
- "struggle",
15591
- "customer pain",
15592
- "frustration",
15593
- "gap",
15594
- "unmet need",
15595
- "user problem"
15596
- ],
15597
- framework_labels: {
15598
- lean_canvas: "Problem",
15599
- design_thinking: "Pain Point",
15600
- ost: "Opportunity (need)",
15601
- jtbd: "Struggle",
15602
- vpc: "Customer Pain"
15603
- },
15604
- designations: {
15605
- pain: "Pain Point",
15606
- gap: "Need",
15607
- desire: "Desire",
15608
- constraint: "Constraint"
15609
- }
15610
- },
15611
- // ── opportunity ──────────────────────────────────────────────────────────────
15612
- {
15613
- id: "opportunity",
15614
- canonical_label: "Opportunity",
15615
- alt_labels: ["product opportunity", "market opportunity", "user opportunity"],
15616
- framework_labels: {
15617
- ost: "Opportunity"
15618
- }
15619
- },
15620
- // ── solution ─────────────────────────────────────────────────────────────────
15621
- {
15622
- id: "solution",
15623
- canonical_label: "Solution",
15624
- alt_labels: ["proposed solution", "solution idea", "concept", "approach"],
15625
- framework_labels: {
15626
- ost: "Solution",
15627
- design_thinking: "Solution",
15628
- lean_canvas: "Solution",
15629
- rice: "Scored Solution"
15630
- }
15631
15683
  },
15632
- // ── experiment (CONSOLIDATED: absorbs ab_test, growth_experiment, pricing_experiment) ──
15633
15684
  {
15634
- id: "experiment",
15635
- canonical_label: "Experiment",
15636
- alt_labels: [
15637
- "test",
15638
- "validation",
15639
- "ab test",
15640
- "a/b test",
15641
- "split test",
15642
- "growth experiment",
15643
- "pricing experiment",
15644
- "usability test",
15645
- "discovery experiment"
15685
+ "id": "ice-scoring",
15686
+ "approach_ids": [
15687
+ "prioritise"
15646
15688
  ],
15647
- framework_labels: {
15648
- ost: "Experiment",
15649
- design_thinking: "Test",
15650
- lean_startup: "Experiment"
15689
+ "name": "ICE Scoring",
15690
+ "version": "1.0.0",
15691
+ "description": "Rate ideas by Impact, Confidence, and Ease on a 1-10 scale. Multiply for a composite score. Fast and lightweight.",
15692
+ "category": "prioritization",
15693
+ "origin": {
15694
+ "type": "practitioner",
15695
+ "attribution": "Sean Ellis",
15696
+ "description": "Created by Sean Ellis as a lightweight growth experiment scoring method. Widely adopted in growth teams.",
15697
+ "year": 2010,
15698
+ "license": "open_attribution"
15651
15699
  },
15652
- designations: {
15653
- discovery: "Discovery Experiment",
15654
- ab_test: "A/B Test",
15655
- growth: "Growth Experiment",
15656
- pricing: "Pricing Experiment",
15657
- usability: "Usability Test"
15658
- }
15659
- },
15660
- // ── hypothesis ───────────────────────────────────────────────────────────────
15661
- {
15662
- id: "hypothesis",
15663
- canonical_label: "Hypothesis",
15664
- alt_labels: ["bet", "testable assumption", "leap of faith"],
15665
- framework_labels: {
15666
- lean_startup: "Hypothesis",
15667
- running_lean: "Riskiest Assumption",
15668
- lean_canvas: "Riskiest Assumption"
15669
- }
15670
- },
15671
- // ── metric (CONSOLIDATED: absorbs kpi, north_star_metric, input_metric, metric_definition) ──
15672
- {
15673
- id: "metric",
15674
- canonical_label: "Metric",
15675
- alt_labels: [
15676
- "kpi",
15677
- "key performance indicator",
15678
- "north star metric",
15679
- "nsm",
15680
- "input metric",
15681
- "output metric",
15682
- "metric definition",
15683
- "measure",
15684
- "indicator",
15685
- "signal",
15686
- "counter metric",
15700
+ "tags": [
15701
+ "prioritization",
15702
+ "table"
15703
+ ],
15704
+ "slots": [
15705
+ {
15706
+ "label": "Items to score",
15707
+ "entityTypeId": "feature",
15708
+ "description": "Features or experiments being evaluated"
15709
+ },
15710
+ {
15711
+ "label": "Impact",
15712
+ "entityTypeId": "outcome",
15713
+ "description": "How much will this move the needle?"
15714
+ },
15715
+ {
15716
+ "label": "Confidence",
15717
+ "entityTypeId": "assumption",
15718
+ "description": "How sure are we about the impact?"
15719
+ },
15720
+ {
15721
+ "label": "Ease",
15722
+ "entityTypeId": "feature",
15723
+ "description": "How easy is this to implement?"
15724
+ }
15725
+ ],
15726
+ "data": {
15727
+ "entity_types": [
15728
+ {
15729
+ "type": "feature",
15730
+ "role": "scored_item"
15731
+ },
15732
+ {
15733
+ "type": "outcome",
15734
+ "role": "item"
15735
+ },
15736
+ {
15737
+ "type": "assumption",
15738
+ "role": "item"
15739
+ }
15740
+ ],
15741
+ "required_properties": {
15742
+ "feature": [
15743
+ {
15744
+ "property": "impact",
15745
+ "type": "number",
15746
+ "required": true,
15747
+ "scope": "framework",
15748
+ "label": "Impact",
15749
+ "description": "Expected impact on the target metric (1-10)"
15750
+ },
15751
+ {
15752
+ "property": "confidence",
15753
+ "type": "number",
15754
+ "required": true,
15755
+ "scope": "framework",
15756
+ "label": "Confidence",
15757
+ "description": "Confidence in the impact estimate (1-10)"
15758
+ },
15759
+ {
15760
+ "property": "ease",
15761
+ "type": "number",
15762
+ "required": true,
15763
+ "scope": "framework",
15764
+ "label": "Ease",
15765
+ "description": "Ease of implementation (1-10)"
15766
+ }
15767
+ ]
15768
+ },
15769
+ "computed_properties": [
15770
+ {
15771
+ "property": "ice_score",
15772
+ "expression": "impact * confidence * ease",
15773
+ "entity_type": "feature",
15774
+ "label": "ICE Score",
15775
+ "format": "number"
15776
+ }
15777
+ ]
15778
+ },
15779
+ "structure": {
15780
+ "pattern": "table"
15781
+ },
15782
+ "presentation": {
15783
+ "layout": {
15784
+ "type": "table",
15785
+ "columns": [
15786
+ {
15787
+ "property": "title",
15788
+ "label": "Items to score",
15789
+ "sortable": true
15790
+ },
15791
+ {
15792
+ "property": "impact",
15793
+ "label": "Impact",
15794
+ "sortable": true
15795
+ },
15796
+ {
15797
+ "property": "confidence",
15798
+ "label": "Confidence",
15799
+ "sortable": true
15800
+ },
15801
+ {
15802
+ "property": "ease",
15803
+ "label": "Ease",
15804
+ "sortable": true
15805
+ }
15806
+ ]
15807
+ },
15808
+ "sort_by": {
15809
+ "property": "title",
15810
+ "direction": "asc"
15811
+ },
15812
+ "colour_by": "type",
15813
+ "card_fields": [
15814
+ "title",
15815
+ "description",
15816
+ "status"
15817
+ ]
15818
+ },
15819
+ "education": {
15820
+ "purpose": "Provide a lightweight scoring model for early-stage ideas when detailed effort estimates are unavailable. Faster than RICE and useful for brainstorm triage.",
15821
+ "core_question": "Which ideas should we investigate further based on their potential impact, confidence in our assumptions, and implementation ease?",
15822
+ "when_to_use": [
15823
+ "You have more ideas or features than capacity to build them",
15824
+ "Stakeholders disagree on what to build next",
15825
+ "You need a transparent, defensible prioritisation process"
15826
+ ],
15827
+ "when_not_to_use": [
15828
+ "You have a single obvious next step with no contention",
15829
+ "The backlog is small enough to sequence intuitively"
15830
+ ]
15831
+ }
15832
+ },
15833
+ {
15834
+ "id": "wsjf",
15835
+ "approach_ids": [
15836
+ "prioritise"
15837
+ ],
15838
+ "name": "WSJF (Weighted Shortest Job First)",
15839
+ "version": "1.0.0",
15840
+ "description": "Prioritise work by dividing Cost of Delay (user value + time criticality + risk reduction) by job duration to maximise economic throughput.",
15841
+ "category": "planning",
15842
+ "origin": {
15843
+ "type": "practitioner",
15844
+ "attribution": "Reinertsen / SAFe",
15845
+ "description": "Developed by Don Reinertsen and adopted as a core practice in the Scaled Agile Framework (SAFe). Combines urgency (Cost of Delay) with job size to produce an economic prioritisation sequence.",
15846
+ "url": "https://www.scaledagileframework.com/wsjf/",
15847
+ "year": 2011,
15848
+ "license": "open_attribution"
15849
+ },
15850
+ "tags": [
15851
+ "planning",
15852
+ "table"
15853
+ ],
15854
+ "slots": [
15855
+ {
15856
+ "label": "Backlog Items",
15857
+ "entityTypeId": "feature",
15858
+ "description": "Backlog Items: feature entries to evaluate"
15859
+ },
15860
+ {
15861
+ "label": "User/Business Value",
15862
+ "entityTypeId": "metric",
15863
+ "description": "User/Business Value: metric entries to evaluate"
15864
+ },
15865
+ {
15866
+ "label": "Time Criticality",
15867
+ "entityTypeId": "metric",
15868
+ "description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
15869
+ },
15870
+ {
15871
+ "label": "Risk Reduction / Opportunity Enablement",
15872
+ "entityTypeId": "metric",
15873
+ "description": "Risk Reduction / Opportunity Enablement: metric entries to evaluate"
15874
+ },
15875
+ {
15876
+ "label": "Job Size",
15877
+ "entityTypeId": "metric",
15878
+ "description": "Estimated effort (story points, t-shirt size, or person-weeks)"
15879
+ }
15880
+ ],
15881
+ "data": {
15882
+ "entity_types": [
15883
+ {
15884
+ "type": "feature",
15885
+ "role": "scored_item"
15886
+ },
15887
+ {
15888
+ "type": "metric",
15889
+ "role": "item"
15890
+ }
15891
+ ],
15892
+ "required_properties": {
15893
+ "feature": [
15894
+ {
15895
+ "property": "user_value",
15896
+ "type": "number",
15897
+ "required": true,
15898
+ "scope": "framework",
15899
+ "label": "User/Business Value",
15900
+ "description": "Relative value to users and the business if delivered"
15901
+ },
15902
+ {
15903
+ "property": "time_criticality",
15904
+ "type": "number",
15905
+ "required": true,
15906
+ "scope": "framework",
15907
+ "label": "Time Criticality",
15908
+ "description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
15909
+ },
15910
+ {
15911
+ "property": "risk_reduction",
15912
+ "type": "number",
15913
+ "required": true,
15914
+ "scope": "framework",
15915
+ "label": "Risk Reduction / Opportunity Enablement",
15916
+ "description": "Value from reducing risk or enabling future opportunities"
15917
+ },
15918
+ {
15919
+ "property": "job_size",
15920
+ "type": "number",
15921
+ "required": true,
15922
+ "scope": "framework",
15923
+ "label": "Job Size",
15924
+ "description": "Estimated effort (story points, t-shirt size, or person-weeks)"
15925
+ }
15926
+ ]
15927
+ },
15928
+ "computed_properties": [
15929
+ {
15930
+ "property": "wsjf_score",
15931
+ "expression": "(user_value + time_criticality + risk_reduction) / job_size",
15932
+ "entity_type": "feature",
15933
+ "label": "WSJF Score",
15934
+ "format": "number"
15935
+ }
15936
+ ]
15937
+ },
15938
+ "structure": {
15939
+ "pattern": "table"
15940
+ },
15941
+ "presentation": {
15942
+ "layout": {
15943
+ "type": "table",
15944
+ "columns": [
15945
+ {
15946
+ "property": "title",
15947
+ "label": "Backlog Items",
15948
+ "sortable": true
15949
+ },
15950
+ {
15951
+ "property": "user_value",
15952
+ "label": "User/Business Value",
15953
+ "sortable": true
15954
+ },
15955
+ {
15956
+ "property": "time_criticality",
15957
+ "label": "Time Criticality",
15958
+ "sortable": true
15959
+ },
15960
+ {
15961
+ "property": "risk_reduction",
15962
+ "label": "Risk Reduction / Opportunity Enablement",
15963
+ "sortable": true
15964
+ },
15965
+ {
15966
+ "property": "job_size",
15967
+ "label": "Job Size",
15968
+ "sortable": true
15969
+ },
15970
+ {
15971
+ "property": "wsjf_score",
15972
+ "label": "WSJF Score",
15973
+ "sortable": true
15974
+ }
15975
+ ]
15976
+ },
15977
+ "sort_by": {
15978
+ "property": "title",
15979
+ "direction": "asc"
15980
+ },
15981
+ "colour_by": "type",
15982
+ "card_fields": [
15983
+ "title",
15984
+ "description",
15985
+ "status"
15986
+ ]
15987
+ },
15988
+ "education": {
15989
+ "purpose": "Prioritise work by dividing the Cost of Delay by job duration, ensuring the most time-sensitive, valuable items are done first.",
15990
+ "core_question": "Considering the cost of waiting, which items should we start now to maximise economic benefit?",
15991
+ "when_to_use": [
15992
+ "You need to coordinate work across multiple teams or time horizons",
15993
+ "Stakeholders need visibility into what is coming and when",
15994
+ "You want to balance commitments with flexibility"
15995
+ ],
15996
+ "when_not_to_use": [
15997
+ "The team is small enough that informal coordination works",
15998
+ "Plans would create false precision about uncertain outcomes"
15999
+ ]
16000
+ }
16001
+ },
16002
+ {
16003
+ "id": "cost-of-delay",
16004
+ "approach_ids": [
16005
+ "prioritise"
16006
+ ],
16007
+ "name": "Cost of Delay",
16008
+ "version": "1.0.0",
16009
+ "description": "Quantify the economic cost of not shipping a feature to drive priority decisions. Combines urgency with value.",
16010
+ "category": "prioritization",
16011
+ "origin": {
16012
+ "type": "practitioner",
16013
+ "attribution": "Don Reinertsen",
16014
+ "description": "Formalised in The Principles of Product Development Flow (Celeritas Publishing). Foundational to lean product economics.",
16015
+ "year": 2009,
16016
+ "license": "public_domain"
16017
+ },
16018
+ "tags": [
16019
+ "prioritization",
16020
+ "table"
16021
+ ],
16022
+ "slots": [
16023
+ {
16024
+ "label": "Items to evaluate",
16025
+ "entityTypeId": "feature",
16026
+ "description": "Features or initiatives being assessed"
16027
+ },
16028
+ {
16029
+ "label": "User-Business Value",
16030
+ "entityTypeId": "outcome",
16031
+ "description": "Revenue, retention, or strategic value"
16032
+ },
16033
+ {
16034
+ "label": "Time Criticality",
16035
+ "entityTypeId": "metric",
16036
+ "description": "How much value decays with delay"
16037
+ },
16038
+ {
16039
+ "label": "Risk Reduction",
16040
+ "entityTypeId": "risk",
16041
+ "description": "What risk does this mitigate?"
16042
+ }
16043
+ ],
16044
+ "data": {
16045
+ "entity_types": [
16046
+ {
16047
+ "type": "feature",
16048
+ "role": "scored_item"
16049
+ },
16050
+ {
16051
+ "type": "metric",
16052
+ "role": "item"
16053
+ },
16054
+ {
16055
+ "type": "outcome",
16056
+ "role": "item"
16057
+ },
16058
+ {
16059
+ "type": "risk",
16060
+ "role": "item"
16061
+ }
16062
+ ],
16063
+ "required_properties": {
16064
+ "feature": [
16065
+ {
16066
+ "property": "cost_of_delay",
16067
+ "type": "number",
16068
+ "required": true,
16069
+ "scope": "framework",
16070
+ "label": "Cost of Delay",
16071
+ "description": "Weekly revenue impact of not shipping"
16072
+ },
16073
+ {
16074
+ "property": "job_size",
16075
+ "type": "number",
16076
+ "required": true,
16077
+ "scope": "framework",
16078
+ "label": "Job Size",
16079
+ "description": "Weeks of development effort"
16080
+ }
16081
+ ]
16082
+ },
16083
+ "computed_properties": [
16084
+ {
16085
+ "property": "wsjf_score",
16086
+ "expression": "cost_of_delay / job_size",
16087
+ "entity_type": "feature",
16088
+ "label": "WSJF Score",
16089
+ "format": "number"
16090
+ }
16091
+ ]
16092
+ },
16093
+ "structure": {
16094
+ "pattern": "table"
16095
+ },
16096
+ "presentation": {
16097
+ "layout": {
16098
+ "type": "table",
16099
+ "columns": [
16100
+ {
16101
+ "property": "title",
16102
+ "label": "Items to evaluate",
16103
+ "sortable": true
16104
+ },
16105
+ {
16106
+ "property": "cost_of_delay",
16107
+ "label": "User-Business Value",
16108
+ "sortable": true
16109
+ },
16110
+ {
16111
+ "property": "job_size",
16112
+ "label": "Job Size",
16113
+ "sortable": true
16114
+ },
16115
+ {
16116
+ "property": "wsjf_score",
16117
+ "label": "CoD Score",
16118
+ "sortable": true
16119
+ }
16120
+ ]
16121
+ },
16122
+ "sort_by": {
16123
+ "property": "title",
16124
+ "direction": "asc"
16125
+ },
16126
+ "colour_by": "type",
16127
+ "card_fields": [
16128
+ "title",
16129
+ "description",
16130
+ "status"
16131
+ ]
16132
+ },
16133
+ "education": {
16134
+ "purpose": "Quantify the economic impact of not delivering a feature by a given date, making urgency visible and enabling time-sensitive prioritisation.",
16135
+ "core_question": "How much value are we losing every week this feature is not in production, and does that urgency justify fast-tracking it?",
16136
+ "when_to_use": [
16137
+ "You have more ideas or features than capacity to build them",
16138
+ "Stakeholders disagree on what to build next",
16139
+ "You need a transparent, defensible prioritisation process"
16140
+ ],
16141
+ "when_not_to_use": [
16142
+ "You have a single obvious next step with no contention",
16143
+ "The backlog is small enough to sequence intuitively"
16144
+ ]
16145
+ }
16146
+ },
16147
+ {
16148
+ "id": "five-whys",
16149
+ "approach_ids": [
16150
+ "reflect",
16151
+ "inspect"
16152
+ ],
16153
+ "name": "Five Whys",
16154
+ "version": "1.0.0",
16155
+ "description": 'Iteratively ask "why?", typically five times, starting from a symptom; each answer becomes the subject of the next question. The chain of answers reveals the underlying root cause behind the surface problem.',
16156
+ "category": "team_process",
16157
+ "origin": {
16158
+ "type": "practitioner",
16159
+ "attribution": "Sakichi Toyoda / Toyota Production System",
16160
+ "description": "Developed within the Toyota Production System as a root-cause analysis technique. Popularised through Lean and Six Sigma practice; now a widely used incident-review and design-debug staple.",
16161
+ "year": 1930,
16162
+ "license": "public_domain"
16163
+ },
16164
+ "tags": [
16165
+ "team_process",
16166
+ "reflection",
16167
+ "root_cause",
16168
+ "tree"
16169
+ ],
16170
+ "slots": [
16171
+ {
16172
+ "label": "Symptom",
16173
+ "entityTypeId": "need",
16174
+ "description": "The observed problem the analysis starts from."
16175
+ },
16176
+ {
16177
+ "label": "Why chain",
16178
+ "entityTypeId": "insight",
16179
+ "description": 'Each "why?" answer along the chain, typically five iterations deep.'
16180
+ },
16181
+ {
16182
+ "label": "Root cause",
16183
+ "entityTypeId": "insight",
16184
+ "description": "The terminal answer at the bottom of the chain: the underlying cause to address."
16185
+ }
16186
+ ],
16187
+ "data": {
16188
+ "entity_types": [
16189
+ {
16190
+ "type": "need",
16191
+ "role": "root"
16192
+ },
16193
+ {
16194
+ "type": "insight",
16195
+ "role": "branch"
16196
+ }
16197
+ ],
16198
+ "required_properties": {}
16199
+ },
16200
+ "structure": {
16201
+ "pattern": "tree"
16202
+ },
16203
+ "presentation": {
16204
+ "layout": {
16205
+ "type": "tree",
16206
+ "direction": "TB"
16207
+ },
16208
+ "sort_by": {
16209
+ "property": "title",
16210
+ "direction": "asc"
16211
+ },
16212
+ "card_fields": [
16213
+ "title",
16214
+ "description"
16215
+ ]
16216
+ },
16217
+ "education": {
16218
+ "purpose": 'Move past surface symptoms by chaining "why?" questions until the underlying root cause surfaces, so fixes target the real driver rather than a downstream effect.',
16219
+ "core_question": "Why is this happening, and why is THAT happening, until we reach a cause we can act on?",
16220
+ "when_to_use": [
16221
+ "A problem keeps recurring after surface fixes",
16222
+ "Post-incident review where the obvious cause feels too obvious",
16223
+ "Designing a fix and you want to confirm you understand the actual driver"
16224
+ ],
16225
+ "when_not_to_use": [
16226
+ "The problem has multiple independent root causes (use a fishbone or richer RCA tool)",
16227
+ "You need quantitative attribution rather than a single-thread narrative",
16228
+ 'Five linear "whys" oversimplify a systems problem with feedback loops'
16229
+ ]
16230
+ }
16231
+ },
16232
+ {
16233
+ "id": "pre-mortem",
16234
+ "approach_ids": [
16235
+ "reflect"
16236
+ ],
16237
+ "name": "Pre-mortem",
16238
+ "version": "1.0.0",
16239
+ "description": "Imagine the project has already failed; work backward listing the plausible causes of the failure. Produce a risk register and matching mitigations before the work starts.",
16240
+ "category": "team_process",
16241
+ "origin": {
16242
+ "type": "practitioner",
16243
+ "attribution": "Gary Klein",
16244
+ "description": "Popularised by Gary Klein in Harvard Business Review (2007) as a prospective-hindsight technique. Inverts the post-mortem: imagine failure first, then list causes, while there is still time to act.",
16245
+ "url": "https://hbr.org/2007/09/performing-a-project-premortem",
16246
+ "year": 2007,
16247
+ "license": "published_methodology"
16248
+ },
16249
+ "tags": [
16250
+ "team_process",
16251
+ "reflection",
16252
+ "risk",
16253
+ "collection"
16254
+ ],
16255
+ "slots": [
16256
+ {
16257
+ "label": "Imagined failures",
16258
+ "entityTypeId": "risk",
16259
+ "description": "Plausible failure modes named as if they had already occurred."
16260
+ },
16261
+ {
16262
+ "label": "Causes",
16263
+ "entityTypeId": "insight",
16264
+ "description": "For each imagined failure, the contributing causes the team can foresee."
16265
+ },
16266
+ {
16267
+ "label": "Mitigations",
16268
+ "entityTypeId": "initiative",
16269
+ "description": "Mitigation actions the team will take before failure can occur."
16270
+ }
16271
+ ],
16272
+ "data": {
16273
+ "entity_types": [
16274
+ {
16275
+ "type": "risk",
16276
+ "role": "bucket"
16277
+ },
16278
+ {
16279
+ "type": "insight",
16280
+ "role": "bucket"
16281
+ },
16282
+ {
16283
+ "type": "initiative",
16284
+ "role": "bucket"
16285
+ }
16286
+ ],
16287
+ "required_properties": {}
16288
+ },
16289
+ "structure": {
16290
+ "pattern": "collection"
16291
+ },
16292
+ "presentation": {
16293
+ "layout": {
16294
+ "type": "grid",
16295
+ "groupBy": "type"
16296
+ },
16297
+ "sort_by": {
16298
+ "property": "title",
16299
+ "direction": "asc"
16300
+ },
16301
+ "colour_by": "group",
16302
+ "card_fields": [
16303
+ "title",
16304
+ "description"
16305
+ ]
16306
+ },
16307
+ "education": {
16308
+ "purpose": "Surface project risks early by inverting hindsight: imagine the project has already failed and ask why, while there is still time to mitigate.",
16309
+ "core_question": "It is six months from now and the project has failed catastrophically. What happened, and why?",
16310
+ "when_to_use": [
16311
+ "Kicking off a project with significant downside or irreversible commitment",
16312
+ "A plan looks too clean and the team senses unspoken concerns",
16313
+ "Stakeholders disagree on risk; the exercise externalises and ranks them"
16314
+ ],
16315
+ "when_not_to_use": [
16316
+ "The work is small, reversible, and cheap to course-correct",
16317
+ "The team is in execution mode and reflective ceremonies will derail momentum",
16318
+ "Risk surfacing has become performative: the team names risks but never mitigates them"
16319
+ ]
16320
+ }
16321
+ },
16322
+ {
16323
+ "id": "red-team",
16324
+ "approach_ids": [
16325
+ "reflect",
16326
+ "inspect"
16327
+ ],
16328
+ "name": "Red Team",
16329
+ "version": "1.0.0",
16330
+ "description": "Structured adversarial review. A designated group is assigned to attack a plan, design, or proposal from an outside-in stance, surfacing weaknesses the inside-out builders cannot see.",
16331
+ "category": "team_process",
16332
+ "origin": {
16333
+ "type": "practitioner",
16334
+ "attribution": "US Department of Defense (Cold War era); broadened by security and intelligence practice",
16335
+ "description": `Originated in Cold War-era military strategic exercises ("red" team takes the adversary role against the "blue" team's defence). Adopted by cybersecurity, intelligence analysis, and product teams as a structured contrarian-review practice.`,
16336
+ "year": 1960,
16337
+ "license": "public_domain"
16338
+ },
16339
+ "tags": [
16340
+ "team_process",
16341
+ "reflection",
16342
+ "adversarial",
16343
+ "collection"
16344
+ ],
16345
+ "slots": [
16346
+ {
16347
+ "label": "Target",
16348
+ "entityTypeId": "initiative",
16349
+ "description": "The plan, design, or proposal under adversarial review."
16350
+ },
16351
+ {
16352
+ "label": "Attack vectors",
16353
+ "entityTypeId": "risk",
16354
+ "description": "The angles the red team uses to probe weaknesses."
16355
+ },
16356
+ {
16357
+ "label": "Findings",
16358
+ "entityTypeId": "insight",
16359
+ "description": "Weaknesses, blind spots, or unstated assumptions surfaced by the review."
16360
+ }
16361
+ ],
16362
+ "data": {
16363
+ "entity_types": [
16364
+ {
16365
+ "type": "initiative",
16366
+ "role": "root"
16367
+ },
16368
+ {
16369
+ "type": "risk",
16370
+ "role": "bucket"
16371
+ },
16372
+ {
16373
+ "type": "insight",
16374
+ "role": "bucket"
16375
+ }
16376
+ ],
16377
+ "required_properties": {}
16378
+ },
16379
+ "structure": {
16380
+ "pattern": "collection"
16381
+ },
16382
+ "presentation": {
16383
+ "layout": {
16384
+ "type": "grid",
16385
+ "groupBy": "type"
16386
+ },
16387
+ "sort_by": {
16388
+ "property": "title",
16389
+ "direction": "asc"
16390
+ },
16391
+ "colour_by": "group",
16392
+ "card_fields": [
16393
+ "title",
16394
+ "description"
16395
+ ]
16396
+ },
16397
+ "education": {
16398
+ "purpose": "Stress-test a plan against an explicit adversary by assigning reviewers to attack rather than agree, so weaknesses surface before reality finds them.",
16399
+ "core_question": "If a competent adversary wanted this to fail, where would they push first, and would we hold?",
16400
+ "when_to_use": [
16401
+ "A high-stakes decision, launch, or security posture needs hardening",
16402
+ "Inside-out thinking is dominant and dissent has gone quiet",
16403
+ "Risk register is suspiciously short for the size of the bet"
16404
+ ],
16405
+ "when_not_to_use": [
16406
+ "Early-stage exploration where adversarial framing would crush a fragile idea prematurely",
16407
+ "Team trust is too low: red-teaming will read as personal attack rather than role-play",
16408
+ "The work is small enough that a lightweight devil's-advocate pass is sufficient"
16409
+ ]
16410
+ }
16411
+ },
16412
+ {
16413
+ "id": "devils-advocate",
16414
+ "approach_ids": [
16415
+ "reflect"
16416
+ ],
16417
+ "name": "Devil's Advocate",
16418
+ "version": "1.0.0",
16419
+ "description": "Designate one reviewer to formally take the opposing position regardless of personal view. The assigned-role contrarian defangs groupthink by making dissent legitimate and structured.",
16420
+ "category": "team_process",
16421
+ "origin": {
16422
+ "type": "practitioner",
16423
+ "attribution": "Roman Catholic Church (advocatus diaboli); broadened by decision-quality practice",
16424
+ "description": "Originated in 16th-century canonisation proceedings as the advocatus diaboli, an official assigned to argue against canonising a candidate. Adopted by decision-science and product-team practice as a structured antidote to groupthink.",
16425
+ "year": 1587,
16426
+ "license": "public_domain"
16427
+ },
16428
+ "tags": [
16429
+ "team_process",
16430
+ "reflection",
16431
+ "decision_quality",
16432
+ "collection"
16433
+ ],
16434
+ "slots": [
16435
+ {
16436
+ "label": "Proposal",
16437
+ "entityTypeId": "initiative",
16438
+ "description": "The plan or recommendation under consideration."
16439
+ },
16440
+ {
16441
+ "label": "Opposing arguments",
16442
+ "entityTypeId": "insight",
16443
+ "description": "The case against the proposal, voiced by the assigned contrarian regardless of personal view."
16444
+ },
16445
+ {
16446
+ "label": "Counter-evidence",
16447
+ "entityTypeId": "evidence",
16448
+ "description": "Data points the contrarian raises that the proposal does not yet account for."
16449
+ }
16450
+ ],
16451
+ "data": {
16452
+ "entity_types": [
16453
+ {
16454
+ "type": "initiative",
16455
+ "role": "root"
16456
+ },
16457
+ {
16458
+ "type": "insight",
16459
+ "role": "bucket"
16460
+ },
16461
+ {
16462
+ "type": "evidence",
16463
+ "role": "bucket"
16464
+ }
16465
+ ],
16466
+ "required_properties": {}
16467
+ },
16468
+ "structure": {
16469
+ "pattern": "collection"
16470
+ },
16471
+ "presentation": {
16472
+ "layout": {
16473
+ "type": "grid",
16474
+ "groupBy": "type"
16475
+ },
16476
+ "sort_by": {
16477
+ "property": "title",
16478
+ "direction": "asc"
16479
+ },
16480
+ "colour_by": "group",
16481
+ "card_fields": [
16482
+ "title",
16483
+ "description"
16484
+ ]
16485
+ },
16486
+ "education": {
16487
+ "purpose": "Make dissent a legitimate role rather than a personal stance. Assigning one reviewer to argue against the proposal forces the team to confront the strongest counter-case.",
16488
+ "core_question": "If we had to argue against this proposal (not because we believe it, but because the role demands it) what is the strongest case?",
16489
+ "when_to_use": [
16490
+ "A decision is heading toward consensus and you suspect groupthink",
16491
+ "Stakes are high and the team has not heard a serious counter-argument",
16492
+ "Cultural norms make raw dissent costly; assigning the role lowers the social cost"
16493
+ ],
16494
+ "when_not_to_use": [
16495
+ "Genuine disagreement already exists in the room (let it surface; do not theatricalise it)",
16496
+ "The decision is small enough that the ceremony costs more than the insight returned",
16497
+ "The assigned contrarian will be punished socially for the role; set the norms first or skip"
16498
+ ]
16499
+ }
16500
+ },
16501
+ {
16502
+ "id": "second-order-thinking",
16503
+ "approach_ids": [
16504
+ "reflect"
16505
+ ],
16506
+ "name": "Second-order Thinking",
16507
+ "version": "1.0.0",
16508
+ "description": 'After deciding a move, ask "and then what?" repeatedly. Trace second-, third-, and higher-order consequences to surface downstream effects that first-order reasoning misses.',
16509
+ "category": "team_process",
16510
+ "origin": {
16511
+ "type": "practitioner",
16512
+ "attribution": "Howard Marks / Charlie Munger",
16513
+ "description": `Howard Marks distinguished first-order vs second-order thinking in The Most Important Thing (2011) as the essential discipline of consequential decision-making. Charlie Munger's "and then what?" framing is the practical heuristic.`,
16514
+ "url": "https://www.oaktreecapital.com/insights/memo/dare-to-be-great-ii",
16515
+ "year": 2011,
16516
+ "license": "published_methodology"
16517
+ },
16518
+ "tags": [
16519
+ "team_process",
16520
+ "reflection",
16521
+ "consequences",
16522
+ "tree"
16523
+ ],
16524
+ "slots": [
16525
+ {
16526
+ "label": "First-order move",
16527
+ "entityTypeId": "decision",
16528
+ "description": "The decision or move under consideration."
16529
+ },
16530
+ {
16531
+ "label": "Second-order consequences",
16532
+ "entityTypeId": "insight",
16533
+ "description": "Downstream effects that follow from the first-order move."
16534
+ },
16535
+ {
16536
+ "label": "Higher-order consequences",
16537
+ "entityTypeId": "insight",
16538
+ "description": "Third-, fourth-, fifth-order ripples: second-order consequences of the second-order consequences."
16539
+ }
16540
+ ],
16541
+ "data": {
16542
+ "entity_types": [
16543
+ {
16544
+ "type": "decision",
16545
+ "role": "root"
16546
+ },
16547
+ {
16548
+ "type": "insight",
16549
+ "role": "branch"
16550
+ }
16551
+ ],
16552
+ "required_properties": {}
16553
+ },
16554
+ "structure": {
16555
+ "pattern": "tree"
16556
+ },
16557
+ "presentation": {
16558
+ "layout": {
16559
+ "type": "tree",
16560
+ "direction": "TB"
16561
+ },
16562
+ "sort_by": {
16563
+ "property": "title",
16564
+ "direction": "asc"
16565
+ },
16566
+ "card_fields": [
16567
+ "title",
16568
+ "description"
16569
+ ]
16570
+ },
16571
+ "education": {
16572
+ "purpose": 'Resist first-order reasoning by chaining "and then what?" until non-obvious downstream consequences come into view.',
16573
+ "core_question": "If we make this move and it works, what does the world look like next, and is that the world we want?",
16574
+ "when_to_use": [
16575
+ "A decision has feedback loops, market reactions, or behavioural ripples",
16576
+ "The first-order case is compelling, which is exactly when downstream effects bite",
16577
+ "Considering an irreversible or large-scale commitment"
16578
+ ],
16579
+ "when_not_to_use": [
16580
+ "Routine, reversible, low-blast-radius decisions where deliberation costs more than mistakes",
16581
+ "Higher-order branches diverge into pure speculation with no anchor in evidence",
16582
+ "Time pressure makes a deeper trace expensive and the first-order call is good enough"
16583
+ ]
16584
+ }
16585
+ }
16586
+ ];
16587
+ var UPG_FRAMEWORKS_BY_ID = Object.fromEntries(
16588
+ UPG_FRAMEWORKS.map((fw) => [fw.id, fw])
16589
+ );
16590
+ var UPG_FRAMEWORKS_BY_CATEGORY = {};
16591
+ for (const fw of UPG_FRAMEWORKS) {
16592
+ if (!UPG_FRAMEWORKS_BY_CATEGORY[fw.category]) UPG_FRAMEWORKS_BY_CATEGORY[fw.category] = [];
16593
+ UPG_FRAMEWORKS_BY_CATEGORY[fw.category].push(fw);
16594
+ }
16595
+
16596
+ // src/presentation/labels.ts
16597
+ var PRIORITY_LABELS = [
16598
+ // ── need (CONSOLIDATED: replaces pain_point + user_need) ─────────────────────
16599
+ {
16600
+ id: "need",
16601
+ canonical_label: "Need",
16602
+ alt_labels: [
16603
+ "pain point",
16604
+ "pain",
16605
+ "user need",
16606
+ "customer need",
16607
+ "problem",
16608
+ "struggle",
16609
+ "customer pain",
16610
+ "frustration",
16611
+ "gap",
16612
+ "unmet need",
16613
+ "user problem"
16614
+ ],
16615
+ framework_labels: {
16616
+ lean_canvas: "Problem",
16617
+ design_thinking: "Pain Point",
16618
+ ost: "Opportunity (need)",
16619
+ jtbd: "Struggle",
16620
+ vpc: "Customer Pain"
16621
+ },
16622
+ designations: {
16623
+ pain: "Pain Point",
16624
+ gap: "Need",
16625
+ desire: "Desire",
16626
+ constraint: "Constraint"
16627
+ }
16628
+ },
16629
+ // ── opportunity ──────────────────────────────────────────────────────────────
16630
+ {
16631
+ id: "opportunity",
16632
+ canonical_label: "Opportunity",
16633
+ alt_labels: ["product opportunity", "market opportunity", "user opportunity"],
16634
+ framework_labels: {
16635
+ ost: "Opportunity"
16636
+ }
16637
+ },
16638
+ // ── solution ─────────────────────────────────────────────────────────────────
16639
+ {
16640
+ id: "solution",
16641
+ canonical_label: "Solution",
16642
+ alt_labels: ["proposed solution", "solution idea", "concept", "approach"],
16643
+ framework_labels: {
16644
+ ost: "Solution",
16645
+ design_thinking: "Solution",
16646
+ lean_canvas: "Solution",
16647
+ rice: "Scored Solution"
16648
+ }
16649
+ },
16650
+ // ── experiment (CONSOLIDATED: absorbs ab_test, growth_experiment, pricing_experiment) ──
16651
+ {
16652
+ id: "experiment",
16653
+ canonical_label: "Experiment",
16654
+ alt_labels: [
16655
+ "test",
16656
+ "validation",
16657
+ "ab test",
16658
+ "a/b test",
16659
+ "split test",
16660
+ "growth experiment",
16661
+ "pricing experiment",
16662
+ "usability test",
16663
+ "discovery experiment"
16664
+ ],
16665
+ framework_labels: {
16666
+ ost: "Experiment",
16667
+ design_thinking: "Test",
16668
+ lean_startup: "Experiment"
16669
+ },
16670
+ designations: {
16671
+ discovery: "Discovery Experiment",
16672
+ ab_test: "A/B Test",
16673
+ growth: "Growth Experiment",
16674
+ pricing: "Pricing Experiment",
16675
+ usability: "Usability Test"
16676
+ }
16677
+ },
16678
+ // ── hypothesis ───────────────────────────────────────────────────────────────
16679
+ {
16680
+ id: "hypothesis",
16681
+ canonical_label: "Hypothesis",
16682
+ alt_labels: ["bet", "testable assumption", "leap of faith"],
16683
+ framework_labels: {
16684
+ lean_startup: "Hypothesis",
16685
+ running_lean: "Riskiest Assumption",
16686
+ lean_canvas: "Riskiest Assumption"
16687
+ }
16688
+ },
16689
+ // ── metric (CONSOLIDATED: absorbs kpi, north_star_metric, input_metric, metric_definition) ──
16690
+ {
16691
+ id: "metric",
16692
+ canonical_label: "Metric",
16693
+ alt_labels: [
16694
+ "kpi",
16695
+ "key performance indicator",
16696
+ "north star metric",
16697
+ "nsm",
16698
+ "input metric",
16699
+ "output metric",
16700
+ "metric definition",
16701
+ "measure",
16702
+ "indicator",
16703
+ "signal",
16704
+ "counter metric",
15687
16705
  "guardrail metric"
15688
16706
  ],
15689
16707
  framework_labels: {
@@ -16515,7 +17533,10 @@ var STRATEGY_OUTCOMES_PLAYBOOK = {
16515
17533
  region: "strategy_outcomes",
16516
17534
  is_canonical: true,
16517
17535
  related_framework_ids: ["okr-framework", "three-horizons", "north-star-metric", "metrics-tree", "wardley-map"],
16518
- target_anchor_entity: "objective",
17536
+ // DT-PB-3: anchor is `outcome`, not `objective`. The creation_sequence
17537
+ // creates outcome (step 3) before objective (step 4), and outcome is the
17538
+ // strategy region's gravitational centre (objectives translate outcomes).
17539
+ target_anchor_entity: "outcome",
16519
17540
  creation_sequence: [
16520
17541
  seqStep(
16521
17542
  1,
@@ -16671,8 +17692,12 @@ var DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK = {
16671
17692
  seqStep(
16672
17693
  8,
16673
17694
  "Test",
16674
- ["experiment", "test_plan", "evidence"],
16675
- "Validate with targeted experiments. Close the loop between research and action."
17695
+ // DT-PB-1: was `experiment` — which resolves to NO canonical edge with
17696
+ // hypothesis or test_plan, forcing an orphan. `experiment_run` is the
17697
+ // hypothesis-linked test unit (experiment_run_validates_hypothesis,
17698
+ // test_plan_ran_as_experiment_run, experiment_run_yields_evidence).
17699
+ ["experiment_run", "test_plan", "evidence"],
17700
+ "Validate with targeted experiment runs. Close the loop between research and action."
16676
17701
  )
16677
17702
  ]
16678
17703
  };
@@ -16719,8 +17744,8 @@ var MARKET_COMPETITIVE_PLAYBOOK = {
16719
17744
  seqStep(
16720
17745
  6,
16721
17746
  "Moves",
16722
- ["partnership"],
16723
- "Look where competitors are weak and trends are strong. That intersection is where your moves live, including who to partner with."
17747
+ ["competitive_battle_card"],
17748
+ "Look where competitors are weak and trends are strong. That intersection is where your moves live: arm the team with battle cards that turn each competitor weakness into a position you can win. (Partnership moves belong in the business & GTM playbook, where `partnership` connects.)"
16724
17749
  )
16725
17750
  ]
16726
17751
  };
@@ -17133,8 +18158,8 @@ var OPERATIONS_QUALITY_PLAYBOOK = {
17133
18158
  seqStep(
17134
18159
  5,
17135
18160
  "Quality Gates",
17136
- ["test_suite", "test_case", "regression_test", "qa_session"],
17137
- "Establish what does not ship until tests pass. Define the test pyramid: unit, integration, end-to-end."
18161
+ ["test_suite", "test_case", "regression_test", "qa_session", "feature", "bug"],
18162
+ "Establish what does not ship until tests pass. Define the test pyramid: unit, integration, end-to-end. Quality is a delivery concern: gates guard the features they cover and the bugs they catch, which is also where incidents trace back (a shipped defect becomes a production incident)."
17138
18163
  ),
17139
18164
  seqStep(
17140
18165
  6,
@@ -18789,7 +19814,7 @@ var PORTFOLIO_GUIDE = {
18789
19814
  var WORKSPACE_GUIDE = {
18790
19815
  domain_id: "workspace",
18791
19816
  anchor_entity: "workspace",
18792
- creation_sequence: ["workspace"],
19817
+ creation_sequence: ["workspace", "framework_exercise"],
18793
19818
  patterns: [],
18794
19819
  required_bridges: [],
18795
19820
  anti_patterns: [
@@ -21201,7 +22226,7 @@ var UPG_ANTI_PATTERNS = [
21201
22226
  ]
21202
22227
  },
21203
22228
  why_it_matters: "Without any chain link, every downstream artefact (need, opportunity, feature) loses its anchor. Features end up addressing demographics instead of struggles.",
21204
- remediation: "For each persona, connect it into the user chain via at least one of: `persona_pursues_job`, `persona_experiences_need`, `persona_aspires_to_desired_outcome`, or `persona_incurs_switching_cost`. Use `/upg-persona` or the JTBD canvas workflow.",
22229
+ remediation: "For each persona, connect it into the user chain via at least one of: `persona_pursues_job`, `persona_experiences_need`, `persona_aspires_to_desired_outcome`, or `persona_incurs_switching_cost`. Use `/upg-new-persona` or the JTBD canvas workflow.",
21205
22230
  stages: ["concept", "validation", "build", "beta", "launch", "growth", "mature"],
21206
22231
  severity: "high",
21207
22232
  source: { kind: "practitioner", attribution: "Clayton Christensen, Jobs to Be Done" }
@@ -21341,7 +22366,7 @@ var UPG_ANTI_PATTERNS = [
21341
22366
  ]
21342
22367
  },
21343
22368
  why_it_matters: "OKRs without measurable key results cannot be tracked, debated, or learned from. The graph carries intent but not accountability.",
21344
- remediation: "For each `objective`, define 2\u20134 `key_result` entities and link via `objective_achieved_through_key_result`. Use `/upg-okr` to author.",
22369
+ remediation: "For each `objective`, define 2\u20134 `key_result` entities and link via `objective_achieved_through_key_result`. Use `/upg-new-okr` to author.",
21345
22370
  stages: ["validation", "build", "beta", "launch", "growth", "mature"],
21346
22371
  severity: "high",
21347
22372
  source: { kind: "book", citation: "Measure What Matters, John Doerr (2017)" }
@@ -21401,7 +22426,7 @@ var UPG_ANTI_PATTERNS = [
21401
22426
  }
21402
22427
  },
21403
22428
  why_it_matters: 'A graph with one persona past validation is usually carrying an unexamined "everyone is the same user" assumption.',
21404
- remediation: "Add personas representing the next 1\u20132 most distinct user segments. Use `/upg-persona`.",
22429
+ remediation: "Add personas representing the next 1\u20132 most distinct user segments. Use `/upg-new-persona`.",
21405
22430
  stages: ["validation", "build", "beta", "launch", "growth", "mature"],
21406
22431
  severity: "medium",
21407
22432
  source: { kind: "practitioner", attribution: "Alan Cooper, The Inmates Are Running the Asylum" }
@@ -21419,7 +22444,7 @@ var UPG_ANTI_PATTERNS = [
21419
22444
  ]
21420
22445
  },
21421
22446
  why_it_matters: "Build-only graphs commit the team to delivery without a learning loop. Every shipped feature becomes a permanent assumption.",
21422
- remediation: "Spin up at least one `experiment_plan` or `hypothesis` per quarter's build batch. Use `/upg-discover` or `/upg-hypothesis`.",
22447
+ remediation: "Spin up at least one `experiment_plan` or `hypothesis` per quarter's build batch. Use `/upg-new-discovery` or `/upg-new-hypothesis`.",
21423
22448
  stages: ["build", "beta", "launch", "growth"],
21424
22449
  severity: "high",
21425
22450
  source: { kind: "practitioner", attribution: "Marty Cagan, Inspired (continuous discovery)" }
@@ -21710,19 +22735,31 @@ var REFLECT_MODES = [
21710
22735
  ];
21711
22736
 
21712
22737
  // src/approaches/definitions/index.ts
22738
+ function deriveFrameworkExamples() {
22739
+ const byApproach = {
22740
+ plan: [],
22741
+ inspect: [],
22742
+ prioritise: [],
22743
+ trace: [],
22744
+ reflect: []
22745
+ };
22746
+ for (const fw of UPG_FRAMEWORKS) {
22747
+ for (const approachId of fw.approach_ids ?? []) {
22748
+ if (approachId in byApproach) {
22749
+ byApproach[approachId].push(fw.id);
22750
+ }
22751
+ }
22752
+ }
22753
+ return byApproach;
22754
+ }
22755
+ var FRAMEWORK_EXAMPLES = deriveFrameworkExamples();
21713
22756
  var PLAN = {
21714
22757
  id: "plan",
21715
22758
  label: "Plan",
21716
22759
  description: 'The path of arrival to "what should I build next?". Plan engages a region by surveying its entity coverage against canonical expectations and surfacing the missing scaffolding: the entities a healthy region carries that this graph does not. Cartographic sense: you are walking the coastline of a region and noting where the contour is incomplete, not deciding a strategy. Frameworks like Now/Next/Later, MoSCoW, and Wardley Mapping live within Plan as the named techniques for organising the gap-filling sequence.',
21717
22760
  question_answered: "what should I build next?",
21718
22761
  signature_hint: "({ region?: UPGRegionId }) \u2192 { missing_entities, coverage_score }",
21719
- framework_id_examples: [
21720
- "now-next-later",
21721
- "moscow",
21722
- "wardley-map",
21723
- "okr-framework",
21724
- "three-horizons"
21725
- ]
22762
+ framework_id_examples: FRAMEWORK_EXAMPLES.plan
21726
22763
  };
21727
22764
  var INSPECT = {
21728
22765
  id: "inspect",
@@ -21730,28 +22767,15 @@ var INSPECT = {
21730
22767
  description: 'The path of arrival to "what\'s broken?". Inspect engages a region or a set of entities by running canonical health checks (anti-pattern audits, drift reports, lint passes) and emitting a structured violation list with severity, kind, target entity, description, and fix hint. Cartographic sense: you are surveying the coastline for hazards before approach; the violations are the rocks marked on the chart. The named techniques inside Inspect are the audit catalogues themselves (`UPG_ANTI_PATTERNS` and the lint passes built on the structural rules).',
21731
22768
  question_answered: "what's broken?",
21732
22769
  signature_hint: "({ region?: UPGRegionId, entities?: entity_ids[] }) \u2192 { violations: [{ severity, kind, entity_id, description, fix_hint }] }",
21733
- framework_id_examples: [
21734
- "heuristic-evaluation",
21735
- "tech-debt-tracker",
21736
- "accessibility-maturity-model",
21737
- "cognitive-walkthrough",
21738
- "blameless-postmortem"
21739
- ]
22770
+ framework_id_examples: FRAMEWORK_EXAMPLES.inspect
21740
22771
  };
21741
22772
  var PRIORITISE = {
21742
22773
  id: "prioritise",
21743
22774
  label: "Prioritise",
21744
- description: `The path of arrival to "what's most important?". Prioritise engages an explicit candidate set (entity ids the caller passes in) and ranks it by an explicit framework: RICE, ICE, Kano, Cost of Delay. The framework_id is required because prioritisation without a declared scoring lens is incoherent. Cartographic sense: you have a set of charted destinations and you are computing the order of arrival from a chosen vantage. Different frameworks weight the same candidate set differently; the approach delegates the actual ranking math to the named technique (the framework definition).`,
22775
+ description: `The path of arrival to "what's most important?". Prioritise engages an explicit candidate set (entity ids the caller passes in) and ranks it by an explicit framework: RICE, Kano, MoSCoW. The framework_id is required because prioritisation without a declared scoring lens is incoherent. Cartographic sense: you have a set of charted destinations and you are computing the order of arrival from a chosen vantage. Different frameworks weight the same candidate set differently; the approach delegates the actual ranking math to the named technique (the framework definition).`,
21745
22776
  question_answered: "what's most important?",
21746
22777
  signature_hint: "({ candidates: entity_ids[], framework_id }) \u2192 { ranked: [{ entity_id, score, rationale }], framework_used }",
21747
- framework_id_examples: [
21748
- "rice-scoring",
21749
- "ice-scoring",
21750
- "kano-model",
21751
- "cost-of-delay",
21752
- "moscow",
21753
- "wsjf"
21754
- ]
22778
+ framework_id_examples: FRAMEWORK_EXAMPLES.prioritise
21755
22779
  };
21756
22780
  var TRACE = {
21757
22781
  id: "trace",
@@ -21759,34 +22783,15 @@ var TRACE = {
21759
22783
  description: 'The path of arrival to "walk a meaningful path through existing graph". Trace engages an anchor entity and follows a path expressed as a UPGEntityType[] shorthand. Example: `["persona", "job", "feature"]` walks persona\u2192job\u2192feature using the canonical edge for each pair (resolved via `resolve_edge_for_pair`). An optional `edges_override` array selects non-canonical edges per hop when a pair has multiple resolutions. Cartographic sense: you are tracing a route across charted terrain; anchor is the departure, path is the heading sequence, the canonical edges are the roads. No DSL invented; the shorthand IS the path expression.',
21760
22784
  question_answered: "walk a meaningful path through existing graph",
21761
22785
  signature_hint: "({ anchor: entity_id, path: UPGEntityType[], edges_override?: (string | null)[] }) \u2192 { trail: [{ depth, entity_id, edge_type_in }], reached: entity_id[] }",
21762
- framework_id_examples: [
21763
- "opportunity-solution-tree",
21764
- "strategic-cascade",
21765
- "metrics-tree",
21766
- "user-journey-map",
21767
- "impact-map",
21768
- "dependency-map"
21769
- ]
22786
+ framework_id_examples: FRAMEWORK_EXAMPLES.trace
21770
22787
  };
21771
22788
  var REFLECT = {
21772
22789
  id: "reflect",
21773
22790
  label: "Reflect",
21774
- description: 'The path of arrival to "what should I be questioning?". Reflect engages an optional scope (region, entity, or `null` for the whole graph) and emits structured prompts a thinker should consider: assumptions to test, alternatives to weigh, blind-spots to surface, load-bearing claims to verify. Mode is optional; absence is open reflection. Cartographic sense: before approaching the coastline, you are asking which features of your chart you have not actually verified; the prompts mark the parts of the map that may be conjecture. Five Whys, Pre-mortem, Red Team, and Devil\'s Advocate are the named techniques inside this approach.',
22791
+ description: 'The path of arrival to "what should I be questioning?". Reflect engages an optional scope (region, entity, or `null` for the whole graph) and emits structured prompts a thinker should consider: assumptions to test, alternatives to weigh, blind-spots to surface, load-bearing claims to verify. Mode is optional; absence is open reflection. Cartographic sense: before approaching the coastline, you are asking which features of your chart you have not actually verified; the prompts mark the parts of the map that may be conjecture. Retrospective and Build-Measure-Learn are the named reflective techniques in the canonical surface.',
21775
22792
  question_answered: "what should I be questioning?",
21776
22793
  signature_hint: "({ scope?: UPGRegionId | entity_id | null, mode?: 'assumptions' | 'alternatives' | 'blind-spots' | 'load-bearing' }) \u2192 { prompts: [{ kind, question, target_entities? }] }",
21777
- framework_id_examples: [
21778
- // Reflection classics: the five canonical reflect frameworks.
21779
- "five-whys",
21780
- "pre-mortem",
21781
- "red-team",
21782
- "devils-advocate",
21783
- "second-order-thinking",
21784
- // Reflective ceremonies + reflective JTBD lens already in the catalog.
21785
- "retrospective",
21786
- "four-forces-of-progress",
21787
- "assumption-canvas",
21788
- "win-loss-analysis"
21789
- ]
22794
+ framework_id_examples: FRAMEWORK_EXAMPLES.reflect
21790
22795
  };
21791
22796
  var UPG_APPROACHES = [PLAN, INSPECT, PRIORITISE, TRACE, REFLECT];
21792
22797
  var UPG_APPROACHES_BY_ID = Object.fromEntries(
@@ -23817,7 +24822,7 @@ var NODE_KEY_ORDER = [
23817
24822
  "sort_order",
23818
24823
  "properties"
23819
24824
  ];
23820
- var EDGE_KEY_ORDER = ["id", "source", "target", "type", "mapping_confidence"];
24825
+ var EDGE_KEY_ORDER = ["id", "source", "target", "type", "mapping_confidence", "properties"];
23821
24826
  var CROSS_EDGE_KEY_ORDER = ["id", "source", "target", "type", "source_product_id", "target_product_id", "mapping_confidence"];
23822
24827
  var PRODUCT_KEY_ORDER = ["id", "title", "description", "stage", "properties"];
23823
24828
  function canonicalNode(node) {
@@ -23829,7 +24834,8 @@ function canonicalNode(node) {
23829
24834
  }
23830
24835
  function canonicalEdge(edge) {
23831
24836
  return orderedObject(edge, EDGE_KEY_ORDER, {
23832
- forceKeys: ["id", "source", "target", "type"]
24837
+ forceKeys: ["id", "source", "target", "type"],
24838
+ openKeys: ["properties"]
23833
24839
  });
23834
24840
  }
23835
24841
  function canonicalCrossEdge(edge) {
@@ -23983,7 +24989,7 @@ function isCanonical(text) {
23983
24989
  }
23984
24990
 
23985
24991
  // src/index.ts
23986
- var UPG_VERSION = "0.8.0";
24992
+ var UPG_VERSION = "0.8.4";
23987
24993
  var UPG_FORMAT_VERSION = "0.4.0";
23988
24994
  var MARKDOWN_FORMAT_VERSION = "0.1";
23989
24995
  var UPG_TYPES = getTypes();
@@ -24132,6 +25138,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
24132
25138
  collectSlugsForType,
24133
25139
  compareVersions,
24134
25140
  computeBodyChecksum,
25141
+ edgeCarriesProperties,
24135
25142
  evaluateAntiPatterns,
24136
25143
  findInvalidStatusMigrationTargets,
24137
25144
  formatUpgText,