@unified-product-graph/core 0.9.8 → 0.9.9

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.js CHANGED
@@ -45,8 +45,8 @@ var UPG_DOMAINS = [
45
45
  {
46
46
  id: "validation",
47
47
  label: "Validation",
48
- description: "Testing ideas before committing to build them. Hypotheses state testable beliefs. Experiments run the tests. Evidence captures what was observed. Learnings distill what was understood. Test plans structure the approach. Research plans coordinate broader investigation. Consumes opportunities from Discovery and insights from User Research, producing evidence that informs Strategy and Product Specification.",
49
- types: ["hypothesis", "experiment", "experiment_plan", "experiment_run", "learning", "test_plan", "evidence", "research_plan"]
48
+ description: "Testing ideas before committing to build them. Hypotheses state testable beliefs. Experiment plans structure the validation design. Experiments run the tests, with experiment runs capturing replication. Evidence captures what was observed. Learnings distill what was understood. Research plans coordinate broader investigation. Consumes opportunities from Discovery and insights from User Research, producing evidence that informs Strategy and Product Specification.",
49
+ types: ["hypothesis", "experiment", "experiment_plan", "experiment_run", "learning", "evidence", "research_plan"]
50
50
  },
51
51
  {
52
52
  id: "market_intelligence",
@@ -291,8 +291,9 @@ var UPG_DOMAINS = [
291
291
  {
292
292
  id: "testing",
293
293
  label: "Quality Assurance",
294
- description: "Verifying your product works correctly. Test suites group related tests. Test cases define individual checks. QA sessions capture exploratory testing. Regression tests guard against regressions. Test coverage reports measure completeness. Test environments define where tests run. Test results record outcomes. Validates Engineering (code quality) and Product Specification (acceptance criteria). Feeds DevOps (release confidence).",
294
+ description: "Verifying your product works correctly. Test plans define the verification approach (scope, environments, pass criteria). Test suites group related tests. Test cases define individual checks. QA sessions capture exploratory testing. Regression tests guard against regressions. Test coverage reports measure completeness. Test environments define where tests run. Test results record outcomes. Validates Engineering (code quality) and Product Specification (acceptance criteria). Feeds DevOps (release confidence).",
295
295
  types: [
296
+ "test_plan",
296
297
  "test_suite",
297
298
  "test_case",
298
299
  "qa_session",
@@ -331,7 +332,7 @@ var UPG_DOMAINS = [
331
332
  {
332
333
  id: "ai",
333
334
  label: "AI & Machine Learning",
334
- description: "AI and machine learning capabilities within your product. AI models track deployed models. Prompt versions manage prompt evolution. Eval benchmarks and eval runs measure quality. AI cost trackers monitor spend. Hallucination reports flag reliability issues. AI guardrails set safety boundaries. Model comparisons evaluate alternatives. AI experiments test new approaches. AI datasets track training data. AI traces log inference chains. Connects to Engineering, Data & Analytics, and Product Specification.",
335
+ description: "AI and machine learning capabilities within your product. AI models track deployed models. Prompt templates define reusable prompts; prompt versions manage their evolution. Eval benchmarks and eval runs measure quality. AI cost trackers monitor spend. Hallucination reports flag reliability issues. AI guardrails set safety boundaries. Model comparisons evaluate alternatives. AI experiments test new approaches. AI datasets track training data. AI traces log inference chains. Connects to Engineering, Data & Analytics, and Product Specification.",
335
336
  types: [
336
337
  "ai_model",
337
338
  "prompt_version",
@@ -554,15 +555,23 @@ var UPG_ENTITY_META = [
554
555
  { name: "hypothesis", type_id: "ent_027", maturity: "stable", since: "0.1.0" },
555
556
  { name: "hypothesis_claim", type_id: "ent_344", maturity: "deprecated", since: "0.2.8", deprecated_in: "0.4.0", replacement: "hypothesis" },
556
557
  { name: "hypothesis_evidence", type_id: "ent_345", maturity: "deprecated", since: "0.2.8", deprecated_in: "0.4.0", replacement: "evidence" },
557
- // `experiment` is canonical. It has its own property schema
558
+ // `experiment` is canonical (UPG-664). It has its own property schema
558
559
  // (method, start_date, end_date, sample_size, expected_lift) and is the
559
- // canonical parent of `ab_test` in the hierarchy. Plan/Run split
560
- // (experiment_plan + experiment_run) remains as the fine-grained option;
561
- // `experiment` covers the general-purpose use case.
560
+ // canonical unit of a structured test. The validation flow is
561
+ // `hypothesis → experiment_plan → experiment → experiment_run`: the plan is
562
+ // the validation design, the experiment is the structured test, the run is
563
+ // the optional multi-run/replication child.
562
564
  { name: "experiment", type_id: "ent_028", maturity: "stable", since: "0.1.0" },
563
- { name: "experiment_plan", type_id: "ent_340", maturity: "proposed", since: "0.2.6" },
565
+ // experiment_plan graduated proposed → stable (UPG-664). It is the canonical
566
+ // validation PLAN type — it absorbed `test_plan`'s planning properties
567
+ // (method / success_criteria / sample_size) when `test_plan` re-homed to the
568
+ // QA/testing domain (UPG-678).
569
+ { name: "experiment_plan", type_id: "ent_340", maturity: "stable", since: "0.2.6" },
564
570
  { name: "experiment_run", type_id: "ent_341", maturity: "proposed", since: "0.2.6" },
565
571
  { name: "learning", type_id: "ent_029", maturity: "stable", since: "0.1.0" },
572
+ // test_plan re-homed validation → testing/QA (UPG-678). It is the QA
573
+ // verification-procedure plan (test scope / environments / pass criteria);
574
+ // its former validation-planning role is carried by experiment_plan.
566
575
  { name: "test_plan", type_id: "ent_030", maturity: "stable", since: "0.1.0" },
567
576
  { name: "evidence", type_id: "ent_031", maturity: "stable", since: "0.1.0" },
568
577
  { name: "research_plan", type_id: "ent_032", maturity: "stable", since: "0.1.0" },
@@ -696,7 +705,7 @@ var UPG_ENTITY_META = [
696
705
  { name: "behavioral_segment", type_id: "ent_111", maturity: "stable", since: "0.1.0" },
697
706
  { name: "segment", type_id: "ent_338", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.2.0", replacement: "behavioral_segment" },
698
707
  { name: "growth_loop", type_id: "ent_112", maturity: "stable", since: "0.1.0" },
699
- { name: "growth_experiment", type_id: "ent_113", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment_run" },
708
+ { name: "growth_experiment", type_id: "ent_113", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment" },
700
709
  { name: "variant", type_id: "ent_114", maturity: "stable", since: "0.1.0" },
701
710
  { name: "attribution_model", type_id: "ent_115", maturity: "stable", since: "0.1.0" },
702
711
  // ── Business Model ──
@@ -746,7 +755,7 @@ var UPG_ENTITY_META = [
746
755
  { name: "metric_definition", type_id: "ent_154", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "metric" },
747
756
  { name: "event_schema", type_id: "ent_155", maturity: "stable", since: "0.1.0" },
748
757
  { name: "dashboard", type_id: "ent_156", maturity: "stable", since: "0.1.0" },
749
- { name: "ab_test", type_id: "ent_157", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment_run" },
758
+ { name: "ab_test", type_id: "ent_157", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment" },
750
759
  { name: "data_model", type_id: "ent_158", maturity: "stable", since: "0.1.0" },
751
760
  { name: "data_quality_rule", type_id: "ent_159", maturity: "stable", since: "0.1.0" },
752
761
  { name: "data_product", type_id: "ent_160", maturity: "stable", since: "0.1.0" },
@@ -828,7 +837,7 @@ var UPG_ENTITY_META = [
828
837
  { name: "feedback_theme", type_id: "ent_225", maturity: "stable", since: "0.1.0" },
829
838
  // ── Pricing & Packaging ──
830
839
  { name: "pricing_strategy", type_id: "ent_226", maturity: "stable", since: "0.1.0" },
831
- { name: "pricing_experiment", type_id: "ent_227", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment_run" },
840
+ { name: "pricing_experiment", type_id: "ent_227", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment" },
832
841
  { name: "package", type_id: "ent_228", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.2.0", replacement: "pricing_tier" },
833
842
  { name: "discount_strategy", type_id: "ent_229", maturity: "stable", since: "0.1.0" },
834
843
  { name: "trial_config", type_id: "ent_230", maturity: "stable", since: "0.1.0" },
@@ -999,8 +1008,22 @@ var UPG_EDGE_CATALOG = {
999
1008
  // `evolved_from` in the reverse lets a feature trace its solution ancestry.
1000
1009
  solution_becomes_feature: { forward_verb: "becomes", reverse_verb: "evolved_from", classification: "causal", source_type: "solution", target_type: "feature" },
1001
1010
  hypothesis_requires_experiment_plan: { forward_verb: "requires", reverse_verb: "planned_for", classification: "causal", source_type: "hypothesis", target_type: "experiment_plan" },
1002
- hypothesis_planned_via_test_plan: { forward_verb: "planned_via", reverse_verb: "plans_for", classification: "hierarchy", source_type: "hypothesis", target_type: "test_plan" },
1011
+ // (UPG-664) V4: close the stable canonical loop directly on `experiment`.
1012
+ // Before this, resolve_edge_for_pair(hypothesis, experiment) and the reverse
1013
+ // were both null — the stable experiment could not attach to the hypothesis
1014
+ // it tests without adopting the proposed run type.
1015
+ hypothesis_tested_by_experiment: { forward_verb: "tested_by", reverse_verb: "tests", classification: "causal", source_type: "hypothesis", target_type: "experiment" },
1016
+ experiment_validates_hypothesis: { forward_verb: "validates", reverse_verb: "validated_by", classification: "causal", source_type: "experiment", target_type: "hypothesis" },
1003
1017
  hypothesis_investigated_via_research_plan: { forward_verb: "investigated_via", reverse_verb: "investigates", classification: "hierarchy", source_type: "hypothesis", target_type: "research_plan" },
1018
+ // (UPG-678) V8: a research insight can seed a hypothesis directly. The region
1019
+ // says validation consumes `insight` from user_research, but the only paths
1020
+ // in were via solution/learning/assumption.
1021
+ insight_generates_hypothesis: { forward_verb: "generates", reverse_verb: "generated_from", classification: "cross-domain", source_type: "insight", target_type: "hypothesis" },
1022
+ // (UPG-678) V7: the validation-side research plan is conducted as the
1023
+ // user_research-side research study. Both sit in the
1024
+ // discovery_research_validation region but had no connecting edge; the plan
1025
+ // jumped straight to `participant`, bypassing the study container.
1026
+ research_plan_conducted_as_research_study: { forward_verb: "conducted_as", reverse_verb: "conducts", classification: "cross-domain", source_type: "research_plan", target_type: "research_study" },
1004
1027
  experiment_run_produces_learning: { forward_verb: "produces", reverse_verb: "learned_from", classification: "causal", source_type: "experiment_run", target_type: "learning" },
1005
1028
  experiment_run_yields_evidence: { forward_verb: "yields", reverse_verb: "supports", classification: "causal", source_type: "experiment_run", target_type: "evidence" },
1006
1029
  // (v0.2.7 closure) the legacy duplicate pair
@@ -1034,7 +1057,11 @@ var UPG_EDGE_CATALOG = {
1034
1057
  // canonical edge, not a foreign-key property (the v0.2.6 ExperimentPlan
1035
1058
  // interface left this slot pending in line with the principle).
1036
1059
  experiment_plan_targets_metric: { forward_verb: "targets", reverse_verb: "targeted_by_plan", classification: "cross-domain", source_type: "experiment_plan", target_type: "metric" },
1037
- experiment_has_plan: { forward_verb: "has_plan", reverse_verb: "is_plan_for", classification: "hierarchy", source_type: "experiment", target_type: "experiment_plan" },
1060
+ // (UPG-664) The validation plan designs the experiment it produces. This
1061
+ // replaces the backwards `experiment_has_plan` (experiment → experiment_plan):
1062
+ // the chain now reads hypothesis → experiment_plan → experiment → experiment_run,
1063
+ // with experiment_plan the owning parent of experiment.
1064
+ experiment_plan_designs_experiment: { forward_verb: "designs", reverse_verb: "designed_by", classification: "hierarchy", source_type: "experiment_plan", target_type: "experiment" },
1038
1065
  experiment_executed_as_experiment_run: { forward_verb: "executed_as", reverse_verb: "execution_of", classification: "hierarchy", source_type: "experiment", target_type: "experiment_run" },
1039
1066
  experiment_produces_learning: { forward_verb: "produces", reverse_verb: "produced_by", classification: "causal", source_type: "experiment", target_type: "learning" },
1040
1067
  experiment_produces_evidence: { forward_verb: "produces", reverse_verb: "produced_by", classification: "causal", source_type: "experiment", target_type: "evidence" },
@@ -1159,11 +1186,15 @@ var UPG_EDGE_CATALOG = {
1159
1186
  product_holds_assumption: { forward_verb: "holds", reverse_verb: "held_by", classification: "semantic", source_type: "product", target_type: "assumption" },
1160
1187
  product_measures_with_metric: { forward_verb: "measures_with", reverse_verb: "measures", classification: "hierarchy", source_type: "product", target_type: "metric" },
1161
1188
  outcome_measured_by_metric: { forward_verb: "measured_by", reverse_verb: "measures", classification: "hierarchy", source_type: "outcome", target_type: "metric" },
1162
- outcome_tracked_by_metric: { forward_verb: "tracked_by", reverse_verb: "tracks", classification: "hierarchy", source_type: "outcome", target_type: "metric" },
1189
+ // (UPG-677) outcome_tracked_by_metric retired — near-synonym of
1190
+ // outcome_measured_by_metric (the kept canonical key). Captain's lean =
1191
+ // collapse. See UPG_EDGE_MIGRATIONS['0.9.9'].
1163
1192
  objective_achieved_through_key_result: { forward_verb: "achieved_through", reverse_verb: "achieves", classification: "hierarchy", source_type: "objective", target_type: "key_result" },
1164
1193
  objective_measured_by_metric: { forward_verb: "measured_by", reverse_verb: "measures", classification: "hierarchy", source_type: "objective", target_type: "metric" },
1165
1194
  key_result_quantified_by_metric: { forward_verb: "quantified_by", reverse_verb: "quantifies", classification: "hierarchy", source_type: "key_result", target_type: "metric" },
1166
- key_result_tracked_by_metric: { forward_verb: "tracked_by", reverse_verb: "tracks", classification: "hierarchy", source_type: "key_result", target_type: "metric" },
1195
+ // (UPG-677) key_result_tracked_by_metric retired — near-synonym of
1196
+ // key_result_quantified_by_metric (the kept canonical key). Captain's lean =
1197
+ // collapse. See UPG_EDGE_MIGRATIONS['0.9.9'].
1167
1198
  vision_realised_through_mission: { forward_verb: "realised_through", reverse_verb: "realises", classification: "hierarchy", source_type: "vision", target_type: "mission" },
1168
1199
  mission_supported_by_strategic_pillar: { forward_verb: "supported_by", reverse_verb: "supports", classification: "hierarchy", source_type: "mission", target_type: "strategic_pillar" },
1169
1200
  strategic_pillar_organises_strategic_theme: { forward_verb: "organises", reverse_verb: "organised_by", classification: "hierarchy", source_type: "strategic_pillar", target_type: "strategic_theme" },
@@ -1187,17 +1218,20 @@ var UPG_EDGE_CATALOG = {
1187
1218
  // make them measurable. Direct link means a dashboard can surface KRs next
1188
1219
  // to the theme without traversing objective.
1189
1220
  //
1190
- // `objective_rolls_up_to_strategic_theme`: the OKR containment edge.
1221
+ // `strategic_theme_contains_objective`: the OKR containment edge (UPG-676,
1222
+ // renamed from the malformed `objective_rolls_up_to_strategic_theme`).
1191
1223
  // an objective is the specific quarterly bet *within* a theme. strategic_theme
1192
1224
  // is the broader multi-quarter focus area; objective is subordinate.
1193
1225
  // Direction: strategic_theme → objective (parent → child, per UPG hierarchy
1194
- // convention where source is the parent). The reverse verb `rolls_up_to`
1226
+ // convention where source is the parent). The key now reads source-first
1227
+ // (strategic_theme), the forward verb is the clean `contains` (was the
1228
+ // malformed `contains_objective`), and the reverse verb `rolls_up_to`
1195
1229
  // surfaces the upward-rollup read. Mirrors `strategic_pillar_organises_
1196
1230
  // strategic_theme` (pillar → theme), completing the strategic cascade:
1197
1231
  // strategic_pillar → strategic_theme → objective → key_result.
1198
1232
  strategic_theme_delivers_outcome: { forward_verb: "delivers", reverse_verb: "delivered_by", classification: "causal", source_type: "strategic_theme", target_type: "outcome" },
1199
1233
  strategic_theme_measured_by_key_result: { forward_verb: "measured_by", reverse_verb: "measures", classification: "causal", source_type: "strategic_theme", target_type: "key_result" },
1200
- objective_rolls_up_to_strategic_theme: { forward_verb: "contains_objective", reverse_verb: "rolls_up_to", classification: "hierarchy", source_type: "strategic_theme", target_type: "objective" },
1234
+ strategic_theme_contains_objective: { forward_verb: "contains", reverse_verb: "rolls_up_to", classification: "hierarchy", source_type: "strategic_theme", target_type: "objective" },
1201
1235
  initiative_assumes_assumption: { forward_verb: "assumes", reverse_verb: "assumed_by", classification: "hierarchy", source_type: "initiative", target_type: "assumption" },
1202
1236
  initiative_drives_outcome: { forward_verb: "drives", reverse_verb: "driven_by", classification: "cross-domain", source_type: "initiative", target_type: "outcome" },
1203
1237
  capability_enables_value_stream: { forward_verb: "enables", reverse_verb: "enabled_by", classification: "cross-domain", source_type: "capability", target_type: "value_stream" },
@@ -1502,7 +1536,9 @@ var UPG_EDGE_CATALOG = {
1502
1536
  threat_model_surfaces_vulnerability: { forward_verb: "surfaces", reverse_verb: "surfaced_by", classification: "hierarchy", source_type: "threat_model", target_type: "vulnerability" },
1503
1537
  product_enforces_security_control: { forward_verb: "enforces", reverse_verb: "enforced_by", classification: "hierarchy", source_type: "product", target_type: "security_control" },
1504
1538
  product_governed_by_security_policy: { forward_verb: "governed_by", reverse_verb: "governs", classification: "hierarchy", source_type: "product", target_type: "security_policy" },
1505
- product_experiences_incident_hierarchy: { forward_verb: "experiences", reverse_verb: "affects", classification: "hierarchy", source_type: "product", target_type: "incident" },
1539
+ // (UPG-677) product_experiences_incident_hierarchy retired — byte-identical
1540
+ // shadow of product_experiences_incident (the clean key). See
1541
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1506
1542
  product_tested_by_penetration_test: { forward_verb: "tested_by", reverse_verb: "tests", classification: "hierarchy", source_type: "product", target_type: "penetration_test" },
1507
1543
  product_reviewed_by_security_review: { forward_verb: "reviewed_by", reverse_verb: "reviews", classification: "hierarchy", source_type: "product", target_type: "security_review" },
1508
1544
  product_classifies_data_with_data_classification: { forward_verb: "classifies_data_with", reverse_verb: "classifies_data_for", classification: "hierarchy", source_type: "product", target_type: "data_classification" },
@@ -1523,6 +1559,12 @@ var UPG_EDGE_CATALOG = {
1523
1559
  vulnerability_discovered_by_penetration_test: { forward_verb: "discovered_by", reverse_verb: "discovers", classification: "cross-domain", source_type: "vulnerability", target_type: "penetration_test" },
1524
1560
  data_classification_applies_to_data_source: { forward_verb: "applies_to", reverse_verb: "classified_by", classification: "cross-domain", source_type: "data_classification", target_type: "data_source" },
1525
1561
  // 3.4 Quality Assurance & Testing Domain
1562
+ // (UPG-678) test_plan re-homed validation → QA. It is the QA planning layer:
1563
+ // the product owns its test plans; each plan is carried out by the suites it
1564
+ // groups and specifies the environments it exercises.
1565
+ product_plans_qa_via_test_plan: { forward_verb: "plans_qa_via", reverse_verb: "plans_qa_for", classification: "hierarchy", source_type: "product", target_type: "test_plan" },
1566
+ test_plan_executed_by_test_suite: { forward_verb: "executed_by", reverse_verb: "executes", classification: "hierarchy", source_type: "test_plan", target_type: "test_suite" },
1567
+ test_plan_specifies_test_environment: { forward_verb: "specifies", reverse_verb: "specified_by", classification: "hierarchy", source_type: "test_plan", target_type: "test_environment" },
1526
1568
  product_maintains_test_suite: { forward_verb: "maintains", reverse_verb: "maintained_by", classification: "hierarchy", source_type: "product", target_type: "test_suite" },
1527
1569
  test_suite_contains_test_case: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "test_suite", target_type: "test_case" },
1528
1570
  product_undergoes_qa_session: { forward_verb: "undergoes", reverse_verb: "conducted_on", classification: "hierarchy", source_type: "product", target_type: "qa_session" },
@@ -1554,7 +1596,12 @@ var UPG_EDGE_CATALOG = {
1554
1596
  a11y_audit_covers_feature: { forward_verb: "covers", reverse_verb: "covered_by", classification: "cross-domain", source_type: "a11y_audit", target_type: "feature" },
1555
1597
  // 3.6 AI/ML Operations Domain
1556
1598
  product_powered_by_ai_model: { forward_verb: "powered_by", reverse_verb: "powers", classification: "hierarchy", source_type: "product", target_type: "ai_model" },
1557
- ai_model_prompted_via_prompt_version: { forward_verb: "prompted_via", reverse_verb: "prompts", classification: "hierarchy", source_type: "ai_model", target_type: "prompt_version" },
1599
+ // (UPG-665) The prompt abstraction is corrected to ai_model → prompt_template
1600
+ // → prompt_version. The model defines templates; each template contains its
1601
+ // versions. This replaces the inverted ai_model → prompt_version ownership
1602
+ // and the backwards prompt_version → prompt_template edge.
1603
+ ai_model_defines_prompt_template: { forward_verb: "defines", reverse_verb: "defined_by", classification: "hierarchy", source_type: "ai_model", target_type: "prompt_template" },
1604
+ prompt_template_contains_prompt_version: { forward_verb: "contains", reverse_verb: "version_of", classification: "hierarchy", source_type: "prompt_template", target_type: "prompt_version" },
1558
1605
  ai_model_benchmarked_by_eval_benchmark: { forward_verb: "benchmarked_by", reverse_verb: "benchmarks", classification: "hierarchy", source_type: "ai_model", target_type: "eval_benchmark" },
1559
1606
  eval_benchmark_executed_as_eval_run: { forward_verb: "executed_as", reverse_verb: "executes", classification: "hierarchy", source_type: "eval_benchmark", target_type: "eval_run" },
1560
1607
  ai_model_costed_by_ai_cost_tracker: { forward_verb: "costed_by", reverse_verb: "costs", classification: "hierarchy", source_type: "ai_model", target_type: "ai_cost_tracker" },
@@ -1562,7 +1609,10 @@ var UPG_EDGE_CATALOG = {
1562
1609
  ai_model_constrained_by_ai_guardrail: { forward_verb: "constrained_by", reverse_verb: "constrains", classification: "hierarchy", source_type: "ai_model", target_type: "ai_guardrail" },
1563
1610
  product_compared_via_model_comparison: { forward_verb: "compared_via", reverse_verb: "compares", classification: "hierarchy", source_type: "product", target_type: "model_comparison" },
1564
1611
  ai_model_compared_in_model_comparison: { forward_verb: "compared_in", reverse_verb: "compares", classification: "hierarchy", source_type: "ai_model", target_type: "model_comparison" },
1565
- prompt_version_evolves_prompt_template: { forward_verb: "evolves", reverse_verb: "evolved_by", classification: "cross-domain", source_type: "prompt_version", target_type: "prompt_template" },
1612
+ // (UPG-665, AI-10) prompt_version self-ordering: track iteration order with a
1613
+ // real edge, not just a string property — the domain's own anti-pattern is
1614
+ // "version prompts like code".
1615
+ prompt_version_supersedes_prompt_version: { forward_verb: "supersedes", reverse_verb: "superseded_by", classification: "semantic", source_type: "prompt_version", target_type: "prompt_version" },
1566
1616
  eval_benchmark_measures_feature: { forward_verb: "measures", reverse_verb: "measured_by", classification: "cross-domain", source_type: "eval_benchmark", target_type: "feature" },
1567
1617
  ai_guardrail_enforces_security_policy: { forward_verb: "enforces", reverse_verb: "enforced_by", classification: "cross-domain", source_type: "ai_guardrail", target_type: "security_policy" },
1568
1618
  model_comparison_informs_decision: { forward_verb: "informs", reverse_verb: "informed_by", classification: "cross-domain", source_type: "model_comparison", target_type: "decision" },
@@ -1570,6 +1620,18 @@ var UPG_EDGE_CATALOG = {
1570
1620
  ai_model_evaluated_through_ai_experiment: { forward_verb: "evaluated_through", reverse_verb: "evaluates", classification: "hierarchy", source_type: "ai_model", target_type: "ai_experiment" },
1571
1621
  ai_model_trained_on_ai_dataset: { forward_verb: "trained_on", reverse_verb: "trains", classification: "hierarchy", source_type: "ai_model", target_type: "ai_dataset" },
1572
1622
  ai_model_produces_ai_trace: { forward_verb: "produces", reverse_verb: "produced_by", classification: "causal", source_type: "ai_model", target_type: "ai_trace" },
1623
+ // (UPG-665) Observability bridges — turn four sinks into wired evidence and
1624
+ // close the Model Evaluation Loop.
1625
+ // AI-3: an eval_run can reach the model and prompt version it judged.
1626
+ eval_run_evaluates_ai_model: { forward_verb: "evaluates", reverse_verb: "evaluated_by", classification: "cross-domain", source_type: "eval_run", target_type: "ai_model" },
1627
+ eval_run_scores_prompt_version: { forward_verb: "scores", reverse_verb: "scored_by", classification: "cross-domain", source_type: "eval_run", target_type: "prompt_version" },
1628
+ // AI-5: a hallucination_report reaches the trace that produced it and its root cause.
1629
+ hallucination_report_traces_to_ai_trace: { forward_verb: "traces_to", reverse_verb: "traced_by", classification: "cross-domain", source_type: "hallucination_report", target_type: "ai_trace" },
1630
+ hallucination_report_caused_by_root_cause: { forward_verb: "caused_by", reverse_verb: "causes", classification: "cross-domain", source_type: "hallucination_report", target_type: "root_cause" },
1631
+ // AI-6: an ai_trace reaches the prompt version it executed.
1632
+ ai_trace_executed_prompt_version: { forward_verb: "executed", reverse_verb: "executed_in", classification: "cross-domain", source_type: "ai_trace", target_type: "prompt_version" },
1633
+ // AI-4/AI-11: an ai_dataset carries provenance to its data source.
1634
+ ai_dataset_sourced_from_data_source: { forward_verb: "sourced_from", reverse_verb: "sources", classification: "cross-domain", source_type: "ai_dataset", target_type: "data_source" },
1573
1635
  // 3.7 Agentic Workflows & Process Domain
1574
1636
  product_automated_via_workflow_template: { forward_verb: "automated_via", reverse_verb: "automates", classification: "hierarchy", source_type: "product", target_type: "workflow_template" },
1575
1637
  workflow_template_executed_as_workflow_run: { forward_verb: "executed_as", reverse_verb: "executes", classification: "hierarchy", source_type: "workflow_template", target_type: "workflow_run" },
@@ -1607,7 +1669,9 @@ var UPG_EDGE_CATALOG = {
1607
1669
  // variant is owned by growth_campaign in the hierarchy; experiments
1608
1670
  // test them via a semantic relationship.
1609
1671
  experiment_run_tests_variant: { forward_verb: "tests", reverse_verb: "tested_in", classification: "semantic", source_type: "experiment_run", target_type: "variant" },
1610
- metric_decomposed_into_metric: { forward_verb: "decomposed_into", reverse_verb: "rolls_up_to", classification: "hierarchy", source_type: "metric", target_type: "metric" },
1672
+ // (UPG-677) metric_decomposed_into_metric retired — tense-twin of
1673
+ // metric_decomposes_into_metric (the active-voice canonical key). See
1674
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1611
1675
  metric_drives_metric: { forward_verb: "drives", reverse_verb: "driven_by", classification: "causal", source_type: "metric", target_type: "metric" },
1612
1676
  funnel_step_reveals_need: { forward_verb: "reveals", reverse_verb: "visible_in", classification: "cross-domain", source_type: "funnel_step", target_type: "need" },
1613
1677
  funnel_step_tracks_event_schema: { forward_verb: "tracks", reverse_verb: "fires_in", classification: "cross-domain", source_type: "funnel_step", target_type: "event_schema" },
@@ -1636,7 +1700,11 @@ var UPG_EDGE_CATALOG = {
1636
1700
  business_model_requires_key_resource: { forward_verb: "requires", reverse_verb: "required_by", classification: "hierarchy", source_type: "business_model", target_type: "key_resource" },
1637
1701
  business_model_performs_key_activity: { forward_verb: "performs", reverse_verb: "performed_by", classification: "hierarchy", source_type: "business_model", target_type: "key_activity" },
1638
1702
  business_model_targets_market_segment: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "business_model", target_type: "market_segment" },
1639
- business_model_reaches_via_distribution_channel: { forward_verb: "reaches_via", reverse_verb: "reaches_for", classification: "hierarchy", source_type: "business_model", target_type: "distribution_channel" },
1703
+ // (UPG-677) business_model_reaches_via_distribution_channel retired —
1704
+ // near-synonym of business_model_distributes_via_distribution_channel (the
1705
+ // kept canonical key; "distributes_via" is the Business Model Canvas Channels
1706
+ // verb). FLAG: Captain has not separately confirmed this collapse. See
1707
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1640
1708
  business_model_maintains_customer_relationship: { forward_verb: "maintains", reverse_verb: "maintained_by", classification: "hierarchy", source_type: "business_model", target_type: "customer_relationship" },
1641
1709
  business_model_distributes_via_distribution_channel: { forward_verb: "distributes_via", reverse_verb: "distributes_for", classification: "hierarchy", source_type: "business_model", target_type: "distribution_channel" },
1642
1710
  revenue_stream_tiered_as_pricing_tier: { forward_verb: "tiered_as", reverse_verb: "tiers", classification: "hierarchy", source_type: "revenue_stream", target_type: "pricing_tier" },
@@ -1658,7 +1726,9 @@ var UPG_EDGE_CATALOG = {
1658
1726
  // as a `need` with `valence='pain'`; link each pain the proposition
1659
1727
  // relieves structurally.
1660
1728
  value_proposition_solves_need: { forward_verb: "solves", reverse_verb: "solved_by", classification: "cross-domain", source_type: "value_proposition", target_type: "need" },
1661
- value_proposition_targets_persona_cross_domain: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "value_proposition", target_type: "persona" },
1729
+ // (UPG-677) value_proposition_targets_persona_cross_domain retired —
1730
+ // byte-identical shadow of value_proposition_targets_persona (the clean key).
1731
+ // See UPG_EDGE_MIGRATIONS['0.9.9'].
1662
1732
  revenue_stream_drives_metric: { forward_verb: "drives", reverse_verb: "driven_by", classification: "cross-domain", source_type: "revenue_stream", target_type: "metric" },
1663
1733
  // Ring 5 structural edges
1664
1734
  // (UPG-668, P-I) These three edges name `funnel_step` but were typed against
@@ -1802,7 +1872,9 @@ var UPG_EDGE_CATALOG = {
1802
1872
  customer_feedback_reveals_churn_reason: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "hierarchy", source_type: "customer_feedback", target_type: "churn_reason" },
1803
1873
  user_flow_contains_customer_journey_stage: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "user_flow", target_type: "customer_journey_stage" },
1804
1874
  customer_journey_stage_contains_touchpoint: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "customer_journey_stage", target_type: "touchpoint" },
1805
- support_ticket_reveals_need: { forward_verb: "reveals", reverse_verb: "revealed_by_ticket", classification: "cross-domain", source_type: "support_ticket", target_type: "need" },
1875
+ // (UPG-677) reverse_verb normalised revealed_by_ticket → revealed_by when the
1876
+ // support_ticket_reveals_need_cross_domain twin was collapsed into this clean key.
1877
+ support_ticket_reveals_need: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "support_ticket", target_type: "need" },
1806
1878
  customer_feedback_creates_observation: { forward_verb: "creates", reverse_verb: "created_from_feedback", classification: "cross-domain", source_type: "customer_feedback", target_type: "observation" },
1807
1879
  churn_reason_generates_hypothesis: { forward_verb: "generates", reverse_verb: "generated_from_churn", classification: "cross-domain", source_type: "churn_reason", target_type: "hypothesis" },
1808
1880
  user_flow_maps_user_journey: { forward_verb: "maps", reverse_verb: "mapped_by", classification: "cross-domain", source_type: "user_flow", target_type: "user_journey" },
@@ -1814,14 +1886,18 @@ var UPG_EDGE_CATALOG = {
1814
1886
  customer_health_score_informs_playbook: { forward_verb: "informs", reverse_verb: "informed_by_health_score", classification: "cross-domain", source_type: "customer_health_score", target_type: "playbook" },
1815
1887
  customer_feedback_becomes_feature_request: { forward_verb: "becomes", reverse_verb: "originated_from", classification: "cross-domain", source_type: "customer_feedback", target_type: "feature_request" },
1816
1888
  support_ticket_reports_bug: { forward_verb: "reports", reverse_verb: "reported_by", classification: "cross-domain", source_type: "support_ticket", target_type: "bug" },
1817
- support_ticket_reveals_need_cross_domain: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "support_ticket", target_type: "need" },
1889
+ // (UPG-677) support_ticket_reveals_need_cross_domain retired — near-duplicate
1890
+ // of support_ticket_reveals_need (the clean key, whose reverse_verb is now
1891
+ // revealed_by). See UPG_EDGE_MIGRATIONS['0.9.9'].
1818
1892
  churn_reason_reveals_need: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "churn_reason", target_type: "need" },
1819
1893
  // 5.2 Content & Knowledge Domain
1820
1894
  product_publishes_content_piece: { forward_verb: "publishes", reverse_verb: "published_by", classification: "hierarchy", source_type: "product", target_type: "content_piece" },
1821
1895
  product_documents_in_knowledge_base_article: { forward_verb: "documents_in", reverse_verb: "documented_by", classification: "hierarchy", source_type: "product", target_type: "knowledge_base_article" },
1822
1896
  product_expressed_via_brand_asset: { forward_verb: "expressed_via", reverse_verb: "expresses", classification: "hierarchy", source_type: "product", target_type: "brand_asset" },
1823
1897
  product_documented_in_document: { forward_verb: "documented_in", reverse_verb: "documents", classification: "hierarchy", source_type: "product", target_type: "document" },
1824
- product_prompted_via_prompt_template: { forward_verb: "prompted_via", reverse_verb: "prompts", classification: "hierarchy", source_type: "product", target_type: "prompt_template" },
1898
+ // (UPG-665) product_prompted_via_prompt_template retired — prompt_template
1899
+ // re-parented product → ai_model (ai_model_defines_prompt_template). See
1900
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1825
1901
  product_templated_via_documentation_template: { forward_verb: "templated_via", reverse_verb: "templates", classification: "hierarchy", source_type: "product", target_type: "documentation_template" },
1826
1902
  product_records_in_document: { forward_verb: "records_in", reverse_verb: "records", classification: "hierarchy", source_type: "product", target_type: "document" },
1827
1903
  content_strategy_scheduled_in_content_calendar: { forward_verb: "scheduled_in", reverse_verb: "schedules", classification: "hierarchy", source_type: "content_strategy", target_type: "content_calendar" },
@@ -1831,12 +1907,19 @@ var UPG_EDGE_CATALOG = {
1831
1907
  content_calendar_schedules_knowledge_base_article: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "knowledge_base_article" },
1832
1908
  content_calendar_schedules_brand_asset: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "brand_asset" },
1833
1909
  content_calendar_schedules_document: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "document" },
1834
- content_calendar_schedules_prompt_template: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "prompt_template" },
1910
+ // (UPG-665) content_calendar_schedules_prompt_template retired — prompt_template
1911
+ // re-homed to the ai_model containment tree (it is an AI artefact, not a
1912
+ // content-calendar-scheduled item). See UPG_EDGE_MIGRATIONS['0.9.9'].
1835
1913
  content_calendar_schedules_documentation_template: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "documentation_template" },
1836
1914
  content_piece_supports_messaging: { forward_verb: "supports", reverse_verb: "supported_by", classification: "cross-domain", source_type: "content_piece", target_type: "messaging" },
1837
1915
  content_piece_part_of_growth_campaign: { forward_verb: "part_of", reverse_verb: "includes", classification: "cross-domain", source_type: "content_piece", target_type: "growth_campaign" },
1838
1916
  knowledge_base_article_documents_feature: { forward_verb: "documents", reverse_verb: "documented_by", classification: "cross-domain", source_type: "knowledge_base_article", target_type: "feature" },
1839
- changelog_documents_release: { forward_verb: "documents", reverse_verb: "documented_in", classification: "cross-domain", source_type: "changelog", target_type: "release" },
1917
+ // (UPG-677) changelog_documents_release retired — inverse of the containment
1918
+ // edge release_documented_in_changelog (release → changelog, matching the
1919
+ // hierarchy release: ['changelog']). The "changelog documents release" read is
1920
+ // preserved by that edge's reverse_verb (documents). FLAG: this is the only
1921
+ // inverse pair collapsed; feature↔bug is kept (distinct classification +
1922
+ // semantic). See UPG_EDGE_MIGRATIONS['0.9.9'].
1840
1923
  prompt_template_powers_feature: { forward_verb: "powers", reverse_verb: "powered_by", classification: "cross-domain", source_type: "prompt_template", target_type: "feature" },
1841
1924
  content_theme_targets_persona: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "content_theme", target_type: "persona" },
1842
1925
  // v0.7.2 (UPG-571 §1): topic-cluster / content-pillar model; themes organize pieces. Connects the isolated `content_theme` AND `content_piece` members.
@@ -1872,7 +1955,9 @@ var UPG_EDGE_CATALOG = {
1872
1955
  // 6.1 Team & Organisation Domain
1873
1956
  product_staffed_by_team: { forward_verb: "staffed_by", reverse_verb: "staffs", classification: "hierarchy", source_type: "product", target_type: "team" },
1874
1957
  product_influenced_by_stakeholder: { forward_verb: "influenced_by", reverse_verb: "influences", classification: "hierarchy", source_type: "product", target_type: "stakeholder" },
1875
- product_decided_via_decision_hierarchy: { forward_verb: "decided_via", reverse_verb: "decides", classification: "hierarchy", source_type: "product", target_type: "decision" },
1958
+ // (UPG-677) product_decided_via_decision_hierarchy retired — duplicate of
1959
+ // product_decided_via_decision (the clean key). See
1960
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1876
1961
  product_organised_into_department: { forward_verb: "organised_into", reverse_verb: "organises", classification: "hierarchy", source_type: "product", target_type: "department" },
1877
1962
  department_contains_team: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "department", target_type: "team" },
1878
1963
  department_includes_stakeholder: { forward_verb: "includes", reverse_verb: "included_in", classification: "hierarchy", source_type: "department", target_type: "stakeholder" },
@@ -1976,7 +2061,9 @@ var UPG_EDGE_CATALOG = {
1976
2061
  dashboard_contains_report: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "dashboard", target_type: "report" },
1977
2062
  dashboard_contains_experiment_run: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "dashboard", target_type: "experiment_run" },
1978
2063
  metric_measures_metric: { forward_verb: "measures", reverse_verb: "measured_by", classification: "cross-domain", source_type: "metric", target_type: "metric" },
1979
- metric_measures_metric_cross_domain: { forward_verb: "measures", reverse_verb: "measured_by", classification: "cross-domain", source_type: "metric", target_type: "metric" },
2064
+ // (UPG-677) metric_measures_metric_cross_domain retired — byte-identical
2065
+ // shadow of metric_measures_metric (the clean key). See
2066
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1980
2067
  // `experiment_tests_hypothesis` dropped, superseded by
1981
2068
  // the canonical `experiment_run_validates_hypothesis` (causal) introduced
1982
2069
  // in v0.2.6. Authors expressing "this experiment tests this hypothesis"
@@ -1998,7 +2085,11 @@ var UPG_EDGE_CATALOG = {
1998
2085
  product_area_contains_product_area: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "product_area", target_type: "product_area" },
1999
2086
  // decision B: product_area groups features (the natural "Studio area owns 6 features" mental model)
2000
2087
  product_area_contains_feature: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "product_area", target_type: "feature" },
2001
- product_categorised_in_product_area: { forward_verb: "categorised_in", reverse_verb: "categorises", classification: "hierarchy", source_type: "product", target_type: "product_area" },
2088
+ // (UPG-677) product_categorised_in_product_area retired — inverse of
2089
+ // product_area_contains_product (the kept containment direction:
2090
+ // product_area → product, matching the UPG parent→child convention). FLAG:
2091
+ // the inverse "product categorised in area" read is preserved by the kept
2092
+ // edge's reverse_verb (belongs_to). See UPG_EDGE_MIGRATIONS['0.9.9'].
2002
2093
  // 8.2 Cross-Product Edges
2003
2094
  // direct product-level shorthand edges for market-intelligence shapes.
2004
2095
  // These are shortcuts: canonical deep paths go via competitive_analysis and gtm_strategy.
@@ -2062,7 +2153,9 @@ var UPG_EDGE_CATALOG = {
2062
2153
  insight_reveals_desired_outcome: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "insight", target_type: "desired_outcome" },
2063
2154
  insight_informs_job: { forward_verb: "informs", reverse_verb: "informed_by", classification: "cross-domain", source_type: "insight", target_type: "job" },
2064
2155
  insight_characterises_persona: { forward_verb: "characterises", reverse_verb: "characterised_by", classification: "cross-domain", source_type: "insight", target_type: "persona" },
2065
- insight_validates_need_cross_domain: { forward_verb: "validates", reverse_verb: "validated_by", classification: "cross-domain", source_type: "insight", target_type: "need" },
2156
+ // (UPG-677) insight_validates_need_cross_domain retired — byte-identical
2157
+ // shadow of insight_validates_need (the clean key). See
2158
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
2066
2159
  observation_reveals_need: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "observation", target_type: "need" },
2067
2160
  observation_characterises_persona: { forward_verb: "characterises", reverse_verb: "characterised_by", classification: "cross-domain", source_type: "observation", target_type: "persona" },
2068
2161
  quote_evidences_need: { forward_verb: "evidences", reverse_verb: "evidenced_by", classification: "cross-domain", source_type: "quote", target_type: "need" },
@@ -2076,7 +2169,9 @@ var UPG_EDGE_CATALOG = {
2076
2169
  insight_inspires_design_concept: { forward_verb: "inspires", reverse_verb: "inspired_by", classification: "cross-domain", source_type: "insight", target_type: "design_concept" },
2077
2170
  observation_yields_insight: { forward_verb: "yields", reverse_verb: "yielded_by", classification: "cross-domain", source_type: "observation", target_type: "insight" },
2078
2171
  insight_refines_into_insight: { forward_verb: "refines_into", reverse_verb: "refined_from", classification: "hierarchy", source_type: "insight", target_type: "insight" },
2079
- insight_informs_opportunity_cross_domain: { forward_verb: "informs", reverse_verb: "informed_by", classification: "cross-domain", source_type: "insight", target_type: "opportunity" },
2172
+ // (UPG-677) insight_informs_opportunity_cross_domain retired — byte-identical
2173
+ // shadow of insight_informs_opportunity (the clean key). See
2174
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
2080
2175
  insight_validates_persona: { forward_verb: "validates", reverse_verb: "validated_by", classification: "cross-domain", source_type: "insight", target_type: "persona" },
2081
2176
  // Validation-to-Discovery Feedback Loops
2082
2177
  learning_validates_opportunity: { forward_verb: "validates", reverse_verb: "validated_by", classification: "cross-domain", source_type: "learning", target_type: "opportunity" },
@@ -2134,7 +2229,9 @@ var UPG_EDGE_CATALOG = {
2134
2229
  customer_health_score_composed_of_metric: { forward_verb: "composed_of", reverse_verb: "composes", classification: "cross-domain", source_type: "customer_health_score", target_type: "metric" },
2135
2230
  service_level_agreement_measures_metric: { forward_verb: "measures", reverse_verb: "measured_by", classification: "cross-domain", source_type: "service_level_agreement", target_type: "metric" },
2136
2231
  // Content
2137
- prompt_template_targets_ai_model: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "prompt_template", target_type: "ai_model" },
2232
+ // (UPG-665) prompt_template_targets_ai_model retired — superseded by the
2233
+ // containment edge ai_model_defines_prompt_template (the model owns its
2234
+ // templates). See UPG_EDGE_MIGRATIONS['0.9.9'].
2138
2235
  // Education
2139
2236
  education_program_targets_persona: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "education_program", target_type: "persona" },
2140
2237
  // Programs
@@ -2298,13 +2395,13 @@ var UPG_EDGE_CATALOG = {
2298
2395
  messaging_used_in_launch: { forward_verb: "used_in", reverse_verb: "uses", classification: "cross-domain", source_type: "messaging", target_type: "launch" },
2299
2396
  messaging_enables_sales_motion: { forward_verb: "enables", reverse_verb: "enabled_by", classification: "cross-domain", source_type: "messaging", target_type: "sales_motion" },
2300
2397
  // Test Card + Learning Card (Strategyzer): canonical validation flow
2301
- // hypothesis → test_plan → experiment_run → evidence → learning → decision.
2302
- // The catalog already had the hypothesis → test_plan and experiment_run →
2303
- // {hypothesis, evidence, learning, decision} edges. Missing: the
2304
- // test_plan ran-as bridge (mirrors `experiment_plan_ran_as_experiment_run`
2305
- // for the test_plan/test-card pair), the evidence → learning interpretation
2306
- // step, and the learning → decision commitment step.
2307
- test_plan_ran_as_experiment_run: { forward_verb: "ran_as", reverse_verb: "ran_for", classification: "hierarchy", source_type: "test_plan", target_type: "experiment_run" },
2398
+ // hypothesis → experiment_plan → experiment → experiment_run → evidence →
2399
+ // learning → decision (UPG-664). The "Test Design" card is the
2400
+ // `experiment_plan`; the plan designs the experiment
2401
+ // (`experiment_plan_designs_experiment`), which is executed as run(s). The
2402
+ // former `test_plan_ran_as_experiment_run` bridge was retired when `test_plan`
2403
+ // re-homed to QA (UPG-678) — see UPG_EDGE_MIGRATIONS['0.9.9']. This block keeps
2404
+ // the evidence → learning interpretation and learning → decision commitment.
2308
2405
  evidence_interpreted_as_learning: { forward_verb: "interpreted_as", reverse_verb: "interpreted_from", classification: "causal", source_type: "evidence", target_type: "learning" },
2309
2406
  learning_informs_decision: { forward_verb: "informs", reverse_verb: "informed_by", classification: "causal", source_type: "learning", target_type: "decision" },
2310
2407
  // Opportunity Canvas: Patton's "Assumptions" slot is meant to capture the
@@ -3030,7 +3127,6 @@ var UPG_VALID_CHILDREN = {
3030
3127
  "knowledge_base_article",
3031
3128
  "brand_asset",
3032
3129
  "document",
3033
- "prompt_template",
3034
3130
  "documentation_template",
3035
3131
  // Legal
3036
3132
  "legal_entity",
@@ -3063,6 +3159,7 @@ var UPG_VALID_CHILDREN = {
3063
3159
  "a11y_audit",
3064
3160
  "a11y_annotation",
3065
3161
  // Quality Assurance
3162
+ "test_plan",
3066
3163
  "test_suite",
3067
3164
  "qa_session",
3068
3165
  "test_coverage_report",
@@ -3112,7 +3209,10 @@ var UPG_VALID_CHILDREN = {
3112
3209
  "api_ecosystem",
3113
3210
  "developer_portal",
3114
3211
  // Portfolio
3115
- "product_area",
3212
+ // (UPG-677) product no longer lists product_area as a child: the
3213
+ // product↔product_area containment is the single direction
3214
+ // product_area_contains_product (product_area → product). The inverse
3215
+ // product_categorised_in_product_area edge was collapsed.
3116
3216
  // Workspace
3117
3217
  "workspace"
3118
3218
  ],
@@ -3127,22 +3227,27 @@ var UPG_VALID_CHILDREN = {
3127
3227
  // belief), not the legacy hypothesis type.
3128
3228
  solution: ["hypothesis", "prototype", "metric"],
3129
3229
  // ── Validation hierarchy ────────────────────────────────────────────────────
3130
- // hypothesis is re-promoted as canonical. hypothesis_evidence
3131
- // collapsed into evidence; relationship expressed via hypothesis_has_evidence
3132
- // edge. evidence.direction carries supports/refutes/neutral semantics.
3133
- hypothesis: ["experiment_plan", "test_plan", "research_plan", "evidence"],
3134
- // experiment is canonical. It can contain experiment_plan/run
3135
- // sub-artefacts (the plan/run split is opt-in for finer granularity).
3136
- experiment: ["experiment_plan", "experiment_run", "learning", "evidence"],
3137
- experiment_plan: ["experiment_run"],
3138
- // v0.5.5 (UPG-528 Part 2a): mirrors `experiment_plan_ran_as_experiment_run`
3139
- // for the Strategyzer Test Card + Learning Card flow. A test plan (the
3140
- // upfront experiment design captured on the Test Card) runs as one or more
3141
- // experiment runs that produce evidence and learning.
3142
- test_plan: ["experiment_run"],
3230
+ // (UPG-664) The validation chain is a single-parent line:
3231
+ // hypothesis ▷ experiment_plan ▷ experiment ▷ experiment_run.
3232
+ // hypothesis is canonical (hypothesis_evidence collapsed into evidence;
3233
+ // relationship expressed via hypothesis_has_evidence edge; evidence.direction
3234
+ // carries supports/refutes/neutral semantics). `test_plan` re-homed to the
3235
+ // QA/testing domain (UPG-678), so it is no longer a hypothesis child.
3236
+ hypothesis: ["experiment_plan", "research_plan", "evidence"],
3237
+ // experiment_plan is the canonical validation PLAN (graduated stable,
3238
+ // UPG-664). It designs the experiment it produces; the plan owns the
3239
+ // experiment in the containment line. It also retains experiment_run as a
3240
+ // child for the v0.2.6 split-migration path
3241
+ // (`experiment_plan_ran_as_experiment_run`), where a legacy experiment routes
3242
+ // directly to a plan + run pair.
3243
+ experiment_plan: ["experiment", "experiment_run"],
3244
+ // experiment is the canonical structured test. It owns its run(s) and the
3245
+ // learning/evidence it produces. The optional experiment_run child captures
3246
+ // the multi-run / replication case.
3247
+ experiment: ["experiment_run", "learning", "evidence"],
3143
3248
  // experiment_run carries learning/evidence/metric children (run produces
3144
- // evidence) and self-nests for replications/multi-armed runs that share
3145
- // a parent plan.
3249
+ // evidence) and self-nests for replications/multi-armed runs (V9: this is
3250
+ // where self-nesting genuinely lives, not on `experiment`).
3146
3251
  experiment_run: ["learning", "evidence", "metric", "experiment_run"],
3147
3252
  // ── OKR hierarchy ──────────────────────────────────────────────────────────
3148
3253
  objective: ["key_result", "metric"],
@@ -3153,8 +3258,9 @@ var UPG_VALID_CHILDREN = {
3153
3258
  strategic_pillar: ["strategic_theme", "capability", "value_stream", "decision"],
3154
3259
  // v0.5.4 (UPG-511): objectives are the specific quarterly bets *within* a
3155
3260
  // strategic theme. The theme is the multi-quarter focus area; objectives are
3156
- // subordinate. Pairs with `objective_rolls_up_to_strategic_theme` in the edge
3157
- // catalog (hierarchy, strategic_theme → objective, reverse_verb: rolls_up_to).
3261
+ // subordinate. Pairs with `strategic_theme_contains_objective` in the edge
3262
+ // catalog (hierarchy, strategic_theme → objective, reverse_verb: rolls_up_to;
3263
+ // renamed from objective_rolls_up_to_strategic_theme in UPG-676).
3158
3264
  strategic_theme: ["initiative", "objective"],
3159
3265
  initiative: ["assumption"],
3160
3266
  // v0.5.2 (UPG-528): capability decomposes into sub-capabilities (Wardley
@@ -3334,7 +3440,6 @@ var UPG_VALID_CHILDREN = {
3334
3440
  "knowledge_base_article",
3335
3441
  "brand_asset",
3336
3442
  "document",
3337
- "prompt_template",
3338
3443
  "documentation_template"
3339
3444
  ],
3340
3445
  // ── Operations & CS hierarchy ────────────────────────────────────────────────
@@ -3439,6 +3544,10 @@ var UPG_VALID_CHILDREN = {
3439
3544
  // a learning_path can include its terminal certification.
3440
3545
  learning_path: ["tutorial", "certification"],
3441
3546
  // ── Quality Assurance & Testing hierarchy ────────────────────────────────────
3547
+ // (UPG-678) test_plan re-homed validation → QA. It is the QA planning layer:
3548
+ // the verification approach (scope, environments, pass criteria) that the
3549
+ // test suites execute. A test_plan groups the suites that carry it out.
3550
+ test_plan: ["test_suite", "test_environment"],
3442
3551
  test_suite: [
3443
3552
  "test_case",
3444
3553
  "regression_test",
@@ -3482,8 +3591,11 @@ var UPG_VALID_CHILDREN = {
3482
3591
  "paywall"
3483
3592
  ],
3484
3593
  // ── AI/ML Operations hierarchy ───────────────────────────────────────────────
3594
+ // (UPG-665) The prompt abstraction is corrected: ai_model → prompt_template →
3595
+ // prompt_version (a model has templates; each template has versions). The
3596
+ // model no longer owns prompt_version directly.
3485
3597
  ai_model: [
3486
- "prompt_version",
3598
+ "prompt_template",
3487
3599
  "eval_benchmark",
3488
3600
  "ai_cost_tracker",
3489
3601
  "hallucination_report",
@@ -3493,6 +3605,8 @@ var UPG_VALID_CHILDREN = {
3493
3605
  "ai_dataset",
3494
3606
  "ai_trace"
3495
3607
  ],
3608
+ // prompt_template owns its prompt_versions the way a file owns its commits.
3609
+ prompt_template: ["prompt_version"],
3496
3610
  // v0.5.7 (UPG-528 Part 2c): benchmarks define the metric set they measure
3497
3611
  // (HELM, MLPerf, BIG-bench all spec a metric list). metric already has many
3498
3612
  // hierarchy parents (outcome, objective, key_result, solution, data_source).
@@ -7751,6 +7865,69 @@ function getSplitMigrations(fromVersion, toVersion) {
7751
7865
  return result;
7752
7866
  }
7753
7867
  var UPG_EDGE_MIGRATIONS = {
7868
+ "0.9.9": [
7869
+ // (since v0.9.9, UPG-664/UPG-678) Validation experiment-model + test_plan
7870
+ // re-home. The validation chain is reshaped to the single-parent line
7871
+ // hypothesis → experiment_plan → experiment → experiment_run, `experiment`
7872
+ // gains its stable hypothesis loop, and `test_plan` re-homes to the QA
7873
+ // domain (its planning role absorbed by `experiment_plan`).
7874
+ //
7875
+ // The plan→experiment containment inverts: the old `experiment_has_plan`
7876
+ // (experiment → experiment_plan, hierarchy) becomes
7877
+ // `experiment_plan_designs_experiment` (experiment_plan → experiment). Flip
7878
+ // swaps endpoints. Endpoint guards reference the post-flip orientation: the
7879
+ // original source (experiment) becomes the new target, the original target
7880
+ // (experiment_plan) becomes the new source.
7881
+ { kind: "rename", from: "experiment_has_plan", to: "experiment_plan_designs_experiment", flip: true, requires_source_type: "experiment", requires_target_type: "experiment_plan", reason: "Validation experiment-model reshape (UPG-664). The plan owns the experiment it designs; the containment direction inverts from experiment \u2192 experiment_plan to experiment_plan \u2192 experiment. Flip swaps endpoints so the surviving edge reads plan-designs-experiment." },
7882
+ // test_plan re-homed validation → QA (UPG-678). Its two validation-side
7883
+ // edges are dropped: a hypothesis no longer plans via a QA test_plan (it
7884
+ // requires an experiment_plan), and the test_plan → experiment_run ran-as
7885
+ // bridge is superseded by the experiment_plan → experiment flow.
7886
+ { kind: "drop", from: "hypothesis_planned_via_test_plan", reason: "test_plan re-homed validation \u2192 QA (UPG-678). A hypothesis no longer plans via test_plan; the validation plan is experiment_plan (hypothesis_requires_experiment_plan)." },
7887
+ { kind: "drop", from: "test_plan_ran_as_experiment_run", reason: "test_plan re-homed validation \u2192 QA (UPG-678). The Strategyzer Test Card flow now runs hypothesis \u2192 experiment_plan \u2192 experiment \u2192 experiment_run; the test_plan \u2192 experiment_run bridge is superseded by experiment_plan_designs_experiment." },
7888
+ // (since v0.9.9, UPG-665) AI prompt-model correction. The prompt abstraction
7889
+ // is inverted to the correct ai_model → prompt_template → prompt_version
7890
+ // containment. The backwards/obsolete edges are dropped, and the
7891
+ // prompt_template → ai_model "targets" relationship is flipped into the new
7892
+ // ai_model → prompt_template ownership.
7893
+ { kind: "rename", from: "prompt_template_targets_ai_model", to: "ai_model_defines_prompt_template", flip: true, requires_source_type: "prompt_template", requires_target_type: "ai_model", reason: 'AI prompt-model correction (UPG-665). The model owns its templates; the prompt_template \u2192 ai_model "targets" edge becomes the ai_model \u2192 prompt_template ownership edge. Flip swaps endpoints so the surviving edge reads model-defines-template.' },
7894
+ { kind: "drop", from: "prompt_version_evolves_prompt_template", reason: "AI prompt-model correction (UPG-665). The backwards prompt_version \u2192 prompt_template edge is retired; prompt_template now contains its versions (prompt_template_contains_prompt_version)." },
7895
+ { kind: "drop", from: "ai_model_prompted_via_prompt_version", reason: "AI prompt-model correction (UPG-665). The model no longer owns prompt_version directly; the chain is ai_model \u2192 prompt_template \u2192 prompt_version." },
7896
+ { kind: "drop", from: "product_prompted_via_prompt_template", reason: "AI prompt-model correction (UPG-665). prompt_template re-parented product \u2192 ai_model (ai_model_defines_prompt_template); the product-level containment is removed." },
7897
+ { kind: "drop", from: "content_calendar_schedules_prompt_template", reason: "AI prompt-model correction (UPG-665). prompt_template re-homed into the ai_model containment tree; it is an AI artefact, not a content-calendar-scheduled item." },
7898
+ // (since v0.9.9, UPG-676) Strategic-theme containment edge rename. The
7899
+ // malformed `objective_rolls_up_to_strategic_theme` (key read object-first
7900
+ // while source_type was strategic_theme; forward_verb was the malformed
7901
+ // `contains_objective`) is renamed to the clean source-first
7902
+ // `strategic_theme_contains_objective`. Endpoints unchanged
7903
+ // (strategic_theme → objective); only the key and forward verb change.
7904
+ { kind: "rename", from: "objective_rolls_up_to_strategic_theme", to: "strategic_theme_contains_objective", requires_source_type: "strategic_theme", requires_target_type: "objective", reason: "Strategic-theme containment edge rename (UPG-676). The key now reads source-first and the forward verb is the clean `contains` (was the malformed `contains_objective`). Endpoints unchanged (strategic_theme \u2192 objective); satisfies the F2 prefix gate." },
7905
+ // (since v0.9.9, UPG-677) Duplicate / shadow edge collapse. Each retired key
7906
+ // is dual-read to the surviving canonical key. Tier 1 = byte-identical
7907
+ // shadows; Tier 2 = tense/suffix twins; Tier 3 = near-synonyms collapsed per
7908
+ // Captain's lean. Inverse pairs flip the direction onto the kept containment
7909
+ // edge. Collapsing these lowers the edge-duplicate gate collision bound.
7910
+ // ── Tier 1: byte-identical shadows ──
7911
+ { kind: "rename", from: "insight_informs_opportunity_cross_domain", to: "insight_informs_opportunity", requires_source_type: "insight", requires_target_type: "opportunity", reason: "Duplicate collapse (UPG-677). Byte-identical shadow of insight_informs_opportunity; the suffixed twin never resolved." },
7912
+ { kind: "rename", from: "product_experiences_incident_hierarchy", to: "product_experiences_incident", requires_source_type: "product", requires_target_type: "incident", reason: "Duplicate collapse (UPG-677). Byte-identical shadow of product_experiences_incident; the suffixed twin never resolved." },
7913
+ { kind: "rename", from: "value_proposition_targets_persona_cross_domain", to: "value_proposition_targets_persona", requires_source_type: "value_proposition", requires_target_type: "persona", reason: "Duplicate collapse (UPG-677). Byte-identical shadow of value_proposition_targets_persona; the suffixed twin never resolved." },
7914
+ { kind: "rename", from: "metric_measures_metric_cross_domain", to: "metric_measures_metric", requires_source_type: "metric", requires_target_type: "metric", reason: "Duplicate collapse (UPG-677). Byte-identical shadow of metric_measures_metric; the suffixed twin never resolved." },
7915
+ { kind: "rename", from: "insight_validates_need_cross_domain", to: "insight_validates_need", requires_source_type: "insight", requires_target_type: "need", reason: "Duplicate collapse (UPG-677). Byte-identical shadow of insight_validates_need; the suffixed twin never resolved." },
7916
+ // ── Tier 2: tense / suffix twins ──
7917
+ { kind: "rename", from: "metric_decomposed_into_metric", to: "metric_decomposes_into_metric", requires_source_type: "metric", requires_target_type: "metric", reason: "Duplicate collapse (UPG-677). Tense-twin of the active-voice metric_decomposes_into_metric." },
7918
+ { kind: "rename", from: "product_decided_via_decision_hierarchy", to: "product_decided_via_decision", requires_source_type: "product", requires_target_type: "decision", reason: "Duplicate collapse (UPG-677). Suffix-twin of product_decided_via_decision." },
7919
+ { kind: "rename", from: "support_ticket_reveals_need_cross_domain", to: "support_ticket_reveals_need", requires_source_type: "support_ticket", requires_target_type: "need", reason: "Duplicate collapse (UPG-677). Near-duplicate of support_ticket_reveals_need (whose reverse_verb is normalised to revealed_by)." },
7920
+ // ── Tier 3: near-synonyms (Captain's lean = collapse) ──
7921
+ { kind: "rename", from: "outcome_tracked_by_metric", to: "outcome_measured_by_metric", requires_source_type: "outcome", requires_target_type: "metric", reason: "Duplicate collapse (UPG-677). Near-synonym of outcome_measured_by_metric (keep measured_by, drop tracked_by)." },
7922
+ { kind: "rename", from: "key_result_tracked_by_metric", to: "key_result_quantified_by_metric", requires_source_type: "key_result", requires_target_type: "metric", reason: "Duplicate collapse (UPG-677). Near-synonym of key_result_quantified_by_metric (keep quantified_by, drop tracked_by)." },
7923
+ { kind: "rename", from: "business_model_reaches_via_distribution_channel", to: "business_model_distributes_via_distribution_channel", requires_source_type: "business_model", requires_target_type: "distribution_channel", reason: "Duplicate collapse (UPG-677). Near-synonym of business_model_distributes_via_distribution_channel (the Business Model Canvas Channels verb)." },
7924
+ // ── Inverse pairs: flip onto the kept containment edge ──
7925
+ // Flip guards reference the PRE-flip (original-edge) endpoints: the original
7926
+ // source becomes the new target after flip, so requires_source_type matches
7927
+ // the new edge's target_type and requires_target_type matches its source_type.
7928
+ { kind: "rename", from: "product_categorised_in_product_area", to: "product_area_contains_product", flip: true, requires_source_type: "product", requires_target_type: "product_area", reason: 'Duplicate collapse (UPG-677). Inverse of the containment edge product_area_contains_product (product_area \u2192 product). Flip swaps endpoints; the "categorised in" read survives as the reverse_verb belongs_to.' },
7929
+ { kind: "rename", from: "changelog_documents_release", to: "release_documented_in_changelog", flip: true, requires_source_type: "changelog", requires_target_type: "release", reason: 'Duplicate collapse (UPG-677). Inverse of the containment edge release_documented_in_changelog (release \u2192 changelog, matching hierarchy release: [changelog]). Flip swaps endpoints; the "changelog documents release" read survives as the reverse_verb documents.' }
7930
+ ],
7754
7931
  "0.9.2": [
7755
7932
  // (since v0.9.2, UPG-663) Journey-model disambiguation. A journey_phase
7756
7933
  // is a temporal BAND over the journey's single step timeline, not a
@@ -9369,6 +9546,7 @@ var UPG_PROPERTY_SCHEMA = {
9369
9546
  // CustomerJourneyStageProperties: Customer journey stage.
9370
9547
  customer_journey_stage: {
9371
9548
  stage_type: { type: "string", enum: ["awareness", "acquisition", "activation", "retention", "revenue", "referral"], description: "Which pirate metric (AAARRR) this stage maps to" },
9549
+ stage_order: { type: "number", description: "Display order along the lifecycle timeline (0-indexed). The `*_order` convention shared with `journey_phase.phase_order` and `journey_step.step_order` (UPG-675 / CS-8), so two same-`stage_type` stages are orderable." },
9372
9550
  avg_duration: { type: "string", description: "Average time a customer spends in this stage" },
9373
9551
  conversion_rate: { type: "number", description: "Percentage of customers who advance to the next stage" }
9374
9552
  },
@@ -9760,7 +9938,7 @@ var UPG_PROPERTY_SCHEMA = {
9760
9938
  observed_at: { type: "string", description: "ISO date observed." },
9761
9939
  source: { type: "string", description: "Free-text provenance note" }
9762
9940
  },
9763
- // ExperimentProperties: A structured activity designed to test a hypothesis.
9941
+ // ExperimentProperties: A structured test designed to validate a hypothesis. The canonical unit of
9764
9942
  experiment: {
9765
9943
  method: { type: "string", description: 'Experimental method (e.g. "A/B test", "usability study", "smoke test")' },
9766
9944
  start_date: { type: "string", description: "ISO start date" },
@@ -9770,10 +9948,11 @@ var UPG_PROPERTY_SCHEMA = {
9770
9948
  expected_lift_unit: { type: "string", enum: ["percentage", "absolute", "ratio"], description: "Unit of `expected_lift`" },
9771
9949
  actual_lift: { type: "number", description: "Observed change in the primary metric" }
9772
9950
  },
9773
- // ExperimentPlanProperties: Planning intent for a structured test of a hypothesis (UCS pattern P4: work-unit).
9951
+ // ExperimentPlanProperties: The validation PLAN: the design for a structured test of a hypothesis
9774
9952
  experiment_plan: {
9775
9953
  method: { type: "string", enum: ["a_b_test", "multivariate", "qual_interview", "prototype_test", "fake_door", "wizard_of_oz", "longitudinal"], description: "Experimental method. Drives renderer and analysis tooling." },
9776
9954
  success_criteria: { type: "string", description: 'Plain-English description of "passing"' },
9955
+ sample_size: { type: "number", description: "Targeted participants or observations for the planned test. Absorbed from `test_plan` (UPG-678) when it re-homed to QA; the planning sample size now lives on the validation plan." },
9777
9956
  projected_reach: {
9778
9957
  type: "assessment",
9779
9958
  scale_id: "reach_5",
@@ -10309,7 +10488,7 @@ var UPG_PROPERTY_SCHEMA = {
10309
10488
  // JourneyStepProperties: Single step within a user journey. A user-moment on the journey's single
10310
10489
  journey_step: {
10311
10490
  step_order: { type: "number", description: "Display order within the journey's step timeline (0-indexed). The scalar ordering convention shared with `journey_phase.phase_order` and `journey_action.action_order` (UPG-663). For branching journeys, the explicit `journey_step_precedes_journey_step` edge captures the chain." },
10312
- touchpoint: { type: "string", description: "Interaction touchpoint" },
10491
+ touchpoint: { type: "string", description: "@deprecated since v0.9.9 (UPG-675). Touchpoints are modelled in two places, neither of which is this string: the `touchpoint` *entity* is the cross-channel customer-success layer (`touchpoint_occurs_in_journey_step`), and `journey_action` is the in-product blueprint layer (the finest band of a `journey_step`). Use one of those rather than a free-text string here. Removed in a future major." },
10313
10492
  channel: { type: "string", description: 'Channel (e.g. "web", "email", "in-store")' },
10314
10493
  emotion_score: {
10315
10494
  type: "assessment",
@@ -11579,12 +11758,13 @@ var UPG_PROPERTY_SCHEMA = {
11579
11758
  env_status: { type: "string", enum: ["available", "in_use", "maintenance", "unavailable"], description: "Current availability status of the environment" },
11580
11759
  config: { type: "string", description: "Configuration details (e.g. OS, browser version, database seed)" }
11581
11760
  },
11582
- // TestPlanProperties: TestPlan entity.
11761
+ // TestPlanProperties: Test plan: the QA verification procedure for a product or release.
11583
11762
  test_plan: {
11584
- plan_type: { type: "string", enum: ["usability", "concept", "ab_test", "beta", "smoke"], description: "Test type" },
11585
- sample_size: { type: "number", description: "Participants or observations" },
11586
- duration: { type: "string", description: 'Run duration. @example "2 weeks"' },
11587
- success_criteria: { type: "string", description: "Criteria determining whether the test passes" }
11763
+ test_scope: { type: "string", description: 'What the plan covers (e.g. "checkout flow", "auth module", "release 2.4").' },
11764
+ plan_type: { type: "string", enum: ["release", "regression", "integration", "acceptance", "smoke", "exploratory"], description: "Kind of verification effort this plan governs." },
11765
+ environments: { type: "object", description: "Environments the plan exercises (mirrors `TestEnvironmentProperties.env_type`)." },
11766
+ entry_criteria: { type: "string", description: "Conditions that must hold before execution may begin." },
11767
+ pass_criteria: { type: "string", description: "Exit / pass criteria determining whether the plan succeeds." }
11588
11768
  },
11589
11769
  // TestResultProperties: Single test execution result.
11590
11770
  test_result: {
@@ -11650,7 +11830,7 @@ var UPG_PROPERTY_SCHEMA = {
11650
11830
  },
11651
11831
  // TouchpointProperties: Customer touchpoint.
11652
11832
  touchpoint: {
11653
- touchpoint_channel: { type: "string", description: 'Channel where the interaction occurs (e.g. "email", "in-app", "call")' },
11833
+ touchpoint_channel: { type: "string", enum: ["in_app", "email", "phone", "chat", "sms", "in_person", "mail"], description: "Medium (modality) of the interaction: how it physically happens. Distinct from a go-to-market *channel* (`marketing_channel`/`acquisition_channel`/ `distribution_channel`), which are market routes, not interaction media. (UPG-679)" },
11654
11834
  touchpoint_type: { type: "string", enum: ["reactive", "proactive", "automated"], description: "Whether the touchpoint is initiated by the customer, CSM, or system" },
11655
11835
  satisfaction_score: { type: "number", description: "Customer satisfaction score for this touchpoint" }
11656
11836
  },
@@ -11703,7 +11883,7 @@ var UPG_PROPERTY_SCHEMA = {
11703
11883
  // UserJourneyProperties: User journey map.
11704
11884
  user_journey: {
11705
11885
  scope: { type: "string", description: 'Scope (e.g. "end-to-end onboarding")' },
11706
- journey_type: { type: "string", enum: ["current_state", "future_state", "day_in_the_life", "service_blueprint"], description: "Maps current or future state" },
11886
+ journey_type: { type: "string", enum: ["current_state", "future_state", "day_in_the_life"], description: "Maps current or future state" },
11707
11887
  scenario: { type: "string", description: "Scenario context" }
11708
11888
  },
11709
11889
  // UserStoryProperties: "As X, I want Y so Z" templated promise on a user story (UCS pattern P5).
@@ -17773,9 +17953,10 @@ var PRIORITY_LABELS = [
17773
17953
  {
17774
17954
  id: "experiment",
17775
17955
  canonical_label: "Experiment",
17956
+ // V6 (UPG-664): dropped 'test' (too generic; collides) and 'validation'
17957
+ // (the domain name). The deprecated growth/pricing/ab experiment types
17958
+ // redirect to `experiment` (V2), so their labels stay here.
17776
17959
  alt_labels: [
17777
- "test",
17778
- "validation",
17779
17960
  "ab test",
17780
17961
  "a/b test",
17781
17962
  "split test",
@@ -18231,8 +18412,8 @@ var STANDARD_LABELS = {
18231
18412
  design_sprint: { alt_labels: ["sprint", "gv sprint", "google ventures sprint"] },
18232
18413
  // Validation layer
18233
18414
  learning: { alt_labels: ["validated learning", "lesson learned", "takeaway"] },
18234
- // 'experiment plan' stripped (P-B / UPG-670): canonical label of the distinct `experiment_plan` type.
18235
- test_plan: { alt_labels: ["test strategy", "validation plan"] },
18415
+ // test_plan re-homed validation → QA (UPG-678); its label entry now lives in
18416
+ // the Quality Assurance & Testing layer below.
18236
18417
  research_plan: { alt_labels: ["study plan", "research brief"] },
18237
18418
  evidence: { alt_labels: ["proof", "supporting data", "signal"] },
18238
18419
  variant: { alt_labels: ["test variant", "experiment arm", "variation"] },
@@ -18486,6 +18667,10 @@ var STANDARD_LABELS = {
18486
18667
  help_video: { alt_labels: ["tutorial video", "screencast", "how-to video"] },
18487
18668
  learning_path: { alt_labels: ["curriculum", "course track", "learning journey"] },
18488
18669
  // Quality Assurance & Testing layer
18670
+ // test_plan re-homed validation → QA (UPG-678): QA-shaped labels only.
18671
+ // 'validation plan' and 'experiment plan' stripped — those belong to the
18672
+ // validation-side `experiment_plan` type.
18673
+ test_plan: { alt_labels: ["test strategy", "qa plan", "master test plan"] },
18489
18674
  test_suite: { alt_labels: ["test collection", "test group"] },
18490
18675
  test_case: { alt_labels: ["test", "test scenario", "verification step"] },
18491
18676
  qa_session: { alt_labels: ["testing session", "exploratory test", "qa round"] },
@@ -18857,11 +19042,13 @@ var DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK = {
18857
19042
  seqStep(
18858
19043
  8,
18859
19044
  "Test",
18860
- // DT-PB-1: was `experiment` — which resolves to NO canonical edge with
18861
- // hypothesis or test_plan, forcing an orphan. `experiment_run` is the
18862
- // hypothesis-linked test unit (experiment_run_validates_hypothesis,
18863
- // test_plan_ran_as_experiment_run, experiment_run_yields_evidence).
18864
- ["experiment_run", "test_plan", "evidence"],
19045
+ // DT-PB-1: was `experiment` — which resolved to NO canonical edge with
19046
+ // hypothesis, forcing an orphan. `experiment_run` is the hypothesis-linked
19047
+ // test unit (experiment_run_validates_hypothesis, experiment_run_yields_evidence).
19048
+ // (UPG-664) `test_plan` re-homed to QA (UPG-678); the validation plan is
19049
+ // `experiment_plan`, which designs the experiment
19050
+ // (experiment_plan_designs_experiment) and runs as experiment_run(s).
19051
+ ["experiment_run", "experiment_plan", "evidence"],
18865
19052
  "Validate with targeted experiment runs. Close the loop between research and action."
18866
19053
  )
18867
19054
  ]
@@ -20237,19 +20424,18 @@ var DISCOVERY_GUIDE = {
20237
20424
  var VALIDATION_GUIDE = {
20238
20425
  domain_id: "validation",
20239
20426
  anchor_entity: "hypothesis",
20240
- // creation_sequence covers every entity registered to the `validation`
20241
- // domain. `experiment` is the legacy parent that decomposed into
20242
- // experiment_plan + experiment_run in v0.2.6 (split rule lives in
20243
- // UPG_SPLIT_MIGRATIONS); it remains in the registry for round-trip
20244
- // compatibility and is listed last so new authors reach the split pair
20245
- // first.
20246
- creation_sequence: ["hypothesis", "experiment_plan", "experiment_run", "test_plan", "evidence", "learning", "research_plan", "experiment"],
20427
+ // (UPG-664) creation_sequence covers every entity registered to the
20428
+ // `validation` domain along the canonical chain hypothesis → experiment_plan
20429
+ // → experiment → experiment_run. `experiment` is the canonical structured
20430
+ // test; experiment_run is its optional replication child. `test_plan`
20431
+ // re-homed to the QA/testing domain (UPG-678).
20432
+ creation_sequence: ["hypothesis", "experiment_plan", "experiment", "experiment_run", "evidence", "learning", "research_plan"],
20247
20433
  patterns: [
20248
20434
  {
20249
20435
  name: "Hypothesis Testing Loop",
20250
- description: "Hypotheses require plans (intent), plans run as experiment_runs (execution), runs produce evidence and learnings, learnings update hypotheses",
20251
- entity_types: ["hypothesis", "experiment_plan", "experiment_run", "evidence", "learning"],
20252
- edge_chain: ["hypothesis_requires_experiment_plan", "experiment_plan_ran_as_experiment_run", "experiment_run_yields_evidence", "experiment_run_produces_learning", "learning_updates_hypothesis"]
20436
+ description: "A hypothesis requires a plan (the experiment design), the plan designs an experiment (the structured test), the experiment is executed as run(s) that produce evidence and learnings, and learnings update the hypothesis",
20437
+ entity_types: ["hypothesis", "experiment_plan", "experiment", "experiment_run", "evidence", "learning"],
20438
+ edge_chain: ["hypothesis_requires_experiment_plan", "experiment_plan_designs_experiment", "experiment_executed_as_experiment_run", "experiment_run_yields_evidence", "experiment_run_produces_learning", "learning_updates_hypothesis"]
20253
20439
  }
20254
20440
  ],
20255
20441
  required_bridges: [
@@ -20367,7 +20553,8 @@ var UX_DESIGN_GUIDE = {
20367
20553
  anti_patterns: [
20368
20554
  { description: "Screens without journeys: isolated screens miss the experience context" },
20369
20555
  { description: "Prototypes without testing: if nobody tests the prototype, it is just art" },
20370
- { description: "Design questions left open: exploration status should progress to resolved or parked" }
20556
+ { description: "Design questions left open: exploration status should progress to resolved or parked" },
20557
+ { description: "Touchpoints stuffed in the deprecated journey_step.touchpoint string (UPG-675). Touchpoints belong in one of two layers: journey_action is the in-product blueprint layer (the finest band of a journey_step), and the touchpoint entity is the cross-channel customer-success layer (touchpoint_occurs_in_journey_step). Pick the layer; do not duplicate the touch as a free-text string." }
20371
20558
  ]
20372
20559
  };
20373
20560
  var DESIGN_SYSTEM_GUIDE = {
@@ -20510,7 +20697,9 @@ var DEVOPS_GUIDE = {
20510
20697
  var TESTING_GUIDE = {
20511
20698
  domain_id: "testing",
20512
20699
  anchor_entity: "test_suite",
20513
- creation_sequence: ["test_suite", "test_case", "qa_session", "regression_test", "test_coverage_report", "test_environment", "test_result"],
20700
+ // (UPG-678) test_plan re-homed validation → QA: the verification-approach
20701
+ // plan that the suites execute. Listed first as the QA planning layer.
20702
+ creation_sequence: ["test_plan", "test_suite", "test_case", "qa_session", "regression_test", "test_coverage_report", "test_environment", "test_result"],
20514
20703
  patterns: [
20515
20704
  {
20516
20705
  name: "Test Pyramid",
@@ -20607,7 +20796,7 @@ var AI_GUIDE = {
20607
20796
  }
20608
20797
  ],
20609
20798
  required_bridges: [
20610
- { edge_type: "prompt_template_targets_ai_model", target_domain: "content", when: "Prompt templates should link to the models they target" },
20799
+ { edge_type: "ai_dataset_sourced_from_data_source", target_domain: "data_analytics", when: "AI datasets should trace provenance to the data sources they draw from" },
20611
20800
  { edge_type: "ai_guardrail_enforces_security_policy", target_domain: "security", when: "AI guardrails should map to the security policies that constrain them" }
20612
20801
  ],
20613
20802
  anti_patterns: [
@@ -20798,7 +20987,8 @@ var CUSTOMER_SUCCESS_GUIDE = {
20798
20987
  anti_patterns: [
20799
20988
  { description: "Health scores without components: define which metrics compose the score" },
20800
20989
  { description: "Playbooks without triggers: specify what conditions activate each playbook" },
20801
- { description: "Churn reasons without analysis: understand patterns, not just individual cases" }
20990
+ { description: "Churn reasons without analysis: understand patterns, not just individual cases" },
20991
+ { description: "Conflating the two journey models (UPG-675). customer_journey_stage models the post-sale AARRR lifecycle (a customer-success timeline; use stage_order to sequence stages); journey_phase models in-product experience phases of a single user_journey. They are different lenses, not duplicates. Likewise, the touchpoint entity is the cross-channel customer-success layer, distinct from the in-product journey_action." }
20802
20992
  ]
20803
20993
  };
20804
20994
  var CONTENT_GUIDE = {
@@ -24196,9 +24386,7 @@ var UPG_REGIONS = [
24196
24386
  "objective_achieved_through_key_result",
24197
24387
  "objective_measured_by_metric",
24198
24388
  "key_result_quantified_by_metric",
24199
- "key_result_tracked_by_metric",
24200
24389
  "outcome_measured_by_metric",
24201
- "outcome_tracked_by_metric",
24202
24390
  "metric_decomposes_into_metric",
24203
24391
  "metric_guards_metric",
24204
24392
  "metric_drives_metric"
@@ -24452,8 +24640,7 @@ var UPG_REGIONS = [
24452
24640
  },
24453
24641
  {
24454
24642
  type: "experiment",
24455
- role: "hub",
24456
- notes: "self-nesting"
24643
+ role: "hub"
24457
24644
  },
24458
24645
  {
24459
24646
  type: "experiment_plan",
@@ -24461,7 +24648,8 @@ var UPG_REGIONS = [
24461
24648
  },
24462
24649
  {
24463
24650
  type: "experiment_run",
24464
- role: "container"
24651
+ role: "container",
24652
+ notes: "self-nesting"
24465
24653
  },
24466
24654
  {
24467
24655
  type: "learning",
@@ -24471,10 +24659,6 @@ var UPG_REGIONS = [
24471
24659
  type: "evidence",
24472
24660
  role: "leaf"
24473
24661
  },
24474
- {
24475
- type: "test_plan",
24476
- role: "leaf"
24477
- },
24478
24662
  {
24479
24663
  type: "research_plan",
24480
24664
  role: "leaf"
@@ -24566,7 +24750,7 @@ var UPG_REGIONS = [
24566
24750
  },
24567
24751
  {
24568
24752
  direction: "export",
24569
- edge_id: "insight_informs_opportunity_cross_domain",
24753
+ edge_id: "insight_informs_opportunity",
24570
24754
  crosses_into: "discovery_research_validation"
24571
24755
  },
24572
24756
  {
@@ -25250,7 +25434,7 @@ var UPG_REGIONS = [
25250
25434
  },
25251
25435
  {
25252
25436
  type: "prompt_template",
25253
- role: "leaf"
25437
+ role: "container"
25254
25438
  },
25255
25439
  {
25256
25440
  type: "prompt_version",
@@ -25682,7 +25866,7 @@ var UPG_REGIONS = [
25682
25866
  "business_model_earns_via_revenue_stream",
25683
25867
  "business_model_costs_via_cost_structure",
25684
25868
  "business_model_measured_by_unit_economics",
25685
- "business_model_reaches_via_distribution_channel",
25869
+ "business_model_distributes_via_distribution_channel",
25686
25870
  "business_model_maintains_customer_relationship",
25687
25871
  "business_model_requires_key_resource",
25688
25872
  "business_model_performs_key_activity",
@@ -25851,11 +26035,6 @@ var UPG_REGIONS = [
25851
26035
  edge_id: "key_result_quantified_by_metric",
25852
26036
  crosses_into: "strategy_outcomes"
25853
26037
  },
25854
- {
25855
- direction: "import",
25856
- edge_id: "key_result_tracked_by_metric",
25857
- crosses_into: "strategy_outcomes"
25858
- },
25859
26038
  {
25860
26039
  direction: "import",
25861
26040
  edge_id: "revenue_stream_measured_by_metric_cross_domain",
@@ -25967,6 +26146,10 @@ var UPG_REGIONS = [
25967
26146
  type: "infrastructure_component",
25968
26147
  role: "container"
25969
26148
  },
26149
+ {
26150
+ type: "test_plan",
26151
+ role: "container"
26152
+ },
25970
26153
  {
25971
26154
  type: "test_suite",
25972
26155
  role: "container"
@@ -26248,7 +26431,7 @@ var UPG_REGIONS = [
26248
26431
  boundary_edges: [
26249
26432
  {
26250
26433
  direction: "import",
26251
- edge_id: "product_experiences_incident_hierarchy",
26434
+ edge_id: "product_experiences_incident",
26252
26435
  crosses_into: "product_delivery"
26253
26436
  },
26254
26437
  {
@@ -26798,7 +26981,7 @@ function isCanonical(text) {
26798
26981
  }
26799
26982
 
26800
26983
  // src/index.ts
26801
- var UPG_VERSION = "0.9.8";
26984
+ var UPG_VERSION = "0.9.9";
26802
26985
  var UPG_FORMAT_VERSION = "0.4.0";
26803
26986
  var MARKDOWN_FORMAT_VERSION = "0.1";
26804
26987
  var UPG_TYPES = getTypes();