@unified-product-graph/core 0.10.1 → 0.10.3

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
@@ -1353,7 +1353,17 @@ var UPG_EDGE_CATALOG = {
1353
1353
  // would be a separate catalog-mechanic change).
1354
1354
  competitive_analysis_dimensioned_by_classification_axis: { forward_verb: "dimensioned_by", reverse_verb: "dimensions", classification: "hierarchy", source_type: "competitive_analysis", target_type: "classification_axis" },
1355
1355
  classification_axis_includes_classification_value: { forward_verb: "includes", reverse_verb: "value_of", classification: "hierarchy", source_type: "classification_axis", target_type: "classification_value" },
1356
+ // 0.10.2: also dual-registered as a cross-edge (UPG_CROSS_EDGE_TYPES) so a
1357
+ // competitor can be classified directly against a `registry/{classification_value}`
1358
+ // canonical, eliminating the per-graph taxonomy node. Within-graph (the catalogue
1359
+ // case) here; cross-product against the registry canonical via create_cross_product_edge.
1356
1360
  competitor_classified_as_classification_value: { forward_verb: "classified_as", reverse_verb: "classification_of", classification: "semantic", source_type: "competitor", target_type: "classification_value" },
1361
+ // 0.10.3: the polymorphic, type-agnostic sibling. ANY node (a feature, a
1362
+ // product, a market_segment) classified against a classification_value, so
1363
+ // classification is not welded to the competitor type. Also dual-registered as
1364
+ // a cross-edge for the registry-canonical case. source_type:'node' (wildcard);
1365
+ // listed in UPG_POLYMORPHIC_EDGE_KEYS.
1366
+ node_classified_as_classification_value: { forward_verb: "classified_as", reverse_verb: "classification_of", classification: "semantic", source_type: "node", target_type: "classification_value" },
1357
1367
  persona_anti_fit_for_classification_value: { forward_verb: "is_anti_fit_for", reverse_verb: "should_not_be_picked_by", classification: "semantic", source_type: "persona", target_type: "classification_value" },
1358
1368
  persona_anti_fit_for_product: { forward_verb: "is_anti_fit_for", reverse_verb: "should_not_be_picked_by", classification: "semantic", source_type: "persona", target_type: "product" },
1359
1369
  persona_anti_fit_for_competitor: { forward_verb: "is_anti_fit_for", reverse_verb: "should_not_be_picked_by", classification: "semantic", source_type: "persona", target_type: "competitor" },
@@ -3191,7 +3201,9 @@ var UPG_POLYMORPHIC_EDGE_KEYS = [
3191
3201
  // Universal architecture references
3192
3202
  "node_belongs_to_bounded_context",
3193
3203
  // Framework exercises (an exercise can include any entity type)
3194
- "framework_exercise_includes_node"
3204
+ "framework_exercise_includes_node",
3205
+ // Universal classification (any node classified against a classification_value)
3206
+ "node_classified_as_classification_value"
3195
3207
  ];
3196
3208
  var _POLY_KEY_SET = new Set(UPG_POLYMORPHIC_EDGE_KEYS);
3197
3209
  function isPolymorphicEdge(key) {
@@ -3248,7 +3260,9 @@ var UPG_CROSS_EDGE_TYPES = [
3248
3260
  "primitive_stored_as_data_type",
3249
3261
  "feature_rivals_competitor_feature",
3250
3262
  "competitor_signal_maps_to_feature",
3251
- "competitor_signal_surfaces_opportunity"
3263
+ "competitor_signal_surfaces_opportunity",
3264
+ "competitor_classified_as_classification_value",
3265
+ "node_classified_as_classification_value"
3252
3266
  ];
3253
3267
  var REGISTRY_PRODUCT_ID = "registry";
3254
3268
 
@@ -7458,6 +7472,34 @@ function getDeprecatedTypes() {
7458
7472
  return deprecated;
7459
7473
  }
7460
7474
  var UPG_PROPERTY_MIGRATIONS = {
7475
+ // ── v0.10.2: market_intelligence number -> assessment reshape ──────────────
7476
+ //
7477
+ // The v0.10.0 property-registry generator fix (#43) corrected several property
7478
+ // types that interfaces already declared as `UPGAssessment` but the generator
7479
+ // had emitted loosely. A pre-0.10.0 graph that stored a bare number for
7480
+ // `market_trend.impact` / `relevance` is now invalid (the schema wants
7481
+ // `{value,label}`). These rules wrap the bare number on its canonical scale so
7482
+ // the migration is one command, not a hand-reshape. The enum tightenings the
7483
+ // same #43 fix surfaced (data_flow.direction, integration_pattern.pattern_type,
7484
+ // api_contract.protocol, service.service_type) already have rules under 0.9.12;
7485
+ // 0.10.2 also makes ALL of them visible to validate_graph (they were applied by
7486
+ // migrate_properties but never surfaced as a fixable drift).
7487
+ "0.10.2": [
7488
+ {
7489
+ kind: "reshape_value_to_assessment",
7490
+ type: "market_trend",
7491
+ property: "impact",
7492
+ scale_id: "impact_5",
7493
+ reason: "market_trend.impact is a UPGAssessment (impact_5); the 0.10.0 generator fix (#43) tightened it from a bare number. A legacy numeric value wraps to {value,label} on impact_5."
7494
+ },
7495
+ {
7496
+ kind: "reshape_value_to_assessment",
7497
+ type: "market_trend",
7498
+ property: "relevance",
7499
+ scale_id: "importance_5",
7500
+ reason: "market_trend.relevance is a UPGAssessment (importance_5); the 0.10.0 generator fix (#43) tightened it from a bare number. A legacy numeric value wraps to {value,label} on importance_5."
7501
+ }
7502
+ ],
7461
7503
  // ── v0.9.14: drop the key_result kr_status / status twin (A2) ──────────────
7462
7504
  //
7463
7505
  // key_result carried BOTH a lifecycle `status` and a `kr_status` property with
@@ -8030,6 +8072,26 @@ function migrateNodeProperties(node, fromVersion, toVersion) {
8030
8072
  mutated = true;
8031
8073
  break;
8032
8074
  }
8075
+ case "reshape_value_to_assessment": {
8076
+ const props = workingNode.properties;
8077
+ if (!props || !(m.property in props)) break;
8078
+ const oldValue = props[m.property];
8079
+ let num = null;
8080
+ if (typeof oldValue === "number" && Number.isFinite(oldValue)) num = oldValue;
8081
+ else if (typeof oldValue === "string" && oldValue.trim() !== "" && Number.isFinite(Number(oldValue))) {
8082
+ num = Number(oldValue);
8083
+ }
8084
+ if (num === null) break;
8085
+ const label = getScale(m.scale_id)?.points.find((p) => p.value === num)?.label ?? String(num);
8086
+ const nextProps = {
8087
+ ...props,
8088
+ [m.property]: { value: num, label, scale_id: m.scale_id }
8089
+ };
8090
+ workingNode.properties = nextProps;
8091
+ mutated = true;
8092
+ changes.push({ kind: "reshaped_to_assessment", property: m.property, scale_id: m.scale_id, value: num, label });
8093
+ break;
8094
+ }
8033
8095
  }
8034
8096
  }
8035
8097
  }
@@ -28584,7 +28646,7 @@ function isCanonical(text) {
28584
28646
  }
28585
28647
 
28586
28648
  // src/index.ts
28587
- var UPG_VERSION = "0.10.1";
28649
+ var UPG_VERSION = "0.10.3";
28588
28650
  var UPG_FORMAT_VERSION = "0.4.0";
28589
28651
  var MARKDOWN_FORMAT_VERSION = "0.1";
28590
28652
  var UPG_TYPES = getTypes();