@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.d.cts CHANGED
@@ -666,6 +666,13 @@ declare const UPG_EDGE_CATALOG: {
666
666
  source_type: string;
667
667
  target_type: string;
668
668
  };
669
+ node_classified_as_classification_value: {
670
+ forward_verb: string;
671
+ reverse_verb: string;
672
+ classification: "semantic";
673
+ source_type: string;
674
+ target_type: string;
675
+ };
669
676
  persona_anti_fit_for_classification_value: {
670
677
  forward_verb: string;
671
678
  reverse_verb: string;
@@ -7208,7 +7215,7 @@ interface UPGProduct {
7208
7215
  /** Product lifecycle stages. Covers the full arc from napkin idea to end-of-life. */
7209
7216
  type UPGProductStage = 'concept' | 'validation' | 'build' | 'beta' | 'launch' | 'growth' | 'mature' | 'maintenance' | 'sunset';
7210
7217
  /** The set of valid cross-product relationship types */
7211
- type UPGCrossEdgeType = 'shares_persona' | 'shares_competitor' | 'shares_metric' | 'depends_on_product' | 'cannibalises' | 'succeeds' | 'hosts' | 'contributes_to' | 'instance_of' | 'area_serves_persona' | 'area_targets_market_segment' | 'rolls_up_to' | 'product_implements_specification' | 'product_exposes_specification' | 'feature_conforms_to_specification' | 'api_contract_speaks_specification' | 'product_exposes_primitive' | 'feature_manipulates_primitive' | 'primitive_stored_as_data_type' | 'feature_rivals_competitor_feature' | 'competitor_signal_maps_to_feature' | 'competitor_signal_surfaces_opportunity';
7218
+ type UPGCrossEdgeType = 'shares_persona' | 'shares_competitor' | 'shares_metric' | 'depends_on_product' | 'cannibalises' | 'succeeds' | 'hosts' | 'contributes_to' | 'instance_of' | 'area_serves_persona' | 'area_targets_market_segment' | 'rolls_up_to' | 'product_implements_specification' | 'product_exposes_specification' | 'feature_conforms_to_specification' | 'api_contract_speaks_specification' | 'product_exposes_primitive' | 'feature_manipulates_primitive' | 'primitive_stored_as_data_type' | 'feature_rivals_competitor_feature' | 'competitor_signal_maps_to_feature' | 'competitor_signal_surfaces_opportunity' | 'competitor_classified_as_classification_value' | 'node_classified_as_classification_value';
7212
7219
  /**
7213
7220
  * Runtime-checkable list of valid cross-product edge types. Mirrors
7214
7221
  * `UPGCrossEdgeType` for use in validators and writers that need to test
@@ -8657,6 +8664,25 @@ type UPGPropertyMigration = {
8657
8664
  reset_value?: string;
8658
8665
  /** Human-readable explanation surfaced in load-time warnings. */
8659
8666
  reason: string;
8667
+ } | {
8668
+ /**
8669
+ * Reshape a bare numeric property into a `UPGAssessment` object
8670
+ * (`{ value, label, scale_id }`). For the v0.10.0 property-registry
8671
+ * correction (#43): interfaces that always declared `UPGAssessment` were
8672
+ * generated loosely, so pre-0.10.0 graphs stored a bare number (e.g.
8673
+ * `market_trend.impact: 4`). This wraps that number on its canonical scale,
8674
+ * deriving the qualitative `label` from the scale's points. Idempotent: a
8675
+ * value that is already an object (assessment) passes through untouched.
8676
+ */
8677
+ kind: 'reshape_value_to_assessment';
8678
+ /** The entity type this rule applies to (or `'*'` for all types). */
8679
+ type: string;
8680
+ /** Key inside `properties` whose bare number is wrapped (stays in the bag). */
8681
+ property: string;
8682
+ /** The assessment scale to label the value on (e.g. `impact_5`, `importance_5`). */
8683
+ scale_id: string;
8684
+ /** Human-readable explanation surfaced in load-time warnings. */
8685
+ reason: string;
8660
8686
  };
8661
8687
  /**
8662
8688
  * Version-scoped property migrations. Same convention as `UPG_MIGRATIONS`:
@@ -8688,6 +8714,12 @@ type UPGPropertyMigrationChange = {
8688
8714
  property: string;
8689
8715
  to_property?: string;
8690
8716
  value_changed: boolean;
8717
+ } | {
8718
+ kind: 'reshaped_to_assessment';
8719
+ property: string;
8720
+ scale_id: string;
8721
+ value: number;
8722
+ label: string;
8691
8723
  };
8692
8724
  /**
8693
8725
  * Apply property migrations to a single node. Returns the (possibly new) node
@@ -21413,7 +21445,7 @@ declare function isCanonical(text: string): boolean;
21413
21445
  * it stamps the `upg_version` field of every `.upg` file written by the SDK.
21414
21446
  * The `check:version-lockstep` gate enforces this at release time.
21415
21447
  */
21416
- declare const UPG_VERSION: "0.10.1";
21448
+ declare const UPG_VERSION: "0.10.3";
21417
21449
  /**
21418
21450
  * The `.upg` JSON document format version. Written to the `upg_version` field.
21419
21451
  * Evolves independently from `UPG_VERSION` (the catalogue version).
package/dist/index.d.ts CHANGED
@@ -666,6 +666,13 @@ declare const UPG_EDGE_CATALOG: {
666
666
  source_type: string;
667
667
  target_type: string;
668
668
  };
669
+ node_classified_as_classification_value: {
670
+ forward_verb: string;
671
+ reverse_verb: string;
672
+ classification: "semantic";
673
+ source_type: string;
674
+ target_type: string;
675
+ };
669
676
  persona_anti_fit_for_classification_value: {
670
677
  forward_verb: string;
671
678
  reverse_verb: string;
@@ -7208,7 +7215,7 @@ interface UPGProduct {
7208
7215
  /** Product lifecycle stages. Covers the full arc from napkin idea to end-of-life. */
7209
7216
  type UPGProductStage = 'concept' | 'validation' | 'build' | 'beta' | 'launch' | 'growth' | 'mature' | 'maintenance' | 'sunset';
7210
7217
  /** The set of valid cross-product relationship types */
7211
- type UPGCrossEdgeType = 'shares_persona' | 'shares_competitor' | 'shares_metric' | 'depends_on_product' | 'cannibalises' | 'succeeds' | 'hosts' | 'contributes_to' | 'instance_of' | 'area_serves_persona' | 'area_targets_market_segment' | 'rolls_up_to' | 'product_implements_specification' | 'product_exposes_specification' | 'feature_conforms_to_specification' | 'api_contract_speaks_specification' | 'product_exposes_primitive' | 'feature_manipulates_primitive' | 'primitive_stored_as_data_type' | 'feature_rivals_competitor_feature' | 'competitor_signal_maps_to_feature' | 'competitor_signal_surfaces_opportunity';
7218
+ type UPGCrossEdgeType = 'shares_persona' | 'shares_competitor' | 'shares_metric' | 'depends_on_product' | 'cannibalises' | 'succeeds' | 'hosts' | 'contributes_to' | 'instance_of' | 'area_serves_persona' | 'area_targets_market_segment' | 'rolls_up_to' | 'product_implements_specification' | 'product_exposes_specification' | 'feature_conforms_to_specification' | 'api_contract_speaks_specification' | 'product_exposes_primitive' | 'feature_manipulates_primitive' | 'primitive_stored_as_data_type' | 'feature_rivals_competitor_feature' | 'competitor_signal_maps_to_feature' | 'competitor_signal_surfaces_opportunity' | 'competitor_classified_as_classification_value' | 'node_classified_as_classification_value';
7212
7219
  /**
7213
7220
  * Runtime-checkable list of valid cross-product edge types. Mirrors
7214
7221
  * `UPGCrossEdgeType` for use in validators and writers that need to test
@@ -8657,6 +8664,25 @@ type UPGPropertyMigration = {
8657
8664
  reset_value?: string;
8658
8665
  /** Human-readable explanation surfaced in load-time warnings. */
8659
8666
  reason: string;
8667
+ } | {
8668
+ /**
8669
+ * Reshape a bare numeric property into a `UPGAssessment` object
8670
+ * (`{ value, label, scale_id }`). For the v0.10.0 property-registry
8671
+ * correction (#43): interfaces that always declared `UPGAssessment` were
8672
+ * generated loosely, so pre-0.10.0 graphs stored a bare number (e.g.
8673
+ * `market_trend.impact: 4`). This wraps that number on its canonical scale,
8674
+ * deriving the qualitative `label` from the scale's points. Idempotent: a
8675
+ * value that is already an object (assessment) passes through untouched.
8676
+ */
8677
+ kind: 'reshape_value_to_assessment';
8678
+ /** The entity type this rule applies to (or `'*'` for all types). */
8679
+ type: string;
8680
+ /** Key inside `properties` whose bare number is wrapped (stays in the bag). */
8681
+ property: string;
8682
+ /** The assessment scale to label the value on (e.g. `impact_5`, `importance_5`). */
8683
+ scale_id: string;
8684
+ /** Human-readable explanation surfaced in load-time warnings. */
8685
+ reason: string;
8660
8686
  };
