@unified-product-graph/core 0.10.1 → 0.10.2

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
@@ -7208,7 +7208,7 @@ interface UPGProduct {
7208
7208
  /** Product lifecycle stages. Covers the full arc from napkin idea to end-of-life. */
7209
7209
  type UPGProductStage = 'concept' | 'validation' | 'build' | 'beta' | 'launch' | 'growth' | 'mature' | 'maintenance' | 'sunset';
7210
7210
  /** 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';
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' | 'competitor_classified_as_classification_value';
7212
7212
  /**
7213
7213
  * Runtime-checkable list of valid cross-product edge types. Mirrors
7214
7214
  * `UPGCrossEdgeType` for use in validators and writers that need to test
@@ -8657,6 +8657,25 @@ type UPGPropertyMigration = {
8657
8657
  reset_value?: string;
8658
8658
  /** Human-readable explanation surfaced in load-time warnings. */
8659
8659
  reason: string;
8660
+ } | {
8661
+ /**
8662
+ * Reshape a bare numeric property into a `UPGAssessment` object
8663
+ * (`{ value, label, scale_id }`). For the v0.10.0 property-registry
8664
+ * correction (#43): interfaces that always declared `UPGAssessment` were
8665
+ * generated loosely, so pre-0.10.0 graphs stored a bare number (e.g.
8666
+ * `market_trend.impact: 4`). This wraps that number on its canonical scale,
8667
+ * deriving the qualitative `label` from the scale's points. Idempotent: a
8668
+ * value that is already an object (assessment) passes through untouched.
8669
+ */
8670
+ kind: 'reshape_value_to_assessment';
8671
+ /** The entity type this rule applies to (or `'*'` for all types). */
8672
+ type: string;
8673
+ /** Key inside `properties` whose bare number is wrapped (stays in the bag). */
8674
+ property: string;
8675
+ /** The assessment scale to label the value on (e.g. `impact_5`, `importance_5`). */
8676
+ scale_id: string;
8677
+ /** Human-readable explanation surfaced in load-time warnings. */
8678
+ reason: string;
8660
8679
  };
8661
8680
  /**
8662
8681
  * Version-scoped property migrations. Same convention as `UPG_MIGRATIONS`:
@@ -8688,6 +8707,12 @@ type UPGPropertyMigrationChange = {
8688
8707
  property: string;
8689
8708
  to_property?: string;
8690
8709
  value_changed: boolean;
8710
+ } | {
8711
+ kind: 'reshaped_to_assessment';
8712
+ property: string;
8713
+ scale_id: string;
8714
+ value: number;
8715
+ label: string;
8691
8716
  };
8692
8717
  /**
8693
8718
  * Apply property migrations to a single node. Returns the (possibly new) node
@@ -21413,7 +21438,7 @@ declare function isCanonical(text: string): boolean;
21413
21438
  * it stamps the `upg_version` field of every `.upg` file written by the SDK.
21414
21439
  * The `check:version-lockstep` gate enforces this at release time.
21415
21440
  */
21416
- declare const UPG_VERSION: "0.10.1";
21441
+ declare const UPG_VERSION: "0.10.2";
21417
21442
  /**
21418
21443
  * The `.upg` JSON document format version. Written to the `upg_version` field.
21419
21444
  * Evolves independently from `UPG_VERSION` (the catalogue version).
package/dist/index.d.ts CHANGED
@@ -7208,7 +7208,7 @@ interface UPGProduct {
7208
7208
  /** Product lifecycle stages. Covers the full arc from napkin idea to end-of-life. */
7209
7209
  type UPGProductStage = 'concept' | 'validation' | 'build' | 'beta' | 'launch' | 'growth' | 'mature' | 'maintenance' | 'sunset';
7210
7210
  /** 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';
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' | 'competitor_classified_as_classification_value';
7212
7212
  /**
7213
7213
  * Runtime-checkable list of valid cross-product edge types. Mirrors
7214
7214
  * `UPGCrossEdgeType` for use in validators and writers that need to test
@@ -8657,6 +8657,25 @@ type UPGPropertyMigration = {
8657
8657
  reset_value?: string;
8658
8658
  /** Human-readable explanation surfaced in load-time warnings. */
8659
8659
  reason: string;
8660
+ } | {
8661
+ /**
8662
+ * Reshape a bare numeric property into a `UPGAssessment` object
8663
+ * (`{ value, label, scale_id }`). For the v0.10.0 property-registry
8664
+ * correction (#43): interfaces that always declared `UPGAssessment` were
8665
+ * generated loosely, so pre-0.10.0 graphs stored a bare number (e.g.
8666
+ * `market_trend.impact: 4`). This wraps that number on its canonical scale,
8667
+ * deriving the qualitative `label` from the scale's points. Idempotent: a
8668
+ * value that is already an object (assessment) passes through untouched.
8669
+ */
8670
+ kind: 'reshape_value_to_assessment';
8671
+ /** The entity type this rule applies to (or `'*'` for all types). */
8672
+ type: string;
8673
+ /** Key inside `properties` whose bare number is wrapped (stays in the bag). */
8674
+ property: string;
8675
+ /** The assessment scale to label the value on (e.g. `impact_5`, `importance_5`). */
8676
+ scale_id: string;
8677
+ /** Human-readable explanation surfaced in load-time warnings. */
8678
+ reason: string;
8660
8679
  };
