@unified-product-graph/core 0.17.3 → 0.17.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/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  The TypeScript core for the **Unified Product Graph (UPG)**, an open specification that gives product tools a shared vocabulary for entities, edges, and frameworks.
9
9
 
10
- 318 entity types across 37 domains. 1017 typed edges. 46 canonical framework definitions, with more added as each is reviewed.
10
+ 319 entity types across 37 domains. 1024 typed edges. 46 canonical framework definitions, with more added as each is reviewed.
11
11
 
12
12
  ## Install
13
13
 
package/dist/index.cjs CHANGED
@@ -39,6 +39,7 @@ __export(index_exports, {
39
39
  COVERAGE_ANTI_PATTERNS: () => COVERAGE_ANTI_PATTERNS,
40
40
  DEFAULT_ENTITY_EMOJI: () => DEFAULT_ENTITY_EMOJI,
41
41
  DEFERRED_PROPOSED_BY_CONTRACT: () => DEFERRED_PROPOSED_BY_CONTRACT,
42
+ DEFER_EDGE_PROPERTY_SCHEMA: () => DEFER_EDGE_PROPERTY_SCHEMA,
42
43
  DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK: () => DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK,
43
44
  ENGINEERING_PLATFORM_PLAYBOOK: () => ENGINEERING_PLATFORM_PLAYBOOK,
44
45
  ENTITY_EMOJI: () => ENTITY_EMOJI,
@@ -70,6 +71,7 @@ __export(index_exports, {
70
71
  UPG_CROSS_EDGE_TYPES: () => UPG_CROSS_EDGE_TYPES,
71
72
  UPG_CROSS_ELIGIBLE_CATALOG_EDGE_TYPES: () => UPG_CROSS_ELIGIBLE_CATALOG_EDGE_TYPES,
72
73
  UPG_CROSS_ONLY_EDGE_TYPES: () => UPG_CROSS_ONLY_EDGE_TYPES,
74
+ UPG_DELIBERATE_ONLY_EDGE_TYPES: () => UPG_DELIBERATE_ONLY_EDGE_TYPES,
73
75
  UPG_DEPRECATED_TYPES: () => UPG_DEPRECATED_TYPES,
74
76
  UPG_DOMAINS: () => UPG_DOMAINS,
75
77
  UPG_DOMAIN_ACTIVATION: () => UPG_DOMAIN_ACTIVATION,
@@ -201,6 +203,7 @@ __export(index_exports, {
201
203
  isCanonicalProductStage: () => isCanonicalProductStage,
202
204
  isContainmentFreeType: () => isContainmentFreeType,
203
205
  isCrossProductEligible: () => isCrossProductEligible,
206
+ isDeliberateOnlyEdge: () => isDeliberateOnlyEdge,
204
207
  isDeprecatedType: () => isDeprecatedType,
205
208
  isDomainGuideStep: () => isDomainGuideStep,
206
209
  isEntitySequenceStep: () => isEntitySequenceStep,
@@ -267,7 +270,8 @@ var UPG_DOMAINS = [
267
270
  "strategic_pillar",
268
271
  "assumption",
269
272
  "decision",
270
- "constraint"
273
+ "constraint",
274
+ "strategic_question"
271
275
  ]
272
276
  },
273
277
  {
@@ -786,6 +790,10 @@ var UPG_ENTITY_META = [
786
790
  { name: "assumption", type_id: "ent_014", maturity: "stable", since: "0.1.0" },
787
791
  { name: "decision", type_id: "ent_015", maturity: "stable", since: "0.1.0" },
788
792
  { name: "constraint", type_id: "ent_348", maturity: "stable", since: "0.4.3" },
793
+ // (0.17.4) Strategy-domain sibling of research_question / design_question,
794
+ // completing the domain-question triad. An open coordination/ownership question
795
+ // a plan is exposed to (distinct from `assumption`, which the plan is built on).
796
+ { name: "strategic_question", type_id: "ent_357", maturity: "proposed", since: "0.17.4" },
789
797
  // ── User ──
790
798
  { name: "persona", type_id: "ent_016", maturity: "stable", since: "0.1.0" },
791
799
  { name: "job", type_id: "ent_017", maturity: "stable", since: "0.1.0" },
@@ -1260,6 +1268,12 @@ var CLASSIFICATION_EDGE_PROPERTY_SCHEMA = {
1260
1268
  description: "Optional. A source URL, or a competitor_signal / evidence node id backing the classification. Mirrors feature_rivals_competitor_feature.evidence (free text or a node id)."
1261
1269
  }
1262
1270
  };
1271
+ var DEFER_EDGE_PROPERTY_SCHEMA = {
1272
+ deferred_to: {
1273
+ type: "string",
1274
+ description: 'Target period the parked work is deferred to. Freeform, mirroring strategic_theme.time_horizon. @example "Q4 2026", "FY27", "next release".'
1275
+ }
1276
+ };
1263
1277
  var UPG_EDGE_CATALOG = {
1264
1278
  // ── Part 1: Core Narrative Spine (Ring 1) ──────────────────────────────────
1265
1279
  // 1.1 User Domain
@@ -3334,7 +3348,45 @@ var UPG_EDGE_CATALOG = {
3334
3348
  // Operating-lifecycle cross-edges (0.11.6): a product's journey phase realises a canonical
3335
3349
  // operating stage (the cross-surface join key); a stage is measured by a rollup metric (loop close).
3336
3350
  journey_phase_realises_operating_stage: { forward_verb: "realises", reverse_verb: "realised_by", classification: "cross-domain", source_type: "journey_phase", target_type: "operating_stage", cross_product_eligible: true },
3337
- operating_stage_measured_by_metric: { forward_verb: "measured_by", reverse_verb: "measures", classification: "cross-domain", source_type: "operating_stage", target_type: "metric" }
3351
+ operating_stage_measured_by_metric: { forward_verb: "measured_by", reverse_verb: "measures", classification: "cross-domain", source_type: "operating_stage", target_type: "metric" },
3352
+ // ── OKR planning coverage (0.17.4) ──────────────────────────────────────────
3353
+ // Reconciling the strategy graph against a live planning doc surfaced texture
3354
+ // the objective — the node every OKR doc organises itself around — could not
3355
+ // reach: cross-team dependencies and deferred (out-of-scope) work.
3356
+ // objective ↔ dependency. Every OKR in the source doc carries a dependency
3357
+ // table ("which other team's work this depends on"). `dependency` (team_org)
3358
+ // already carries dependency_type / criticality / target_date; it was
3359
+ // reachable only from `team`. An objective DEPENDS ON a dependency; the
3360
+ // dependency BLOCKS the objective (explicit mirror, not left implicit).
3361
+ // Cross-domain, not hierarchy: a dependency is parented under its owning team,
3362
+ // so an objective references it laterally rather than containing it. Both are
3363
+ // cross_product_eligible — a Studio objective can depend on a dependency owned
3364
+ // by another team's graph within the portfolio (0.17.3 locked predicate).
3365
+ objective_depends_on_dependency: { forward_verb: "depends_on", reverse_verb: "dependency_of", classification: "cross-domain", source_type: "objective", target_type: "dependency", cross_product_eligible: true },
3366
+ dependency_blocks_objective: { forward_verb: "blocks", reverse_verb: "blocked_by", classification: "cross-domain", source_type: "dependency", target_type: "objective", cross_product_eligible: true },
3367
+ // objective / initiative → strategic_question. A planning doc's "Risks & Open
3368
+ // Questions" section names unresolved coordination questions the plan is
3369
+ // exposed to (who owns a capability across teams after a reorg). The
3370
+ // strategy-domain sibling of research_question / design_question. The question
3371
+ // is raised under (contained by) the objective or initiative that surfaces it,
3372
+ // so hierarchy — mirroring initiative_assumes_assumption. Within-graph: the
3373
+ // question node is authored alongside its objective, so NOT
3374
+ // cross_product_eligible (the cross-team nature lives in the question text, not
3375
+ // in a cross-graph edge).
3376
+ objective_raises_strategic_question: { forward_verb: "raises", reverse_verb: "raised_by", classification: "hierarchy", source_type: "objective", target_type: "strategic_question" },
3377
+ initiative_raises_strategic_question: { forward_verb: "raises", reverse_verb: "raised_by", classification: "hierarchy", source_type: "initiative", target_type: "strategic_question" },
3378
+ // objective → feature / capability (the defer / out-of-scope edge). Every OKR
3379
+ // in the source doc carries an explicit "Out of scope" list, load-bearing for
3380
+ // keeping a quarter from silently expanding (localization deferred to Q4,
3381
+ // another team's feature work parked). Modelled as an edge to the real feature
3382
+ // or capability parked for later, not a scalar exclusion string, so the parked
3383
+ // work stays queryable and connected. The `deferred_to` edge property carries
3384
+ // the temporal target. feature is product_spec (cross-domain); capability is
3385
+ // intra-strategy (semantic). Both cross_product_eligible: deferring another
3386
+ // product's feature or capability, authored in a different graph within the
3387
+ // portfolio, fits the 0.17.3 locked predicate.
3388
+ objective_defers_feature: { forward_verb: "defers", reverse_verb: "deferred_by", classification: "cross-domain", source_type: "objective", target_type: "feature", carries_properties: true, property_schema: DEFER_EDGE_PROPERTY_SCHEMA, cross_product_eligible: true, deliberate_only: true },
3389
+ objective_defers_capability: { forward_verb: "defers", reverse_verb: "deferred_by", classification: "semantic", source_type: "objective", target_type: "capability", carries_properties: true, property_schema: DEFER_EDGE_PROPERTY_SCHEMA, cross_product_eligible: true, deliberate_only: true }
3338
3390
  };
3339
3391
  var UPG_CROSS_ELIGIBLE_CATALOG_EDGE_TYPES = Object.keys(UPG_EDGE_CATALOG).filter(
3340
3392
  (k) => UPG_EDGE_CATALOG[k].cross_product_eligible === true
@@ -3343,6 +3395,13 @@ function isCrossProductEligible(type) {
3343
3395
  const def = UPG_EDGE_CATALOG[type];
3344
3396
  return def?.cross_product_eligible === true;
3345
3397
  }
3398
+ var UPG_DELIBERATE_ONLY_EDGE_TYPES = Object.keys(UPG_EDGE_CATALOG).filter(
3399
+ (k) => UPG_EDGE_CATALOG[k].deliberate_only === true
3400
+ );
3401
+ function isDeliberateOnlyEdge(type) {
3402
+ const def = UPG_EDGE_CATALOG[type];
3403
+ return def?.deliberate_only === true;
3404
+ }
3346
3405
  var UPG_POLYMORPHIC_EDGE_KEYS = [
3347
3406
  // Universal semantic verbs
3348
3407
  "node_informs_node",
@@ -3710,7 +3769,9 @@ var UPG_VALID_CHILDREN = {
3710
3769
  // where self-nesting genuinely lives, not on `experiment`).
3711
3770
  experiment_run: ["learning", "evidence", "metric", "experiment_run"],
3712
3771
  // ── OKR hierarchy ──────────────────────────────────────────────────────────
3713
- objective: ["key_result", "metric"],
3772
+ // strategic_question (0.17.4): an open coordination question raised under an
3773
+ // objective (objective_raises_strategic_question, hierarchy).
3774
+ objective: ["key_result", "metric", "strategic_question"],
3714
3775
  key_result: ["metric"],
3715
3776
  // ── Strategic cascade: vision → mission → strategic_pillar → strategic_theme →
3716
3777
  // { initiative, objective } ───────────────────────────────────────────────
@@ -3735,7 +3796,7 @@ var UPG_VALID_CHILDREN = {
3735
3796
  // catalog (hierarchy, strategic_theme → objective, reverse_verb: rolls_up_to;
3736
3797
  // renamed from objective_rolls_up_to_strategic_theme in UPG-676).
3737
3798
  strategic_theme: ["initiative", "objective"],
3738
- initiative: ["assumption"],
3799
+ initiative: ["assumption", "strategic_question"],
3739
3800
  // v0.5.2 (UPG-528): capability decomposes into sub-capabilities (Wardley
3740
3801
  // value-chain spine) and is realised by user-facing features. Pairs with
3741
3802
  // `capability_depends_on_capability` and `capability_implemented_by_feature`
@@ -4808,6 +4869,15 @@ var ASSUMPTION_LIFECYCLE = {
4808
4869
  }
4809
4870
  ]
4810
4871
  };
4872
+ var STRATEGIC_QUESTION_LIFECYCLE = {
4873
+ entity_type: "strategic_question",
4874
+ initial_phase: "open",
4875
+ terminal_phases: ["resolved"],
4876
+ phases: [
4877
+ { id: "open", label: "Open", description: "The question has been raised but not yet answered. The plan is exposed to it.", transitions_to: ["resolved"] },
4878
+ { id: "resolved", label: "Resolved", description: "The question has been answered; the resolution is captured. The plan is no longer exposed to it. May reopen if the answer unravels.", transitions_to: ["open"] }
4879
+ ]
4880
+ };
4811
4881
  var OUTCOME_LIFECYCLE = {
4812
4882
  entity_type: "outcome",
4813
4883
  initial_phase: "identified",
@@ -6485,6 +6555,7 @@ var UPG_LIFECYCLES = [
6485
6555
  INITIATIVE_LIFECYCLE,
6486
6556
  STRATEGIC_PILLAR_LIFECYCLE,
6487
6557
  ASSUMPTION_LIFECYCLE,
6558
+ STRATEGIC_QUESTION_LIFECYCLE,
6488
6559
  FEATURE_AREA_LIFECYCLE,
6489
6560
  FEATURE_LIFECYCLE,
6490
6561
  EPIC_LIFECYCLE,
@@ -12961,6 +13032,15 @@ var UPG_PROPERTY_SCHEMA = {
12961
13032
  time_horizon: { type: "string", description: 'Standing / multi-year horizon, often open-ended (a pillar is durable). @example "3 years", "2026-2028", "ongoing"' },
12962
13033
  success_indicator: { type: "string", description: "How the business knows this durable pillar is on track. Narrative, not a metric edge. A strategic_theme deliberately has no success_indicator: it is measured through its child objectives, not on its own." }
12963
13034
  },
13035
+ // StrategicQuestionProperties: StrategicQuestion entity. An open coordination or
13036
+ // ownership question a plan is exposed to (strategy sibling of research_question /
13037
+ // design_question). `status` is the node lifecycle (open -> resolved).
13038
+ strategic_question: {
13039
+ question: { type: "string", description: "The question itself. Primary content." },
13040
+ context: { type: "string", description: "Context that surfaced the question: the reorg, the boundary, the unowned area." },
13041
+ resolution: { type: "string", description: "The answer, captured when the question moves to `resolved`." },
13042
+ priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Importance to resolve relative to other open questions." }
13043
+ },
12964
13044
  // StrategicThemeProperties: StrategicTheme entity. A time-bound thrust WITHIN a
12965
13045
  // strategic_pillar (distinct cascade level, UPG-692 T3.3) — measured through its
12966
13046
  // child objectives, so it carries no success_indicator of its own.
@@ -19989,6 +20069,7 @@ var ENTITY_EMOJI = {
19989
20069
  stakeholder: "\u{1F9D1}\u200D\u{1F4BC}",
19990
20070
  status_report: "\u{1F4C4}",
19991
20071
  strategic_pillar: "\u{1F3DB}\uFE0F",
20072
+ strategic_question: "\u2754",
19992
20073
  strategic_theme: "\u{1F9F5}",
19993
20074
  subscription: "\u{1F502}",
19994
20075
  success_milestone: "\u{1F389}",
@@ -20546,6 +20627,7 @@ var STANDARD_LABELS = {
20546
20627
  value_stream: { alt_labels: ["value chain", "stream"] },
20547
20628
  strategic_pillar: { alt_labels: ["pillar", "foundation"] },
20548
20629
  assumption: { alt_labels: ["belief", "working assumption", "premise"] },
20630
+ strategic_question: { alt_labels: ["open question", "coordination question", "ownership question"] },
20549
20631
  // User layer
20550
20632
  job_step: { alt_labels: ["job stage", "job phase", "job map step"] },
20551
20633
  switching_cost: { alt_labels: ["lock-in", "migration cost", "switching barrier"] },
@@ -23090,7 +23172,7 @@ var STRATEGY_GUIDE = {
23090
23172
  // entries are later additions appended in chronological-introduction
23091
23173
  // order. Placement reflects the order in which they would naturally be
23092
23174
  // authored after the canonical strategy spine.
23093
- creation_sequence: ["product", "vision", "mission", "outcome", "objective", "key_result", "metric", "metric_quality_assessment", "strategic_theme", "strategic_pillar", "initiative", "capability", "value_stream", "assumption", "decision", "constraint"],
23175
+ creation_sequence: ["product", "vision", "mission", "outcome", "objective", "key_result", "metric", "metric_quality_assessment", "strategic_theme", "strategic_pillar", "initiative", "capability", "value_stream", "assumption", "strategic_question", "decision", "constraint"],
23094
23176
  patterns: [
23095
23177
  {
23096
23178
  name: "Strategic Cascade",
@@ -23108,13 +23190,16 @@ var STRATEGY_GUIDE = {
23108
23190
  required_bridges: [
23109
23191
  { edge_type: "outcome_reveals_opportunity", target_domain: "discovery", when: "Outcomes should connect to the opportunities that deliver them" },
23110
23192
  { edge_type: "outcome_delivered_by_feature", target_domain: "product_spec", when: "Strategic outcomes should decompose into shipped features" },
23111
- { edge_type: "assumption_becomes_hypothesis", target_domain: "validation", when: "Risky assumptions should become testable hypotheses" }
23193
+ { edge_type: "assumption_becomes_hypothesis", target_domain: "validation", when: "Risky assumptions should become testable hypotheses" },
23194
+ { edge_type: "objective_depends_on_dependency", target_domain: "team_org", when: "An objective that hinges on cross-team work should name the dependency it is exposed to" },
23195
+ { edge_type: "objective_defers_feature", target_domain: "product_spec", when: "Work an objective explicitly puts out of scope for now should point at the deferred feature with a deferred_to period, not silently vanish from the quarter" }
23112
23196
  ],
23113
23197
  anti_patterns: [
23114
23198
  { description: "Outcomes without metrics. Measurement is the signal an outcome happened." },
23115
23199
  { description: "Objectives without key results: an objective without measurement is a wish" },
23116
23200
  { description: "Too many strategic themes: focus beats breadth, aim for 2-4 active themes" },
23117
- { description: "Constraints without an origin: a qualitative guardrail lives on a constraint, so set constraint_origin to say where it comes from. internal marks a self-imposed principle or operating tenet the team commits to; external marks a limit, requirement, or ceiling imposed from outside (a regulation, a budget cap, a platform bound)." }
23201
+ { description: "Constraints without an origin: a qualitative guardrail lives on a constraint, so set constraint_origin to say where it comes from. internal marks a self-imposed principle or operating tenet the team commits to; external marks a limit, requirement, or ceiling imposed from outside (a regulation, a budget cap, a platform bound)." },
23202
+ { description: "Overloading assumption for open questions: an assumption is a premise the plan is built on and resolves by being tested (assumption_becomes_hypothesis). An unresolved coordination or ownership question the plan is exposed to (who owns a capability across teams, where a boundary falls after a reorg) is a strategic_question, raised under the objective or initiative it hangs off. Do not force one into the other." }
23118
23203
  ]
23119
23204
  };
23120
23205
  var PRODUCT_SPEC_GUIDE = {
@@ -27167,6 +27252,10 @@ var UPG_REGIONS = [
27167
27252
  type: "assumption",
27168
27253
  role: "leaf"
27169
27254
  },
27255
+ {
27256
+ type: "strategic_question",
27257
+ role: "leaf"
27258
+ },
27170
27259
  {
27171
27260
  type: "constraint",
27172
27261
  role: "leaf"
@@ -27192,6 +27281,8 @@ var UPG_REGIONS = [
27192
27281
  "strategic_pillar_decided_via_decision",
27193
27282
  "strategic_theme_pursues_initiative",
27194
27283
  "initiative_assumes_assumption",
27284
+ "objective_raises_strategic_question",
27285
+ "initiative_raises_strategic_question",
27195
27286
  "initiative_drives_outcome",
27196
27287
  "objective_achieved_through_key_result",
27197
27288
  "objective_measured_by_metric",
@@ -29876,7 +29967,7 @@ function isCanonical(text) {
29876
29967
  }
29877
29968
 
29878
29969
  // src/index.ts
29879
- var UPG_VERSION = "0.17.3";
29970
+ var UPG_VERSION = "0.17.4";
29880
29971
  var UPG_FORMAT_VERSION = "0.4.0";
29881
29972
  var MARKDOWN_FORMAT_VERSION = "0.1";
29882
29973
  var UPG_TYPES = getTypes();
@@ -29950,6 +30041,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
29950
30041
  COVERAGE_ANTI_PATTERNS,
29951
30042
  DEFAULT_ENTITY_EMOJI,
29952
30043
  DEFERRED_PROPOSED_BY_CONTRACT,
30044
+ DEFER_EDGE_PROPERTY_SCHEMA,
29953
30045
  DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK,
29954
30046
  ENGINEERING_PLATFORM_PLAYBOOK,
29955
30047
  ENTITY_EMOJI,
@@ -29981,6 +30073,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
29981
30073
  UPG_CROSS_EDGE_TYPES,
29982
30074
  UPG_CROSS_ELIGIBLE_CATALOG_EDGE_TYPES,
29983
30075
  UPG_CROSS_ONLY_EDGE_TYPES,
30076
+ UPG_DELIBERATE_ONLY_EDGE_TYPES,
29984
30077
  UPG_DEPRECATED_TYPES,
29985
30078
  UPG_DOMAINS,
29986
30079
  UPG_DOMAIN_ACTIVATION,
@@ -30112,6 +30205,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
30112
30205
  isCanonicalProductStage,
30113
30206
  isContainmentFreeType,
30114
30207
  isCrossProductEligible,
30208
+ isDeliberateOnlyEdge,
30115
30209
  isDeprecatedType,
30116
30210
  isDomainGuideStep,
30117
30211
  isEntitySequenceStep,