8661
8687
  /**
8662
8688
  * Version-scoped property migrations. Same convention as `UPG_MIGRATIONS`:
@@ -8688,6 +8714,12 @@ type UPGPropertyMigrationChange = {
8688
8714
  property: string;
8689
8715
  to_property?: string;
8690
8716
  value_changed: boolean;
8717
+ } | {
8718
+ kind: 'reshaped_to_assessment';
8719
+ property: string;
8720
+ scale_id: string;
8721
+ value: number;
8722
+ label: string;
8691
8723
  };
8692
8724
  /**
8693
8725
  * Apply property migrations to a single node. Returns the (possibly new) node
@@ -21413,7 +21445,7 @@ declare function isCanonical(text: string): boolean;
21413
21445
  * it stamps the `upg_version` field of every `.upg` file written by the SDK.
21414
21446
  * The `check:version-lockstep` gate enforces this at release time.
21415
21447
  */
21416
- declare const UPG_VERSION: "0.10.1";
21448
+ declare const UPG_VERSION: "0.10.3";
21417
21449
  /**
21418
21450
  * The `.upg` JSON document format version. Written to the `upg_version` field.
21419
21451
  * Evolves independently from `UPG_VERSION` (the catalogue version).
package/dist/index.js CHANGED
@@ -1145,7 +1145,17 @@ var UPG_EDGE_CATALOG = {
1145
1145
  // would be a separate catalog-mechanic change).
1146
1146
  competitive_analysis_dimensioned_by_classification_axis: { forward_verb: "dimensioned_by", reverse_verb: "dimensions", classification: "hierarchy", source_type: "competitive_analysis", target_type: "classification_axis" },
1147
1147
  classification_axis_includes_classification_value: { forward_verb: "includes", reverse_verb: "value_of", classification: "hierarchy", source_type: "classification_axis", target_type: "classification_value" },
1148
+ // 0.10.2: also dual-registered as a cross-edge (UPG_CROSS_EDGE_TYPES) so a
1149
+ // competitor can be classified directly against a `registry/{classification_value}`
1150
+ // canonical, eliminating the per-graph taxonomy node. Within-graph (the catalogue
1151
+ // case) here; cross-product against the registry canonical via create_cross_product_edge.
1148
1152
  competitor_classified_as_classification_value: { forward_verb: "classified_as", reverse_verb: "classification_of", classification: "semantic", source_type: "competitor", target_type: "classification_value" },
1153
+ // 0.10.3: the polymorphic, type-agnostic sibling. ANY node (a feature, a
1154
+ // product, a market_segment) classified against a classification_value, so
1155
+ // classification is not welded to the competitor type. Also dual-registered as
1156
+ // a cross-edge for the registry-canonical case. source_type:'node' (wildcard);
1157
+ // listed in UPG_POLYMORPHIC_EDGE_KEYS.
1158
+ node_classified_as_classification_value: { forward_verb: "classified_as", reverse_verb: "classification_of", classification: "semantic", source_type: "node", target_type: "classification_value" },
1149
1159
  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" },
1150
1160
  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" },
1151
1161
  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" },
@@ -2983,7 +2993,9 @@ var UPG_POLYMORPHIC_EDGE_KEYS = [
2983
2993
  // Universal architecture references
2984
2994
  "node_belongs_to_bounded_context",
2985
2995
  // Framework exercises (an exercise can include any entity type)
2986
- "framework_exercise_includes_node"
2996
+ "framework_exercise_includes_node",
2997
+ // Universal classification (any node classified against a classification_value)
2998
+ "node_classified_as_classification_value"
2987
2999
  ];
2988
3000
  var _POLY_KEY_SET = new Set(UPG_POLYMORPHIC_EDGE_KEYS);
2989
3001
  function isPolymorphicEdge(key) {
@@ -3040,7 +3052,9 @@ var UPG_CROSS_EDGE_TYPES = [
3040
3052
  "primitive_stored_as_data_type",
3041
3053
  "feature_rivals_competitor_feature",
3042
3054
  "competitor_signal_maps_to_feature",
3043
- "competitor_signal_surfaces_opportunity"
3055
+ "competitor_signal_surfaces_opportunity",
3056
+ "competitor_classified_as_classification_value",
3057
+ "node_classified_as_classification_value"
3044
3058
  ];
3045
3059
  var REGISTRY_PRODUCT_ID = "registry";
3046
3060
 
@@ -7250,6 +7264,34 @@ function getDeprecatedTypes() {
7250
7264
  return deprecated;
7251
7265
  }
7252
7266
  var UPG_PROPERTY_MIGRATIONS = {
7267
+ // ── v0.10.2: market_intelligence number -> assessment reshape ──────────────
7268
+ //
7269
+ // The v0.10.0 property-registry generator fix (#43) corrected several property
7270
+ // types that interfaces already declared as `UPGAssessment` but the generator
7271
+ // had emitted loosely. A pre-0.10.0 graph that stored a bare number for
7272
+ // `market_trend.impact` / `relevance` is now invalid (the schema wants
7273
+ // `{value,label}`). These rules wrap the bare number on its canonical scale so
7274
+ // the migration is one command, not a hand-reshape. The enum tightenings the
7275
+ // same #43 fix surfaced (data_flow.direction, integration_pattern.pattern_type,
7276
+ // api_contract.protocol, service.service_type) already have rules under 0.9.12;
7277
+ // 0.10.2 also makes ALL of them visible to validate_graph (they were applied by
7278
+ // migrate_properties but never surfaced as a fixable drift).
7279
+ "0.10.2": [
7280
+ {
7281
+ kind: "reshape_value_to_assessment",
7282
+ type: "market_trend",
7283
+ property: "impact",
7284
+ scale_id: "impact_5",
7285
+ 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."
7286
+ },
7287
+ {
7288
+ kind: "reshape_value_to_assessment",
7289
+ type: "market_trend",
7290
+ property: "relevance",
7291
+ scale_id: "importance_5",
7292
+ 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."
7293
+ }
7294
+ ],
7253
7295
  // ── v0.9.14: drop the key_result kr_status / status twin (A2) ──────────────
7254
7296
  //
7255
7297
  // key_result carried BOTH a lifecycle `status` and a `kr_status` property with
@@ -7822,6 +7864,26 @@ function migrateNodeProperties(node, fromVersion, toVersion) {
7822
7864
  mutated = true;
7823
7865
  break;
7824
7866
  }
7867
+ case "reshape_value_to_assessment": {
7868
+ const props = workingNode.properties;
7869
+ if (!props || !(m.property in props)) break;
7870
+ const oldValue = props[m.property];
7871
+ let num = null;
7872
+ if (typeof oldValue === "number" && Number.isFinite(oldValue)) num = oldValue;
7873
+ else if (typeof oldValue === "string" && oldValue.trim() !== "" && Number.isFinite(Number(oldValue))) {
7874
+ num = Number(oldValue);
7875
+ }
7876
+ if (num === null) break;
7877
+ const label = getScale(m.scale_id)?.points.find((p) => p.value === num)?.label ?? String(num);
7878
+ const nextProps = {
7879
+ ...props,
7880
+ [m.property]: { value: num, label, scale_id: m.scale_id }
7881
+ };
7882
+ workingNode.properties = nextProps;
7883
+ mutated = true;
7884
+ changes.push({ kind: "reshaped_to_assessment", property: m.property, scale_id: m.scale_id, value: num, label });
7885
+ break;
7886
+ }
7825
7887
  }
7826
7888
  }
7827
7889
  }
@@ -28376,7 +28438,7 @@ function isCanonical(text) {
28376
28438
  }
28377
28439
 
28378
28440
  // src/index.ts
28379
- var UPG_VERSION = "0.10.1";
28441
+ var UPG_VERSION = "0.10.3";
28380
28442
  var UPG_FORMAT_VERSION = "0.4.0";
28381
28443
  var MARKDOWN_FORMAT_VERSION = "0.1";
28382
28444
  var UPG_TYPES = getTypes();