8661
8680
  /**
8662
8681
  * Version-scoped property migrations. Same convention as `UPG_MIGRATIONS`:
@@ -8688,6 +8707,12 @@ type UPGPropertyMigrationChange = {
8688
8707
  property: string;
8689
8708
  to_property?: string;
8690
8709
  value_changed: boolean;
8710
+ } | {
8711
+ kind: 'reshaped_to_assessment';
8712
+ property: string;
8713
+ scale_id: string;
8714
+ value: number;
8715
+ label: string;
8691
8716
  };
8692
8717
  /**
8693
8718
  * Apply property migrations to a single node. Returns the (possibly new) node
@@ -21413,7 +21438,7 @@ declare function isCanonical(text: string): boolean;
21413
21438
  * it stamps the `upg_version` field of every `.upg` file written by the SDK.
21414
21439
  * The `check:version-lockstep` gate enforces this at release time.
21415
21440
  */
21416
- declare const UPG_VERSION: "0.10.1";
21441
+ declare const UPG_VERSION: "0.10.2";
21417
21442
  /**
21418
21443
  * The `.upg` JSON document format version. Written to the `upg_version` field.
21419
21444
  * Evolves independently from `UPG_VERSION` (the catalogue version).
package/dist/index.js CHANGED
@@ -1145,6 +1145,10 @@ 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" },
1149
1153
  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
1154
  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" },
@@ -3040,7 +3044,8 @@ var UPG_CROSS_EDGE_TYPES = [
3040
3044
  "primitive_stored_as_data_type",
3041
3045
  "feature_rivals_competitor_feature",
3042
3046
  "competitor_signal_maps_to_feature",
3043
- "competitor_signal_surfaces_opportunity"
3047
+ "competitor_signal_surfaces_opportunity",
3048
+ "competitor_classified_as_classification_value"
3044
3049
  ];
3045
3050
  var REGISTRY_PRODUCT_ID = "registry";
3046
3051
 
@@ -7250,6 +7255,34 @@ function getDeprecatedTypes() {
7250
7255
  return deprecated;
7251
7256
  }
7252
7257
  var UPG_PROPERTY_MIGRATIONS = {
7258
+ // ── v0.10.2: market_intelligence number -> assessment reshape ──────────────
7259
+ //
7260
+ // The v0.10.0 property-registry generator fix (#43) corrected several property
7261
+ // types that interfaces already declared as `UPGAssessment` but the generator
7262
+ // had emitted loosely. A pre-0.10.0 graph that stored a bare number for
7263
+ // `market_trend.impact` / `relevance` is now invalid (the schema wants
7264
+ // `{value,label}`). These rules wrap the bare number on its canonical scale so
7265
+ // the migration is one command, not a hand-reshape. The enum tightenings the
7266
+ // same #43 fix surfaced (data_flow.direction, integration_pattern.pattern_type,
7267
+ // api_contract.protocol, service.service_type) already have rules under 0.9.12;
7268
+ // 0.10.2 also makes ALL of them visible to validate_graph (they were applied by
7269
+ // migrate_properties but never surfaced as a fixable drift).
7270
+ "0.10.2": [
7271
+ {
7272
+ kind: "reshape_value_to_assessment",
7273
+ type: "market_trend",
7274
+ property: "impact",
7275
+ scale_id: "impact_5",
7276
+ 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."
7277
+ },
7278
+ {
7279
+ kind: "reshape_value_to_assessment",
7280
+ type: "market_trend",
7281
+ property: "relevance",
7282
+ scale_id: "importance_5",
7283
+ 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."
7284
+ }
7285
+ ],
7253
7286
  // ── v0.9.14: drop the key_result kr_status / status twin (A2) ──────────────
7254
7287
  //
7255
7288
  // key_result carried BOTH a lifecycle `status` and a `kr_status` property with
@@ -7822,6 +7855,26 @@ function migrateNodeProperties(node, fromVersion, toVersion) {
7822
7855
  mutated = true;
7823
7856
  break;
7824
7857
  }
7858
+ case "reshape_value_to_assessment": {
7859
+ const props = workingNode.properties;
7860
+ if (!props || !(m.property in props)) break;
7861
+ const oldValue = props[m.property];
7862
+ let num = null;
7863
+ if (typeof oldValue === "number" && Number.isFinite(oldValue)) num = oldValue;
7864
+ else if (typeof oldValue === "string" && oldValue.trim() !== "" && Number.isFinite(Number(oldValue))) {
7865
+ num = Number(oldValue);
7866
+ }
7867
+ if (num === null) break;
7868
+ const label = getScale(m.scale_id)?.points.find((p) => p.value === num)?.label ?? String(num);
7869
+ const nextProps = {
7870
+ ...props,
7871
+ [m.property]: { value: num, label, scale_id: m.scale_id }
7872
+ };
7873
+ workingNode.properties = nextProps;
7874
+ mutated = true;
7875
+ changes.push({ kind: "reshaped_to_assessment", property: m.property, scale_id: m.scale_id, value: num, label });
7876
+ break;
7877
+ }
7825
7878
  }
7826
7879
  }
7827
7880
  }
@@ -28376,7 +28429,7 @@ function isCanonical(text) {
28376
28429
  }
28377
28430
 
28378
28431
  // src/index.ts
28379
- var UPG_VERSION = "0.10.1";
28432
+ var UPG_VERSION = "0.10.2";
28380
28433
  var UPG_FORMAT_VERSION = "0.4.0";
28381
28434
  var MARKDOWN_FORMAT_VERSION = "0.1";
28382
28435
  var UPG_TYPES = getTypes();