@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.cjs CHANGED
@@ -238,8 +238,8 @@ var UPG_DOMAINS = [
238
238
  {
239
239
  id: "validation",
240
240
  label: "Validation",
241
- 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.",
242
- types: ["hypothesis", "experiment", "experiment_plan", "experiment_run", "learning", "test_plan", "evidence", "research_plan"]
241
+ 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.",
242
+ types: ["hypothesis", "experiment", "experiment_plan", "experiment_run", "learning", "evidence", "research_plan"]
243
243
  },
244
244
  {
245
245
  id: "market_intelligence",
@@ -484,8 +484,9 @@ var UPG_DOMAINS = [
484
484
  {
485
485
  id: "testing",
486
486
  label: "Quality Assurance",
487
- 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).",
487
+ 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).",
488
488
  types: [
489
+ "test_plan",
489
490
  "test_suite",
490
491
  "test_case",
491
492
  "qa_session",
@@ -524,7 +525,7 @@ var UPG_DOMAINS = [
524
525
  {
525
526
  id: "ai",
526
527
  label: "AI & Machine Learning",
527
- 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.",
528
+ 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.",
528
529
  types: [
529
530
  "ai_model",
530
531
  "prompt_version",
@@ -747,15 +748,23 @@ var UPG_ENTITY_META = [
747
748
  { name: "hypothesis", type_id: "ent_027", maturity: "stable", since: "0.1.0" },
748
749
  { name: "hypothesis_claim", type_id: "ent_344", maturity: "deprecated", since: "0.2.8", deprecated_in: "0.4.0", replacement: "hypothesis" },
749
750
  { name: "hypothesis_evidence", type_id: "ent_345", maturity: "deprecated", since: "0.2.8", deprecated_in: "0.4.0", replacement: "evidence" },
750
- // `experiment` is canonical. It has its own property schema
751
+ // `experiment` is canonical (UPG-664). It has its own property schema
751
752
  // (method, start_date, end_date, sample_size, expected_lift) and is the
752
- // canonical parent of `ab_test` in the hierarchy. Plan/Run split
753
- // (experiment_plan + experiment_run) remains as the fine-grained option;
754
- // `experiment` covers the general-purpose use case.
753
+ // canonical unit of a structured test. The validation flow is
754
+ // `hypothesis → experiment_plan → experiment → experiment_run`: the plan is
755
+ // the validation design, the experiment is the structured test, the run is
756
+ // the optional multi-run/replication child.
755
757
  { name: "experiment", type_id: "ent_028", maturity: "stable", since: "0.1.0" },
756
- { name: "experiment_plan", type_id: "ent_340", maturity: "proposed", since: "0.2.6" },
758
+ // experiment_plan graduated proposed → stable (UPG-664). It is the canonical
759
+ // validation PLAN type — it absorbed `test_plan`'s planning properties
760
+ // (method / success_criteria / sample_size) when `test_plan` re-homed to the
761
+ // QA/testing domain (UPG-678).
762
+ { name: "experiment_plan", type_id: "ent_340", maturity: "stable", since: "0.2.6" },
757
763
  { name: "experiment_run", type_id: "ent_341", maturity: "proposed", since: "0.2.6" },
758
764
  { name: "learning", type_id: "ent_029", maturity: "stable", since: "0.1.0" },
765
+ // test_plan re-homed validation → testing/QA (UPG-678). It is the QA
766
+ // verification-procedure plan (test scope / environments / pass criteria);
767
+ // its former validation-planning role is carried by experiment_plan.
759
768
  { name: "test_plan", type_id: "ent_030", maturity: "stable", since: "0.1.0" },
760
769
  { name: "evidence", type_id: "ent_031", maturity: "stable", since: "0.1.0" },
761
770
  { name: "research_plan", type_id: "ent_032", maturity: "stable", since: "0.1.0" },
@@ -889,7 +898,7 @@ var UPG_ENTITY_META = [
889
898
  { name: "behavioral_segment", type_id: "ent_111", maturity: "stable", since: "0.1.0" },
890
899
  { name: "segment", type_id: "ent_338", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.2.0", replacement: "behavioral_segment" },
891
900
  { name: "growth_loop", type_id: "ent_112", maturity: "stable", since: "0.1.0" },
892
- { name: "growth_experiment", type_id: "ent_113", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment_run" },
901
+ { name: "growth_experiment", type_id: "ent_113", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment" },
893
902
  { name: "variant", type_id: "ent_114", maturity: "stable", since: "0.1.0" },
894
903
  { name: "attribution_model", type_id: "ent_115", maturity: "stable", since: "0.1.0" },
895
904
  // ── Business Model ──
@@ -939,7 +948,7 @@ var UPG_ENTITY_META = [
939
948
  { name: "metric_definition", type_id: "ent_154", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "metric" },
940
949
  { name: "event_schema", type_id: "ent_155", maturity: "stable", since: "0.1.0" },
941
950
  { name: "dashboard", type_id: "ent_156", maturity: "stable", since: "0.1.0" },
942
- { name: "ab_test", type_id: "ent_157", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment_run" },
951
+ { name: "ab_test", type_id: "ent_157", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment" },
943
952
  { name: "data_model", type_id: "ent_158", maturity: "stable", since: "0.1.0" },
944
953
  { name: "data_quality_rule", type_id: "ent_159", maturity: "stable", since: "0.1.0" },
945
954
  { name: "data_product", type_id: "ent_160", maturity: "stable", since: "0.1.0" },
@@ -1021,7 +1030,7 @@ var UPG_ENTITY_META = [
1021
1030
  { name: "feedback_theme", type_id: "ent_225", maturity: "stable", since: "0.1.0" },
1022
1031
  // ── Pricing & Packaging ──
1023
1032
  { name: "pricing_strategy", type_id: "ent_226", maturity: "stable", since: "0.1.0" },
1024
- { name: "pricing_experiment", type_id: "ent_227", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment_run" },
1033
+ { name: "pricing_experiment", type_id: "ent_227", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.1.0", replacement: "experiment" },
1025
1034
  { name: "package", type_id: "ent_228", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.2.0", replacement: "pricing_tier" },
1026
1035
  { name: "discount_strategy", type_id: "ent_229", maturity: "stable", since: "0.1.0" },
1027
1036
  { name: "trial_config", type_id: "ent_230", maturity: "stable", since: "0.1.0" },
@@ -1192,8 +1201,22 @@ var UPG_EDGE_CATALOG = {
1192
1201
  // `evolved_from` in the reverse lets a feature trace its solution ancestry.
1193
1202
  solution_becomes_feature: { forward_verb: "becomes", reverse_verb: "evolved_from", classification: "causal", source_type: "solution", target_type: "feature" },
1194
1203
  hypothesis_requires_experiment_plan: { forward_verb: "requires", reverse_verb: "planned_for", classification: "causal", source_type: "hypothesis", target_type: "experiment_plan" },
1195
- hypothesis_planned_via_test_plan: { forward_verb: "planned_via", reverse_verb: "plans_for", classification: "hierarchy", source_type: "hypothesis", target_type: "test_plan" },
1204
+ // (UPG-664) V4: close the stable canonical loop directly on `experiment`.
1205
+ // Before this, resolve_edge_for_pair(hypothesis, experiment) and the reverse
1206
+ // were both null — the stable experiment could not attach to the hypothesis
1207
+ // it tests without adopting the proposed run type.
1208
+ hypothesis_tested_by_experiment: { forward_verb: "tested_by", reverse_verb: "tests", classification: "causal", source_type: "hypothesis", target_type: "experiment" },
1209
+ experiment_validates_hypothesis: { forward_verb: "validates", reverse_verb: "validated_by", classification: "causal", source_type: "experiment", target_type: "hypothesis" },
1196
1210
  hypothesis_investigated_via_research_plan: { forward_verb: "investigated_via", reverse_verb: "investigates", classification: "hierarchy", source_type: "hypothesis", target_type: "research_plan" },
1211
+ // (UPG-678) V8: a research insight can seed a hypothesis directly. The region
1212
+ // says validation consumes `insight` from user_research, but the only paths
1213
+ // in were via solution/learning/assumption.
1214
+ insight_generates_hypothesis: { forward_verb: "generates", reverse_verb: "generated_from", classification: "cross-domain", source_type: "insight", target_type: "hypothesis" },
1215
+ // (UPG-678) V7: the validation-side research plan is conducted as the
1216
+ // user_research-side research study. Both sit in the
1217
+ // discovery_research_validation region but had no connecting edge; the plan
1218
+ // jumped straight to `participant`, bypassing the study container.
1219
+ research_plan_conducted_as_research_study: { forward_verb: "conducted_as", reverse_verb: "conducts", classification: "cross-domain", source_type: "research_plan", target_type: "research_study" },
1197
1220
  experiment_run_produces_learning: { forward_verb: "produces", reverse_verb: "learned_from", classification: "causal", source_type: "experiment_run", target_type: "learning" },
1198
1221
  experiment_run_yields_evidence: { forward_verb: "yields", reverse_verb: "supports", classification: "causal", source_type: "experiment_run", target_type: "evidence" },
1199
1222
  // (v0.2.7 closure) the legacy duplicate pair
@@ -1227,7 +1250,11 @@ var UPG_EDGE_CATALOG = {
1227
1250
  // canonical edge, not a foreign-key property (the v0.2.6 ExperimentPlan
1228
1251
  // interface left this slot pending in line with the principle).
1229
1252
  experiment_plan_targets_metric: { forward_verb: "targets", reverse_verb: "targeted_by_plan", classification: "cross-domain", source_type: "experiment_plan", target_type: "metric" },
1230
- experiment_has_plan: { forward_verb: "has_plan", reverse_verb: "is_plan_for", classification: "hierarchy", source_type: "experiment", target_type: "experiment_plan" },
1253
+ // (UPG-664) The validation plan designs the experiment it produces. This
1254
+ // replaces the backwards `experiment_has_plan` (experiment → experiment_plan):
1255
+ // the chain now reads hypothesis → experiment_plan → experiment → experiment_run,
1256
+ // with experiment_plan the owning parent of experiment.
1257
+ experiment_plan_designs_experiment: { forward_verb: "designs", reverse_verb: "designed_by", classification: "hierarchy", source_type: "experiment_plan", target_type: "experiment" },
1231
1258
  experiment_executed_as_experiment_run: { forward_verb: "executed_as", reverse_verb: "execution_of", classification: "hierarchy", source_type: "experiment", target_type: "experiment_run" },
1232
1259
  experiment_produces_learning: { forward_verb: "produces", reverse_verb: "produced_by", classification: "causal", source_type: "experiment", target_type: "learning" },
1233
1260
  experiment_produces_evidence: { forward_verb: "produces", reverse_verb: "produced_by", classification: "causal", source_type: "experiment", target_type: "evidence" },
@@ -1352,11 +1379,15 @@ var UPG_EDGE_CATALOG = {
1352
1379
  product_holds_assumption: { forward_verb: "holds", reverse_verb: "held_by", classification: "semantic", source_type: "product", target_type: "assumption" },
1353
1380
  product_measures_with_metric: { forward_verb: "measures_with", reverse_verb: "measures", classification: "hierarchy", source_type: "product", target_type: "metric" },
1354
1381
  outcome_measured_by_metric: { forward_verb: "measured_by", reverse_verb: "measures", classification: "hierarchy", source_type: "outcome", target_type: "metric" },
1355
- outcome_tracked_by_metric: { forward_verb: "tracked_by", reverse_verb: "tracks", classification: "hierarchy", source_type: "outcome", target_type: "metric" },
1382
+ // (UPG-677) outcome_tracked_by_metric retired — near-synonym of
1383
+ // outcome_measured_by_metric (the kept canonical key). Captain's lean =
1384
+ // collapse. See UPG_EDGE_MIGRATIONS['0.9.9'].
1356
1385
  objective_achieved_through_key_result: { forward_verb: "achieved_through", reverse_verb: "achieves", classification: "hierarchy", source_type: "objective", target_type: "key_result" },
1357
1386
  objective_measured_by_metric: { forward_verb: "measured_by", reverse_verb: "measures", classification: "hierarchy", source_type: "objective", target_type: "metric" },
1358
1387
  key_result_quantified_by_metric: { forward_verb: "quantified_by", reverse_verb: "quantifies", classification: "hierarchy", source_type: "key_result", target_type: "metric" },
1359
- key_result_tracked_by_metric: { forward_verb: "tracked_by", reverse_verb: "tracks", classification: "hierarchy", source_type: "key_result", target_type: "metric" },
1388
+ // (UPG-677) key_result_tracked_by_metric retired — near-synonym of
1389
+ // key_result_quantified_by_metric (the kept canonical key). Captain's lean =
1390
+ // collapse. See UPG_EDGE_MIGRATIONS['0.9.9'].
1360
1391
  vision_realised_through_mission: { forward_verb: "realised_through", reverse_verb: "realises", classification: "hierarchy", source_type: "vision", target_type: "mission" },
1361
1392
  mission_supported_by_strategic_pillar: { forward_verb: "supported_by", reverse_verb: "supports", classification: "hierarchy", source_type: "mission", target_type: "strategic_pillar" },
1362
1393
  strategic_pillar_organises_strategic_theme: { forward_verb: "organises", reverse_verb: "organised_by", classification: "hierarchy", source_type: "strategic_pillar", target_type: "strategic_theme" },
@@ -1380,17 +1411,20 @@ var UPG_EDGE_CATALOG = {
1380
1411
  // make them measurable. Direct link means a dashboard can surface KRs next
1381
1412
  // to the theme without traversing objective.
1382
1413
  //
1383
- // `objective_rolls_up_to_strategic_theme`: the OKR containment edge.
1414
+ // `strategic_theme_contains_objective`: the OKR containment edge (UPG-676,
1415
+ // renamed from the malformed `objective_rolls_up_to_strategic_theme`).
1384
1416
  // an objective is the specific quarterly bet *within* a theme. strategic_theme
1385
1417
  // is the broader multi-quarter focus area; objective is subordinate.
1386
1418
  // Direction: strategic_theme → objective (parent → child, per UPG hierarchy
1387
- // convention where source is the parent). The reverse verb `rolls_up_to`
1419
+ // convention where source is the parent). The key now reads source-first
1420
+ // (strategic_theme), the forward verb is the clean `contains` (was the
1421
+ // malformed `contains_objective`), and the reverse verb `rolls_up_to`
1388
1422
  // surfaces the upward-rollup read. Mirrors `strategic_pillar_organises_
1389
1423
  // strategic_theme` (pillar → theme), completing the strategic cascade:
1390
1424
  // strategic_pillar → strategic_theme → objective → key_result.
1391
1425
  strategic_theme_delivers_outcome: { forward_verb: "delivers", reverse_verb: "delivered_by", classification: "causal", source_type: "strategic_theme", target_type: "outcome" },
1392
1426
  strategic_theme_measured_by_key_result: { forward_verb: "measured_by", reverse_verb: "measures", classification: "causal", source_type: "strategic_theme", target_type: "key_result" },
1393
- objective_rolls_up_to_strategic_theme: { forward_verb: "contains_objective", reverse_verb: "rolls_up_to", classification: "hierarchy", source_type: "strategic_theme", target_type: "objective" },
1427
+ strategic_theme_contains_objective: { forward_verb: "contains", reverse_verb: "rolls_up_to", classification: "hierarchy", source_type: "strategic_theme", target_type: "objective" },
1394
1428
  initiative_assumes_assumption: { forward_verb: "assumes", reverse_verb: "assumed_by", classification: "hierarchy", source_type: "initiative", target_type: "assumption" },
1395
1429
  initiative_drives_outcome: { forward_verb: "drives", reverse_verb: "driven_by", classification: "cross-domain", source_type: "initiative", target_type: "outcome" },
1396
1430
  capability_enables_value_stream: { forward_verb: "enables", reverse_verb: "enabled_by", classification: "cross-domain", source_type: "capability", target_type: "value_stream" },
@@ -1695,7 +1729,9 @@ var UPG_EDGE_CATALOG = {
1695
1729
  threat_model_surfaces_vulnerability: { forward_verb: "surfaces", reverse_verb: "surfaced_by", classification: "hierarchy", source_type: "threat_model", target_type: "vulnerability" },
1696
1730
  product_enforces_security_control: { forward_verb: "enforces", reverse_verb: "enforced_by", classification: "hierarchy", source_type: "product", target_type: "security_control" },
1697
1731
  product_governed_by_security_policy: { forward_verb: "governed_by", reverse_verb: "governs", classification: "hierarchy", source_type: "product", target_type: "security_policy" },
1698
- product_experiences_incident_hierarchy: { forward_verb: "experiences", reverse_verb: "affects", classification: "hierarchy", source_type: "product", target_type: "incident" },
1732
+ // (UPG-677) product_experiences_incident_hierarchy retired — byte-identical
1733
+ // shadow of product_experiences_incident (the clean key). See
1734
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1699
1735
  product_tested_by_penetration_test: { forward_verb: "tested_by", reverse_verb: "tests", classification: "hierarchy", source_type: "product", target_type: "penetration_test" },
1700
1736
  product_reviewed_by_security_review: { forward_verb: "reviewed_by", reverse_verb: "reviews", classification: "hierarchy", source_type: "product", target_type: "security_review" },
1701
1737
  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" },
@@ -1716,6 +1752,12 @@ var UPG_EDGE_CATALOG = {
1716
1752
  vulnerability_discovered_by_penetration_test: { forward_verb: "discovered_by", reverse_verb: "discovers", classification: "cross-domain", source_type: "vulnerability", target_type: "penetration_test" },
1717
1753
  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" },
1718
1754
  // 3.4 Quality Assurance & Testing Domain
1755
+ // (UPG-678) test_plan re-homed validation → QA. It is the QA planning layer:
1756
+ // the product owns its test plans; each plan is carried out by the suites it
1757
+ // groups and specifies the environments it exercises.
1758
+ 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" },
1759
+ test_plan_executed_by_test_suite: { forward_verb: "executed_by", reverse_verb: "executes", classification: "hierarchy", source_type: "test_plan", target_type: "test_suite" },
1760
+ test_plan_specifies_test_environment: { forward_verb: "specifies", reverse_verb: "specified_by", classification: "hierarchy", source_type: "test_plan", target_type: "test_environment" },
1719
1761
  product_maintains_test_suite: { forward_verb: "maintains", reverse_verb: "maintained_by", classification: "hierarchy", source_type: "product", target_type: "test_suite" },
1720
1762
  test_suite_contains_test_case: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "test_suite", target_type: "test_case" },
1721
1763
  product_undergoes_qa_session: { forward_verb: "undergoes", reverse_verb: "conducted_on", classification: "hierarchy", source_type: "product", target_type: "qa_session" },
@@ -1747,7 +1789,12 @@ var UPG_EDGE_CATALOG = {
1747
1789
  a11y_audit_covers_feature: { forward_verb: "covers", reverse_verb: "covered_by", classification: "cross-domain", source_type: "a11y_audit", target_type: "feature" },
1748
1790
  // 3.6 AI/ML Operations Domain
1749
1791
  product_powered_by_ai_model: { forward_verb: "powered_by", reverse_verb: "powers", classification: "hierarchy", source_type: "product", target_type: "ai_model" },
1750
- ai_model_prompted_via_prompt_version: { forward_verb: "prompted_via", reverse_verb: "prompts", classification: "hierarchy", source_type: "ai_model", target_type: "prompt_version" },
1792
+ // (UPG-665) The prompt abstraction is corrected to ai_model → prompt_template
1793
+ // → prompt_version. The model defines templates; each template contains its
1794
+ // versions. This replaces the inverted ai_model → prompt_version ownership
1795
+ // and the backwards prompt_version → prompt_template edge.
1796
+ ai_model_defines_prompt_template: { forward_verb: "defines", reverse_verb: "defined_by", classification: "hierarchy", source_type: "ai_model", target_type: "prompt_template" },
1797
+ prompt_template_contains_prompt_version: { forward_verb: "contains", reverse_verb: "version_of", classification: "hierarchy", source_type: "prompt_template", target_type: "prompt_version" },
1751
1798
  ai_model_benchmarked_by_eval_benchmark: { forward_verb: "benchmarked_by", reverse_verb: "benchmarks", classification: "hierarchy", source_type: "ai_model", target_type: "eval_benchmark" },
1752
1799
  eval_benchmark_executed_as_eval_run: { forward_verb: "executed_as", reverse_verb: "executes", classification: "hierarchy", source_type: "eval_benchmark", target_type: "eval_run" },
1753
1800
  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" },
@@ -1755,7 +1802,10 @@ var UPG_EDGE_CATALOG = {
1755
1802
  ai_model_constrained_by_ai_guardrail: { forward_verb: "constrained_by", reverse_verb: "constrains", classification: "hierarchy", source_type: "ai_model", target_type: "ai_guardrail" },
1756
1803
  product_compared_via_model_comparison: { forward_verb: "compared_via", reverse_verb: "compares", classification: "hierarchy", source_type: "product", target_type: "model_comparison" },
1757
1804
  ai_model_compared_in_model_comparison: { forward_verb: "compared_in", reverse_verb: "compares", classification: "hierarchy", source_type: "ai_model", target_type: "model_comparison" },
1758
- prompt_version_evolves_prompt_template: { forward_verb: "evolves", reverse_verb: "evolved_by", classification: "cross-domain", source_type: "prompt_version", target_type: "prompt_template" },
1805
+ // (UPG-665, AI-10) prompt_version self-ordering: track iteration order with a
1806
+ // real edge, not just a string property — the domain's own anti-pattern is
1807
+ // "version prompts like code".
1808
+ prompt_version_supersedes_prompt_version: { forward_verb: "supersedes", reverse_verb: "superseded_by", classification: "semantic", source_type: "prompt_version", target_type: "prompt_version" },
1759
1809
  eval_benchmark_measures_feature: { forward_verb: "measures", reverse_verb: "measured_by", classification: "cross-domain", source_type: "eval_benchmark", target_type: "feature" },
1760
1810
  ai_guardrail_enforces_security_policy: { forward_verb: "enforces", reverse_verb: "enforced_by", classification: "cross-domain", source_type: "ai_guardrail", target_type: "security_policy" },
1761
1811
  model_comparison_informs_decision: { forward_verb: "informs", reverse_verb: "informed_by", classification: "cross-domain", source_type: "model_comparison", target_type: "decision" },
@@ -1763,6 +1813,18 @@ var UPG_EDGE_CATALOG = {
1763
1813
  ai_model_evaluated_through_ai_experiment: { forward_verb: "evaluated_through", reverse_verb: "evaluates", classification: "hierarchy", source_type: "ai_model", target_type: "ai_experiment" },
1764
1814
  ai_model_trained_on_ai_dataset: { forward_verb: "trained_on", reverse_verb: "trains", classification: "hierarchy", source_type: "ai_model", target_type: "ai_dataset" },
1765
1815
  ai_model_produces_ai_trace: { forward_verb: "produces", reverse_verb: "produced_by", classification: "causal", source_type: "ai_model", target_type: "ai_trace" },
1816
+ // (UPG-665) Observability bridges — turn four sinks into wired evidence and
1817
+ // close the Model Evaluation Loop.
1818
+ // AI-3: an eval_run can reach the model and prompt version it judged.
1819
+ eval_run_evaluates_ai_model: { forward_verb: "evaluates", reverse_verb: "evaluated_by", classification: "cross-domain", source_type: "eval_run", target_type: "ai_model" },
1820
+ eval_run_scores_prompt_version: { forward_verb: "scores", reverse_verb: "scored_by", classification: "cross-domain", source_type: "eval_run", target_type: "prompt_version" },
1821
+ // AI-5: a hallucination_report reaches the trace that produced it and its root cause.
1822
+ 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" },
1823
+ hallucination_report_caused_by_root_cause: { forward_verb: "caused_by", reverse_verb: "causes", classification: "cross-domain", source_type: "hallucination_report", target_type: "root_cause" },
1824
+ // AI-6: an ai_trace reaches the prompt version it executed.
1825
+ ai_trace_executed_prompt_version: { forward_verb: "executed", reverse_verb: "executed_in", classification: "cross-domain", source_type: "ai_trace", target_type: "prompt_version" },
1826
+ // AI-4/AI-11: an ai_dataset carries provenance to its data source.
1827
+ ai_dataset_sourced_from_data_source: { forward_verb: "sourced_from", reverse_verb: "sources", classification: "cross-domain", source_type: "ai_dataset", target_type: "data_source" },
1766
1828
  // 3.7 Agentic Workflows & Process Domain
1767
1829
  product_automated_via_workflow_template: { forward_verb: "automated_via", reverse_verb: "automates", classification: "hierarchy", source_type: "product", target_type: "workflow_template" },
1768
1830
  workflow_template_executed_as_workflow_run: { forward_verb: "executed_as", reverse_verb: "executes", classification: "hierarchy", source_type: "workflow_template", target_type: "workflow_run" },
@@ -1800,7 +1862,9 @@ var UPG_EDGE_CATALOG = {
1800
1862
  // variant is owned by growth_campaign in the hierarchy; experiments
1801
1863
  // test them via a semantic relationship.
1802
1864
  experiment_run_tests_variant: { forward_verb: "tests", reverse_verb: "tested_in", classification: "semantic", source_type: "experiment_run", target_type: "variant" },
1803
- metric_decomposed_into_metric: { forward_verb: "decomposed_into", reverse_verb: "rolls_up_to", classification: "hierarchy", source_type: "metric", target_type: "metric" },
1865
+ // (UPG-677) metric_decomposed_into_metric retired — tense-twin of
1866
+ // metric_decomposes_into_metric (the active-voice canonical key). See
1867
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1804
1868
  metric_drives_metric: { forward_verb: "drives", reverse_verb: "driven_by", classification: "causal", source_type: "metric", target_type: "metric" },
1805
1869
  funnel_step_reveals_need: { forward_verb: "reveals", reverse_verb: "visible_in", classification: "cross-domain", source_type: "funnel_step", target_type: "need" },
1806
1870
  funnel_step_tracks_event_schema: { forward_verb: "tracks", reverse_verb: "fires_in", classification: "cross-domain", source_type: "funnel_step", target_type: "event_schema" },
@@ -1829,7 +1893,11 @@ var UPG_EDGE_CATALOG = {
1829
1893
  business_model_requires_key_resource: { forward_verb: "requires", reverse_verb: "required_by", classification: "hierarchy", source_type: "business_model", target_type: "key_resource" },
1830
1894
  business_model_performs_key_activity: { forward_verb: "performs", reverse_verb: "performed_by", classification: "hierarchy", source_type: "business_model", target_type: "key_activity" },
1831
1895
  business_model_targets_market_segment: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "business_model", target_type: "market_segment" },
1832
- business_model_reaches_via_distribution_channel: { forward_verb: "reaches_via", reverse_verb: "reaches_for", classification: "hierarchy", source_type: "business_model", target_type: "distribution_channel" },
1896
+ // (UPG-677) business_model_reaches_via_distribution_channel retired —
1897
+ // near-synonym of business_model_distributes_via_distribution_channel (the
1898
+ // kept canonical key; "distributes_via" is the Business Model Canvas Channels
1899
+ // verb). FLAG: Captain has not separately confirmed this collapse. See
1900
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
1833
1901
  business_model_maintains_customer_relationship: { forward_verb: "maintains", reverse_verb: "maintained_by", classification: "hierarchy", source_type: "business_model", target_type: "customer_relationship" },
1834
1902
  business_model_distributes_via_distribution_channel: { forward_verb: "distributes_via", reverse_verb: "distributes_for", classification: "hierarchy", source_type: "business_model", target_type: "distribution_channel" },
1835
1903
  revenue_stream_tiered_as_pricing_tier: { forward_verb: "tiered_as", reverse_verb: "tiers", classification: "hierarchy", source_type: "revenue_stream", target_type: "pricing_tier" },
@@ -1851,7 +1919,9 @@ var UPG_EDGE_CATALOG = {
1851
1919
  // as a `need` with `valence='pain'`; link each pain the proposition
1852
1920
  // relieves structurally.
1853
1921
  value_proposition_solves_need: { forward_verb: "solves", reverse_verb: "solved_by", classification: "cross-domain", source_type: "value_proposition", target_type: "need" },
1854
- value_proposition_targets_persona_cross_domain: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "value_proposition", target_type: "persona" },
1922
+ // (UPG-677) value_proposition_targets_persona_cross_domain retired —
1923
+ // byte-identical shadow of value_proposition_targets_persona (the clean key).
1924
+ // See UPG_EDGE_MIGRATIONS['0.9.9'].
1855
1925
  revenue_stream_drives_metric: { forward_verb: "drives", reverse_verb: "driven_by", classification: "cross-domain", source_type: "revenue_stream", target_type: "metric" },
1856
1926
  // Ring 5 structural edges
1857
1927
  // (UPG-668, P-I) These three edges name `funnel_step` but were typed against
@@ -1995,7 +2065,9 @@ var UPG_EDGE_CATALOG = {
1995
2065
  customer_feedback_reveals_churn_reason: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "hierarchy", source_type: "customer_feedback", target_type: "churn_reason" },
1996
2066
  user_flow_contains_customer_journey_stage: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "user_flow", target_type: "customer_journey_stage" },
1997
2067
  customer_journey_stage_contains_touchpoint: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "customer_journey_stage", target_type: "touchpoint" },
1998
- support_ticket_reveals_need: { forward_verb: "reveals", reverse_verb: "revealed_by_ticket", classification: "cross-domain", source_type: "support_ticket", target_type: "need" },
2068
+ // (UPG-677) reverse_verb normalised revealed_by_ticket → revealed_by when the
2069
+ // support_ticket_reveals_need_cross_domain twin was collapsed into this clean key.
2070
+ support_ticket_reveals_need: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "support_ticket", target_type: "need" },
1999
2071
  customer_feedback_creates_observation: { forward_verb: "creates", reverse_verb: "created_from_feedback", classification: "cross-domain", source_type: "customer_feedback", target_type: "observation" },
2000
2072
  churn_reason_generates_hypothesis: { forward_verb: "generates", reverse_verb: "generated_from_churn", classification: "cross-domain", source_type: "churn_reason", target_type: "hypothesis" },
2001
2073
  user_flow_maps_user_journey: { forward_verb: "maps", reverse_verb: "mapped_by", classification: "cross-domain", source_type: "user_flow", target_type: "user_journey" },
@@ -2007,14 +2079,18 @@ var UPG_EDGE_CATALOG = {
2007
2079
  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" },
2008
2080
  customer_feedback_becomes_feature_request: { forward_verb: "becomes", reverse_verb: "originated_from", classification: "cross-domain", source_type: "customer_feedback", target_type: "feature_request" },
2009
2081
  support_ticket_reports_bug: { forward_verb: "reports", reverse_verb: "reported_by", classification: "cross-domain", source_type: "support_ticket", target_type: "bug" },
2010
- support_ticket_reveals_need_cross_domain: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "support_ticket", target_type: "need" },
2082
+ // (UPG-677) support_ticket_reveals_need_cross_domain retired — near-duplicate
2083
+ // of support_ticket_reveals_need (the clean key, whose reverse_verb is now
2084
+ // revealed_by). See UPG_EDGE_MIGRATIONS['0.9.9'].
2011
2085
  churn_reason_reveals_need: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "churn_reason", target_type: "need" },
2012
2086
  // 5.2 Content & Knowledge Domain
2013
2087
  product_publishes_content_piece: { forward_verb: "publishes", reverse_verb: "published_by", classification: "hierarchy", source_type: "product", target_type: "content_piece" },
2014
2088
  product_documents_in_knowledge_base_article: { forward_verb: "documents_in", reverse_verb: "documented_by", classification: "hierarchy", source_type: "product", target_type: "knowledge_base_article" },
2015
2089
  product_expressed_via_brand_asset: { forward_verb: "expressed_via", reverse_verb: "expresses", classification: "hierarchy", source_type: "product", target_type: "brand_asset" },
2016
2090
  product_documented_in_document: { forward_verb: "documented_in", reverse_verb: "documents", classification: "hierarchy", source_type: "product", target_type: "document" },
2017
- product_prompted_via_prompt_template: { forward_verb: "prompted_via", reverse_verb: "prompts", classification: "hierarchy", source_type: "product", target_type: "prompt_template" },
2091
+ // (UPG-665) product_prompted_via_prompt_template retired — prompt_template
2092
+ // re-parented product → ai_model (ai_model_defines_prompt_template). See
2093
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
2018
2094
  product_templated_via_documentation_template: { forward_verb: "templated_via", reverse_verb: "templates", classification: "hierarchy", source_type: "product", target_type: "documentation_template" },
2019
2095
  product_records_in_document: { forward_verb: "records_in", reverse_verb: "records", classification: "hierarchy", source_type: "product", target_type: "document" },
2020
2096
  content_strategy_scheduled_in_content_calendar: { forward_verb: "scheduled_in", reverse_verb: "schedules", classification: "hierarchy", source_type: "content_strategy", target_type: "content_calendar" },
@@ -2024,12 +2100,19 @@ var UPG_EDGE_CATALOG = {
2024
2100
  content_calendar_schedules_knowledge_base_article: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "knowledge_base_article" },
2025
2101
  content_calendar_schedules_brand_asset: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "brand_asset" },
2026
2102
  content_calendar_schedules_document: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "document" },
2027
- content_calendar_schedules_prompt_template: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "prompt_template" },
2103
+ // (UPG-665) content_calendar_schedules_prompt_template retired — prompt_template
2104
+ // re-homed to the ai_model containment tree (it is an AI artefact, not a
2105
+ // content-calendar-scheduled item). See UPG_EDGE_MIGRATIONS['0.9.9'].
2028
2106
  content_calendar_schedules_documentation_template: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "hierarchy", source_type: "content_calendar", target_type: "documentation_template" },
2029
2107
  content_piece_supports_messaging: { forward_verb: "supports", reverse_verb: "supported_by", classification: "cross-domain", source_type: "content_piece", target_type: "messaging" },
2030
2108
  content_piece_part_of_growth_campaign: { forward_verb: "part_of", reverse_verb: "includes", classification: "cross-domain", source_type: "content_piece", target_type: "growth_campaign" },
2031
2109
  knowledge_base_article_documents_feature: { forward_verb: "documents", reverse_verb: "documented_by", classification: "cross-domain", source_type: "knowledge_base_article", target_type: "feature" },
2032
- changelog_documents_release: { forward_verb: "documents", reverse_verb: "documented_in", classification: "cross-domain", source_type: "changelog", target_type: "release" },
2110
+ // (UPG-677) changelog_documents_release retired — inverse of the containment
2111
+ // edge release_documented_in_changelog (release → changelog, matching the
2112
+ // hierarchy release: ['changelog']). The "changelog documents release" read is
2113
+ // preserved by that edge's reverse_verb (documents). FLAG: this is the only
2114
+ // inverse pair collapsed; feature↔bug is kept (distinct classification +
2115
+ // semantic). See UPG_EDGE_MIGRATIONS['0.9.9'].
2033
2116
  prompt_template_powers_feature: { forward_verb: "powers", reverse_verb: "powered_by", classification: "cross-domain", source_type: "prompt_template", target_type: "feature" },
2034
2117
  content_theme_targets_persona: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "content_theme", target_type: "persona" },
2035
2118
  // v0.7.2 (UPG-571 §1): topic-cluster / content-pillar model; themes organize pieces. Connects the isolated `content_theme` AND `content_piece` members.
@@ -2065,7 +2148,9 @@ var UPG_EDGE_CATALOG = {
2065
2148
  // 6.1 Team & Organisation Domain
2066
2149
  product_staffed_by_team: { forward_verb: "staffed_by", reverse_verb: "staffs", classification: "hierarchy", source_type: "product", target_type: "team" },
2067
2150
  product_influenced_by_stakeholder: { forward_verb: "influenced_by", reverse_verb: "influences", classification: "hierarchy", source_type: "product", target_type: "stakeholder" },
2068
- product_decided_via_decision_hierarchy: { forward_verb: "decided_via", reverse_verb: "decides", classification: "hierarchy", source_type: "product", target_type: "decision" },
2151
+ // (UPG-677) product_decided_via_decision_hierarchy retired — duplicate of
2152
+ // product_decided_via_decision (the clean key). See
2153
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
2069
2154
  product_organised_into_department: { forward_verb: "organised_into", reverse_verb: "organises", classification: "hierarchy", source_type: "product", target_type: "department" },
2070
2155
  department_contains_team: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "department", target_type: "team" },
2071
2156
  department_includes_stakeholder: { forward_verb: "includes", reverse_verb: "included_in", classification: "hierarchy", source_type: "department", target_type: "stakeholder" },
@@ -2169,7 +2254,9 @@ var UPG_EDGE_CATALOG = {
2169
2254
  dashboard_contains_report: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "dashboard", target_type: "report" },
2170
2255
  dashboard_contains_experiment_run: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "dashboard", target_type: "experiment_run" },
2171
2256
  metric_measures_metric: { forward_verb: "measures", reverse_verb: "measured_by", classification: "cross-domain", source_type: "metric", target_type: "metric" },
2172
- metric_measures_metric_cross_domain: { forward_verb: "measures", reverse_verb: "measured_by", classification: "cross-domain", source_type: "metric", target_type: "metric" },
2257
+ // (UPG-677) metric_measures_metric_cross_domain retired — byte-identical
2258
+ // shadow of metric_measures_metric (the clean key). See
2259
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
2173
2260
  // `experiment_tests_hypothesis` dropped, superseded by
2174
2261
  // the canonical `experiment_run_validates_hypothesis` (causal) introduced
2175
2262
  // in v0.2.6. Authors expressing "this experiment tests this hypothesis"
@@ -2191,7 +2278,11 @@ var UPG_EDGE_CATALOG = {
2191
2278
  product_area_contains_product_area: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "product_area", target_type: "product_area" },
2192
2279
  // decision B: product_area groups features (the natural "Studio area owns 6 features" mental model)
2193
2280
  product_area_contains_feature: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "product_area", target_type: "feature" },
2194
- product_categorised_in_product_area: { forward_verb: "categorised_in", reverse_verb: "categorises", classification: "hierarchy", source_type: "product", target_type: "product_area" },
2281
+ // (UPG-677) product_categorised_in_product_area retired — inverse of
2282
+ // product_area_contains_product (the kept containment direction:
2283
+ // product_area → product, matching the UPG parent→child convention). FLAG:
2284
+ // the inverse "product categorised in area" read is preserved by the kept
2285
+ // edge's reverse_verb (belongs_to). See UPG_EDGE_MIGRATIONS['0.9.9'].
2195
2286
  // 8.2 Cross-Product Edges
2196
2287
  // direct product-level shorthand edges for market-intelligence shapes.
2197
2288
  // These are shortcuts: canonical deep paths go via competitive_analysis and gtm_strategy.
@@ -2255,7 +2346,9 @@ var UPG_EDGE_CATALOG = {
2255
2346
  insight_reveals_desired_outcome: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "insight", target_type: "desired_outcome" },
2256
2347
  insight_informs_job: { forward_verb: "informs", reverse_verb: "informed_by", classification: "cross-domain", source_type: "insight", target_type: "job" },
2257
2348
  insight_characterises_persona: { forward_verb: "characterises", reverse_verb: "characterised_by", classification: "cross-domain", source_type: "insight", target_type: "persona" },
2258
- insight_validates_need_cross_domain: { forward_verb: "validates", reverse_verb: "validated_by", classification: "cross-domain", source_type: "insight", target_type: "need" },
2349
+ // (UPG-677) insight_validates_need_cross_domain retired — byte-identical
2350
+ // shadow of insight_validates_need (the clean key). See
2351
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
2259
2352
  observation_reveals_need: { forward_verb: "reveals", reverse_verb: "revealed_by", classification: "cross-domain", source_type: "observation", target_type: "need" },
2260
2353
  observation_characterises_persona: { forward_verb: "characterises", reverse_verb: "characterised_by", classification: "cross-domain", source_type: "observation", target_type: "persona" },
2261
2354
  quote_evidences_need: { forward_verb: "evidences", reverse_verb: "evidenced_by", classification: "cross-domain", source_type: "quote", target_type: "need" },
@@ -2269,7 +2362,9 @@ var UPG_EDGE_CATALOG = {
2269
2362
  insight_inspires_design_concept: { forward_verb: "inspires", reverse_verb: "inspired_by", classification: "cross-domain", source_type: "insight", target_type: "design_concept" },
2270
2363
  observation_yields_insight: { forward_verb: "yields", reverse_verb: "yielded_by", classification: "cross-domain", source_type: "observation", target_type: "insight" },
2271
2364
  insight_refines_into_insight: { forward_verb: "refines_into", reverse_verb: "refined_from", classification: "hierarchy", source_type: "insight", target_type: "insight" },
2272
- insight_informs_opportunity_cross_domain: { forward_verb: "informs", reverse_verb: "informed_by", classification: "cross-domain", source_type: "insight", target_type: "opportunity" },
2365
+ // (UPG-677) insight_informs_opportunity_cross_domain retired — byte-identical
2366
+ // shadow of insight_informs_opportunity (the clean key). See
2367
+ // UPG_EDGE_MIGRATIONS['0.9.9'].
2273
2368
  insight_validates_persona: { forward_verb: "validates", reverse_verb: "validated_by", classification: "cross-domain", source_type: "insight", target_type: "persona" },
2274
2369
  // Validation-to-Discovery Feedback Loops
2275
2370
  learning_validates_opportunity: { forward_verb: "validates", reverse_verb: "validated_by", classification: "cross-domain", source_type: "learning", target_type: "opportunity" },
@@ -2327,7 +2422,9 @@ var UPG_EDGE_CATALOG = {
2327
2422
  customer_health_score_composed_of_metric: { forward_verb: "composed_of", reverse_verb: "composes", classification: "cross-domain", source_type: "customer_health_score", target_type: "metric" },
2328
2423
  service_level_agreement_measures_metric: { forward_verb: "measures", reverse_verb: "measured_by", classification: "cross-domain", source_type: "service_level_agreement", target_type: "metric" },
2329
2424
  // Content
2330
- prompt_template_targets_ai_model: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "prompt_template", target_type: "ai_model" },
2425
+ // (UPG-665) prompt_template_targets_ai_model retired — superseded by the
2426
+ // containment edge ai_model_defines_prompt_template (the model owns its
2427
+ // templates). See UPG_EDGE_MIGRATIONS['0.9.9'].
2331
2428
  // Education
2332
2429
  education_program_targets_persona: { forward_verb: "targets", reverse_verb: "targeted_by", classification: "cross-domain", source_type: "education_program", target_type: "persona" },
2333
2430
  // Programs
@@ -2491,13 +2588,13 @@ var UPG_EDGE_CATALOG = {
2491
2588
  messaging_used_in_launch: { forward_verb: "used_in", reverse_verb: "uses", classification: "cross-domain", source_type: "messaging", target_type: "launch" },
2492
2589
  messaging_enables_sales_motion: { forward_verb: "enables", reverse_verb: "enabled_by", classification: "cross-domain", source_type: "messaging", target_type: "sales_motion" },
2493
2590
  // Test Card + Learning Card (Strategyzer): canonical validation flow
2494
- // hypothesis → test_plan → experiment_run → evidence → learning → decision.
2495
- // The catalog already had the hypothesis → test_plan and experiment_run →
2496
- // {hypothesis, evidence, learning, decision} edges. Missing: the
2497
- // test_plan ran-as bridge (mirrors `experiment_plan_ran_as_experiment_run`
2498
- // for the test_plan/test-card pair), the evidence → learning interpretation
2499
- // step, and the learning → decision commitment step.
2500
- test_plan_ran_as_experiment_run: { forward_verb: "ran_as", reverse_verb: "ran_for", classification: "hierarchy", source_type: "test_plan", target_type: "experiment_run" },
2591
+ // hypothesis → experiment_plan → experiment → experiment_run → evidence →
2592
+ // learning → decision (UPG-664). The "Test Design" card is the
2593
+ // `experiment_plan`; the plan designs the experiment
2594
+ // (`experiment_plan_designs_experiment`), which is executed as run(s). The
2595
+ // former `test_plan_ran_as_experiment_run` bridge was retired when `test_plan`
2596
+ // re-homed to QA (UPG-678) — see UPG_EDGE_MIGRATIONS['0.9.9']. This block keeps
2597
+ // the evidence → learning interpretation and learning → decision commitment.
2501
2598
  evidence_interpreted_as_learning: { forward_verb: "interpreted_as", reverse_verb: "interpreted_from", classification: "causal", source_type: "evidence", target_type: "learning" },
2502
2599
  learning_informs_decision: { forward_verb: "informs", reverse_verb: "informed_by", classification: "causal", source_type: "learning", target_type: "decision" },
2503
2600
  // Opportunity Canvas: Patton's "Assumptions" slot is meant to capture the
@@ -3223,7 +3320,6 @@ var UPG_VALID_CHILDREN = {
3223
3320
  "knowledge_base_article",
3224
3321
  "brand_asset",
3225
3322
  "document",
3226
- "prompt_template",
3227
3323
  "documentation_template",
3228
3324
  // Legal
3229
3325
  "legal_entity",
@@ -3256,6 +3352,7 @@ var UPG_VALID_CHILDREN = {
3256
3352
  "a11y_audit",
3257
3353
  "a11y_annotation",
3258
3354
  // Quality Assurance
3355
+ "test_plan",
3259
3356
  "test_suite",
3260
3357
  "qa_session",
3261
3358
  "test_coverage_report",
@@ -3305,7 +3402,10 @@ var UPG_VALID_CHILDREN = {
3305
3402
  "api_ecosystem",
3306
3403
  "developer_portal",
3307
3404
  // Portfolio
3308
- "product_area",
3405
+ // (UPG-677) product no longer lists product_area as a child: the
3406
+ // product↔product_area containment is the single direction
3407
+ // product_area_contains_product (product_area → product). The inverse
3408
+ // product_categorised_in_product_area edge was collapsed.
3309
3409
  // Workspace
3310
3410
  "workspace"
3311
3411
  ],
@@ -3320,22 +3420,27 @@ var UPG_VALID_CHILDREN = {
3320
3420
  // belief), not the legacy hypothesis type.
3321
3421
  solution: ["hypothesis", "prototype", "metric"],
3322
3422
  // ── Validation hierarchy ────────────────────────────────────────────────────
3323
- // hypothesis is re-promoted as canonical. hypothesis_evidence
3324
- // collapsed into evidence; relationship expressed via hypothesis_has_evidence
3325
- // edge. evidence.direction carries supports/refutes/neutral semantics.
3326
- hypothesis: ["experiment_plan", "test_plan", "research_plan", "evidence"],
3327
- // experiment is canonical. It can contain experiment_plan/run
3328
- // sub-artefacts (the plan/run split is opt-in for finer granularity).
3329
- experiment: ["experiment_plan", "experiment_run", "learning", "evidence"],
3330
- experiment_plan: ["experiment_run"],
3331
- // v0.5.5 (UPG-528 Part 2a): mirrors `experiment_plan_ran_as_experiment_run`
3332
- // for the Strategyzer Test Card + Learning Card flow. A test plan (the
3333
- // upfront experiment design captured on the Test Card) runs as one or more
3334
- // experiment runs that produce evidence and learning.
3335
- test_plan: ["experiment_run"],
3423
+ // (UPG-664) The validation chain is a single-parent line:
3424
+ // hypothesis ▷ experiment_plan ▷ experiment ▷ experiment_run.
3425
+ // hypothesis is canonical (hypothesis_evidence collapsed into evidence;
3426
+ // relationship expressed via hypothesis_has_evidence edge; evidence.direction
3427
+ // carries supports/refutes/neutral semantics). `test_plan` re-homed to the
3428
+ // QA/testing domain (UPG-678), so it is no longer a hypothesis child.
3429
+ hypothesis: ["experiment_plan", "research_plan", "evidence"],
3430
+ // experiment_plan is the canonical validation PLAN (graduated stable,
3431
+ // UPG-664). It designs the experiment it produces; the plan owns the
3432
+ // experiment in the containment line. It also retains experiment_run as a
3433
+ // child for the v0.2.6 split-migration path
3434
+ // (`experiment_plan_ran_as_experiment_run`), where a legacy experiment routes
3435
+ // directly to a plan + run pair.
3436
+ experiment_plan: ["experiment", "experiment_run"],
3437
+ // experiment is the canonical structured test. It owns its run(s) and the
3438
+ // learning/evidence it produces. The optional experiment_run child captures
3439
+ // the multi-run / replication case.
3440
+ experiment: ["experiment_run", "learning", "evidence"],
3336
3441
  // experiment_run carries learning/evidence/metric children (run produces
3337
- // evidence) and self-nests for replications/multi-armed runs that share
3338
- // a parent plan.
3442
+ // evidence) and self-nests for replications/multi-armed runs (V9: this is
3443
+ // where self-nesting genuinely lives, not on `experiment`).
3339
3444
  experiment_run: ["learning", "evidence", "metric", "experiment_run"],
3340
3445
  // ── OKR hierarchy ──────────────────────────────────────────────────────────
3341
3446
  objective: ["key_result", "metric"],
@@ -3346,8 +3451,9 @@ var UPG_VALID_CHILDREN = {
3346
3451
  strategic_pillar: ["strategic_theme", "capability", "value_stream", "decision"],
3347
3452
  // v0.5.4 (UPG-511): objectives are the specific quarterly bets *within* a
3348
3453
  // strategic theme. The theme is the multi-quarter focus area; objectives are
3349
- // subordinate. Pairs with `objective_rolls_up_to_strategic_theme` in the edge
3350
- // catalog (hierarchy, strategic_theme → objective, reverse_verb: rolls_up_to).
3454
+ // subordinate. Pairs with `strategic_theme_contains_objective` in the edge
3455
+ // catalog (hierarchy, strategic_theme → objective, reverse_verb: rolls_up_to;
3456
+ // renamed from objective_rolls_up_to_strategic_theme in UPG-676).
3351
3457
  strategic_theme: ["initiative", "objective"],
3352
3458
  initiative: ["assumption"],
3353
3459
  // v0.5.2 (UPG-528): capability decomposes into sub-capabilities (Wardley
@@ -3527,7 +3633,6 @@ var UPG_VALID_CHILDREN = {
3527
3633
  "knowledge_base_article",
3528
3634
  "brand_asset",
3529
3635
  "document",
3530
- "prompt_template",
3531
3636
  "documentation_template"
3532
3637
  ],
3533
3638
  // ── Operations & CS hierarchy ────────────────────────────────────────────────
@@ -3632,6 +3737,10 @@ var UPG_VALID_CHILDREN = {
3632
3737
  // a learning_path can include its terminal certification.
3633
3738
  learning_path: ["tutorial", "certification"],
3634
3739
  // ── Quality Assurance & Testing hierarchy ────────────────────────────────────
3740
+ // (UPG-678) test_plan re-homed validation → QA. It is the QA planning layer:
3741
+ // the verification approach (scope, environments, pass criteria) that the
3742
+ // test suites execute. A test_plan groups the suites that carry it out.
3743
+ test_plan: ["test_suite", "test_environment"],
3635
3744
  test_suite: [
3636
3745
  "test_case",
3637
3746
  "regression_test",
@@ -3675,8 +3784,11 @@ var UPG_VALID_CHILDREN = {
3675
3784
  "paywall"
3676
3785
  ],
3677
3786
  // ── AI/ML Operations hierarchy ───────────────────────────────────────────────
3787
+ // (UPG-665) The prompt abstraction is corrected: ai_model → prompt_template →
3788
+ // prompt_version (a model has templates; each template has versions). The
3789
+ // model no longer owns prompt_version directly.
3678
3790
  ai_model: [
3679
- "prompt_version",
3791
+ "prompt_template",
3680
3792
  "eval_benchmark",
3681
3793
  "ai_cost_tracker",
3682
3794
  "hallucination_report",
@@ -3686,6 +3798,8 @@ var UPG_VALID_CHILDREN = {
3686
3798
  "ai_dataset",
3687
3799
  "ai_trace"
3688
3800
  ],
3801
+ // prompt_template owns its prompt_versions the way a file owns its commits.
3802
+ prompt_template: ["prompt_version"],
3689
3803
  // v0.5.7 (UPG-528 Part 2c): benchmarks define the metric set they measure
3690
3804
  // (HELM, MLPerf, BIG-bench all spec a metric list). metric already has many
3691
3805
  // hierarchy parents (outcome, objective, key_result, solution, data_source).
@@ -7944,6 +8058,69 @@ function getSplitMigrations(fromVersion, toVersion) {
7944
8058
  return result;
7945
8059
  }
7946
8060
  var UPG_EDGE_MIGRATIONS = {
8061
+ "0.9.9": [
8062
+ // (since v0.9.9, UPG-664/UPG-678) Validation experiment-model + test_plan
8063
+ // re-home. The validation chain is reshaped to the single-parent line
8064
+ // hypothesis → experiment_plan → experiment → experiment_run, `experiment`
8065
+ // gains its stable hypothesis loop, and `test_plan` re-homes to the QA
8066
+ // domain (its planning role absorbed by `experiment_plan`).
8067
+ //
8068
+ // The plan→experiment containment inverts: the old `experiment_has_plan`
8069
+ // (experiment → experiment_plan, hierarchy) becomes
8070
+ // `experiment_plan_designs_experiment` (experiment_plan → experiment). Flip
8071
+ // swaps endpoints. Endpoint guards reference the post-flip orientation: the
8072
+ // original source (experiment) becomes the new target, the original target
8073
+ // (experiment_plan) becomes the new source.
8074
+ { 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." },
8075
+ // test_plan re-homed validation → QA (UPG-678). Its two validation-side
8076
+ // edges are dropped: a hypothesis no longer plans via a QA test_plan (it
8077
+ // requires an experiment_plan), and the test_plan → experiment_run ran-as
8078
+ // bridge is superseded by the experiment_plan → experiment flow.
8079
+ { 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)." },
8080
+ { 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." },
8081
+ // (since v0.9.9, UPG-665) AI prompt-model correction. The prompt abstraction
8082
+ // is inverted to the correct ai_model → prompt_template → prompt_version
8083
+ // containment. The backwards/obsolete edges are dropped, and the
8084
+ // prompt_template → ai_model "targets" relationship is flipped into the new
8085
+ // ai_model → prompt_template ownership.
8086
+ { 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.' },
8087
+ { 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)." },
8088
+ { 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." },
8089
+ { 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." },
8090
+ { 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." },
8091
+ // (since v0.9.9, UPG-676) Strategic-theme containment edge rename. The
8092
+ // malformed `objective_rolls_up_to_strategic_theme` (key read object-first
8093
+ // while source_type was strategic_theme; forward_verb was the malformed
8094
+ // `contains_objective`) is renamed to the clean source-first
8095
+ // `strategic_theme_contains_objective`. Endpoints unchanged
8096
+ // (strategic_theme → objective); only the key and forward verb change.
8097
+ { 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." },
8098
+ // (since v0.9.9, UPG-677) Duplicate / shadow edge collapse. Each retired key
8099
+ // is dual-read to the surviving canonical key. Tier 1 = byte-identical
8100
+ // shadows; Tier 2 = tense/suffix twins; Tier 3 = near-synonyms collapsed per
8101
+ // Captain's lean. Inverse pairs flip the direction onto the kept containment
8102
+ // edge. Collapsing these lowers the edge-duplicate gate collision bound.
8103
+ // ── Tier 1: byte-identical shadows ──
8104
+ { 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." },
8105
+ { 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." },
8106
+ { 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." },
8107
+ { 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." },
8108
+ { 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." },
8109
+ // ── Tier 2: tense / suffix twins ──
8110
+ { 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." },
8111
+ { 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." },
8112
+ { 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)." },
8113
+ // ── Tier 3: near-synonyms (Captain's lean = collapse) ──
8114
+ { 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)." },
8115
+ { 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)." },
8116
+ { 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)." },
8117
+ // ── Inverse pairs: flip onto the kept containment edge ──
8118
+ // Flip guards reference the PRE-flip (original-edge) endpoints: the original
8119
+ // source becomes the new target after flip, so requires_source_type matches
8120
+ // the new edge's target_type and requires_target_type matches its source_type.
8121
+ { 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.' },
8122
+ { 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.' }
8123
+ ],
7947
8124
  "0.9.2": [
7948
8125
  // (since v0.9.2, UPG-663) Journey-model disambiguation. A journey_phase
7949
8126
  // is a temporal BAND over the journey's single step timeline, not a
@@ -9562,6 +9739,7 @@ var UPG_PROPERTY_SCHEMA = {
9562
9739
  // CustomerJourneyStageProperties: Customer journey stage.
9563
9740
  customer_journey_stage: {
9564
9741
  stage_type: { type: "string", enum: ["awareness", "acquisition", "activation", "retention", "revenue", "referral"], description: "Which pirate metric (AAARRR) this stage maps to" },
9742
+ 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." },
9565
9743
  avg_duration: { type: "string", description: "Average time a customer spends in this stage" },
9566
9744
  conversion_rate: { type: "number", description: "Percentage of customers who advance to the next stage" }
9567
9745
  },
@@ -9953,7 +10131,7 @@ var UPG_PROPERTY_SCHEMA = {
9953
10131
  observed_at: { type: "string", description: "ISO date observed." },
9954
10132
  source: { type: "string", description: "Free-text provenance note" }
9955
10133
  },
9956
- // ExperimentProperties: A structured activity designed to test a hypothesis.
10134
+ // ExperimentProperties: A structured test designed to validate a hypothesis. The canonical unit of
9957
10135
  experiment: {
9958
10136
  method: { type: "string", description: 'Experimental method (e.g. "A/B test", "usability study", "smoke test")' },
9959
10137
  start_date: { type: "string", description: "ISO start date" },
@@ -9963,10 +10141,11 @@ var UPG_PROPERTY_SCHEMA = {
9963
10141
  expected_lift_unit: { type: "string", enum: ["percentage", "absolute", "ratio"], description: "Unit of `expected_lift`" },
9964
10142
  actual_lift: { type: "number", description: "Observed change in the primary metric" }
9965
10143
  },
9966
- // ExperimentPlanProperties: Planning intent for a structured test of a hypothesis (UCS pattern P4: work-unit).
10144
+ // ExperimentPlanProperties: The validation PLAN: the design for a structured test of a hypothesis
9967
10145
  experiment_plan: {
9968
10146
  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." },
9969
10147
  success_criteria: { type: "string", description: 'Plain-English description of "passing"' },
10148
+ 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." },
9970
10149
  projected_reach: {
9971
10150
  type: "assessment",
9972
10151
  scale_id: "reach_5",
@@ -10502,7 +10681,7 @@ var UPG_PROPERTY_SCHEMA = {
10502
10681
  // JourneyStepProperties: Single step within a user journey. A user-moment on the journey's single
10503
10682
  journey_step: {
10504
10683
  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." },
10505
- touchpoint: { type: "string", description: "Interaction touchpoint" },
10684
+ 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." },
10506
10685
  channel: { type: "string", description: 'Channel (e.g. "web", "email", "in-store")' },
10507
10686
  emotion_score: {
10508
10687
  type: "assessment",
@@ -11772,12 +11951,13 @@ var UPG_PROPERTY_SCHEMA = {
11772
11951
  env_status: { type: "string", enum: ["available", "in_use", "maintenance", "unavailable"], description: "Current availability status of the environment" },
11773
11952
  config: { type: "string", description: "Configuration details (e.g. OS, browser version, database seed)" }
11774
11953
  },
11775
- // TestPlanProperties: TestPlan entity.
11954
+ // TestPlanProperties: Test plan: the QA verification procedure for a product or release.
11776
11955
  test_plan: {
11777
- plan_type: { type: "string", enum: ["usability", "concept", "ab_test", "beta", "smoke"], description: "Test type" },
11778
- sample_size: { type: "number", description: "Participants or observations" },
11779
- duration: { type: "string", description: 'Run duration. @example "2 weeks"' },
11780
- success_criteria: { type: "string", description: "Criteria determining whether the test passes" }
11956
+ test_scope: { type: "string", description: 'What the plan covers (e.g. "checkout flow", "auth module", "release 2.4").' },
11957
+ plan_type: { type: "string", enum: ["release", "regression", "integration", "acceptance", "smoke", "exploratory"], description: "Kind of verification effort this plan governs." },
11958
+ environments: { type: "object", description: "Environments the plan exercises (mirrors `TestEnvironmentProperties.env_type`)." },
11959
+ entry_criteria: { type: "string", description: "Conditions that must hold before execution may begin." },
11960
+ pass_criteria: { type: "string", description: "Exit / pass criteria determining whether the plan succeeds." }
11781
11961
  },
11782
11962
  // TestResultProperties: Single test execution result.
11783
11963
  test_result: {
@@ -11843,7 +12023,7 @@ var UPG_PROPERTY_SCHEMA = {
11843
12023
  },
11844
12024
  // TouchpointProperties: Customer touchpoint.
11845
12025
  touchpoint: {
11846
- touchpoint_channel: { type: "string", description: 'Channel where the interaction occurs (e.g. "email", "in-app", "call")' },
12026
+ 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)" },
11847
12027
  touchpoint_type: { type: "string", enum: ["reactive", "proactive", "automated"], description: "Whether the touchpoint is initiated by the customer, CSM, or system" },
11848
12028
  satisfaction_score: { type: "number", description: "Customer satisfaction score for this touchpoint" }
11849
12029
  },
@@ -11896,7 +12076,7 @@ var UPG_PROPERTY_SCHEMA = {
11896
12076
  // UserJourneyProperties: User journey map.
11897
12077
  user_journey: {
11898
12078
  scope: { type: "string", description: 'Scope (e.g. "end-to-end onboarding")' },
11899
- journey_type: { type: "string", enum: ["current_state", "future_state", "day_in_the_life", "service_blueprint"], description: "Maps current or future state" },
12079
+ journey_type: { type: "string", enum: ["current_state", "future_state", "day_in_the_life"], description: "Maps current or future state" },
11900
12080
  scenario: { type: "string", description: "Scenario context" }
11901
12081
  },
11902
12082
  // UserStoryProperties: "As X, I want Y so Z" templated promise on a user story (UCS pattern P5).
@@ -17966,9 +18146,10 @@ var PRIORITY_LABELS = [
17966
18146
  {
17967
18147
  id: "experiment",
17968
18148
  canonical_label: "Experiment",
18149
+ // V6 (UPG-664): dropped 'test' (too generic; collides) and 'validation'
18150
+ // (the domain name). The deprecated growth/pricing/ab experiment types
18151
+ // redirect to `experiment` (V2), so their labels stay here.
17969
18152
  alt_labels: [
17970
- "test",
17971
- "validation",
17972
18153
  "ab test",
17973
18154
  "a/b test",
17974
18155
  "split test",
@@ -18424,8 +18605,8 @@ var STANDARD_LABELS = {
18424
18605
  design_sprint: { alt_labels: ["sprint", "gv sprint", "google ventures sprint"] },
18425
18606
  // Validation layer
18426
18607
  learning: { alt_labels: ["validated learning", "lesson learned", "takeaway"] },
18427
- // 'experiment plan' stripped (P-B / UPG-670): canonical label of the distinct `experiment_plan` type.
18428
- test_plan: { alt_labels: ["test strategy", "validation plan"] },
18608
+ // test_plan re-homed validation → QA (UPG-678); its label entry now lives in
18609
+ // the Quality Assurance & Testing layer below.
18429
18610
  research_plan: { alt_labels: ["study plan", "research brief"] },
18430
18611
  evidence: { alt_labels: ["proof", "supporting data", "signal"] },
18431
18612
  variant: { alt_labels: ["test variant", "experiment arm", "variation"] },
@@ -18679,6 +18860,10 @@ var STANDARD_LABELS = {
18679
18860
  help_video: { alt_labels: ["tutorial video", "screencast", "how-to video"] },
18680
18861
  learning_path: { alt_labels: ["curriculum", "course track", "learning journey"] },
18681
18862
  // Quality Assurance & Testing layer
18863
+ // test_plan re-homed validation → QA (UPG-678): QA-shaped labels only.
18864
+ // 'validation plan' and 'experiment plan' stripped — those belong to the
18865
+ // validation-side `experiment_plan` type.
18866
+ test_plan: { alt_labels: ["test strategy", "qa plan", "master test plan"] },
18682
18867
  test_suite: { alt_labels: ["test collection", "test group"] },
18683
18868
  test_case: { alt_labels: ["test", "test scenario", "verification step"] },
18684
18869
  qa_session: { alt_labels: ["testing session", "exploratory test", "qa round"] },
@@ -19050,11 +19235,13 @@ var DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK = {
19050
19235
  seqStep(
19051
19236
  8,
19052
19237
  "Test",
19053
- // DT-PB-1: was `experiment` — which resolves to NO canonical edge with
19054
- // hypothesis or test_plan, forcing an orphan. `experiment_run` is the
19055
- // hypothesis-linked test unit (experiment_run_validates_hypothesis,
19056
- // test_plan_ran_as_experiment_run, experiment_run_yields_evidence).
19057
- ["experiment_run", "test_plan", "evidence"],
19238
+ // DT-PB-1: was `experiment` — which resolved to NO canonical edge with
19239
+ // hypothesis, forcing an orphan. `experiment_run` is the hypothesis-linked
19240
+ // test unit (experiment_run_validates_hypothesis, experiment_run_yields_evidence).
19241
+ // (UPG-664) `test_plan` re-homed to QA (UPG-678); the validation plan is
19242
+ // `experiment_plan`, which designs the experiment
19243
+ // (experiment_plan_designs_experiment) and runs as experiment_run(s).
19244
+ ["experiment_run", "experiment_plan", "evidence"],
19058
19245
  "Validate with targeted experiment runs. Close the loop between research and action."
19059
19246
  )
19060
19247
  ]
@@ -20430,19 +20617,18 @@ var DISCOVERY_GUIDE = {
20430
20617
  var VALIDATION_GUIDE = {
20431
20618
  domain_id: "validation",
20432
20619
  anchor_entity: "hypothesis",
20433
- // creation_sequence covers every entity registered to the `validation`
20434
- // domain. `experiment` is the legacy parent that decomposed into
20435
- // experiment_plan + experiment_run in v0.2.6 (split rule lives in
20436
- // UPG_SPLIT_MIGRATIONS); it remains in the registry for round-trip
20437
- // compatibility and is listed last so new authors reach the split pair
20438
- // first.
20439
- creation_sequence: ["hypothesis", "experiment_plan", "experiment_run", "test_plan", "evidence", "learning", "research_plan", "experiment"],
20620
+ // (UPG-664) creation_sequence covers every entity registered to the
20621
+ // `validation` domain along the canonical chain hypothesis → experiment_plan
20622
+ // → experiment → experiment_run. `experiment` is the canonical structured
20623
+ // test; experiment_run is its optional replication child. `test_plan`
20624
+ // re-homed to the QA/testing domain (UPG-678).
20625
+ creation_sequence: ["hypothesis", "experiment_plan", "experiment", "experiment_run", "evidence", "learning", "research_plan"],
20440
20626
  patterns: [
20441
20627
  {
20442
20628
  name: "Hypothesis Testing Loop",
20443
- description: "Hypotheses require plans (intent), plans run as experiment_runs (execution), runs produce evidence and learnings, learnings update hypotheses",
20444
- entity_types: ["hypothesis", "experiment_plan", "experiment_run", "evidence", "learning"],
20445
- edge_chain: ["hypothesis_requires_experiment_plan", "experiment_plan_ran_as_experiment_run", "experiment_run_yields_evidence", "experiment_run_produces_learning", "learning_updates_hypothesis"]
20629
+ 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",
20630
+ entity_types: ["hypothesis", "experiment_plan", "experiment", "experiment_run", "evidence", "learning"],
20631
+ 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"]
20446
20632
  }
20447
20633
  ],
20448
20634
  required_bridges: [
@@ -20560,7 +20746,8 @@ var UX_DESIGN_GUIDE = {
20560
20746
  anti_patterns: [
20561
20747
  { description: "Screens without journeys: isolated screens miss the experience context" },
20562
20748
  { description: "Prototypes without testing: if nobody tests the prototype, it is just art" },
20563
- { description: "Design questions left open: exploration status should progress to resolved or parked" }
20749
+ { description: "Design questions left open: exploration status should progress to resolved or parked" },
20750
+ { 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." }
20564
20751
  ]
20565
20752
  };
20566
20753
  var DESIGN_SYSTEM_GUIDE = {
@@ -20703,7 +20890,9 @@ var DEVOPS_GUIDE = {
20703
20890
  var TESTING_GUIDE = {
20704
20891
  domain_id: "testing",
20705
20892
  anchor_entity: "test_suite",
20706
- creation_sequence: ["test_suite", "test_case", "qa_session", "regression_test", "test_coverage_report", "test_environment", "test_result"],
20893
+ // (UPG-678) test_plan re-homed validation → QA: the verification-approach
20894
+ // plan that the suites execute. Listed first as the QA planning layer.
20895
+ creation_sequence: ["test_plan", "test_suite", "test_case", "qa_session", "regression_test", "test_coverage_report", "test_environment", "test_result"],
20707
20896
  patterns: [
20708
20897
  {
20709
20898
  name: "Test Pyramid",
@@ -20800,7 +20989,7 @@ var AI_GUIDE = {
20800
20989
  }
20801
20990
  ],
20802
20991
  required_bridges: [
20803
- { edge_type: "prompt_template_targets_ai_model", target_domain: "content", when: "Prompt templates should link to the models they target" },
20992
+ { 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" },
20804
20993
  { edge_type: "ai_guardrail_enforces_security_policy", target_domain: "security", when: "AI guardrails should map to the security policies that constrain them" }
20805
20994
  ],
20806
20995
  anti_patterns: [
@@ -20991,7 +21180,8 @@ var CUSTOMER_SUCCESS_GUIDE = {
20991
21180
  anti_patterns: [
20992
21181
  { description: "Health scores without components: define which metrics compose the score" },
20993
21182
  { description: "Playbooks without triggers: specify what conditions activate each playbook" },
20994
- { description: "Churn reasons without analysis: understand patterns, not just individual cases" }
21183
+ { description: "Churn reasons without analysis: understand patterns, not just individual cases" },
21184
+ { 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." }
20995
21185
  ]
20996
21186
  };
20997
21187
  var CONTENT_GUIDE = {
@@ -24389,9 +24579,7 @@ var UPG_REGIONS = [
24389
24579
  "objective_achieved_through_key_result",
24390
24580
  "objective_measured_by_metric",
24391
24581
  "key_result_quantified_by_metric",
24392
- "key_result_tracked_by_metric",
24393
24582
  "outcome_measured_by_metric",
24394
- "outcome_tracked_by_metric",
24395
24583
  "metric_decomposes_into_metric",
24396
24584
  "metric_guards_metric",
24397
24585
  "metric_drives_metric"
@@ -24645,8 +24833,7 @@ var UPG_REGIONS = [
24645
24833
  },
24646
24834
  {
24647
24835
  type: "experiment",
24648
- role: "hub",
24649
- notes: "self-nesting"
24836
+ role: "hub"
24650
24837
  },
24651
24838
  {
24652
24839
  type: "experiment_plan",
@@ -24654,7 +24841,8 @@ var UPG_REGIONS = [
24654
24841
  },
24655
24842
  {
24656
24843
  type: "experiment_run",
24657
- role: "container"
24844
+ role: "container",
24845
+ notes: "self-nesting"
24658
24846
  },
24659
24847
  {
24660
24848
  type: "learning",
@@ -24664,10 +24852,6 @@ var UPG_REGIONS = [
24664
24852
  type: "evidence",
24665
24853
  role: "leaf"
24666
24854
  },
24667
- {
24668
- type: "test_plan",
24669
- role: "leaf"
24670
- },
24671
24855
  {
24672
24856
  type: "research_plan",
24673
24857
  role: "leaf"
@@ -24759,7 +24943,7 @@ var UPG_REGIONS = [
24759
24943
  },
24760
24944
  {
24761
24945
  direction: "export",
24762
- edge_id: "insight_informs_opportunity_cross_domain",
24946
+ edge_id: "insight_informs_opportunity",
24763
24947
  crosses_into: "discovery_research_validation"
24764
24948
  },
24765
24949
  {
@@ -25443,7 +25627,7 @@ var UPG_REGIONS = [
25443
25627
  },
25444
25628
  {
25445
25629
  type: "prompt_template",
25446
- role: "leaf"
25630
+ role: "container"
25447
25631
  },
25448
25632
  {
25449
25633
  type: "prompt_version",
@@ -25875,7 +26059,7 @@ var UPG_REGIONS = [
25875
26059
  "business_model_earns_via_revenue_stream",
25876
26060
  "business_model_costs_via_cost_structure",
25877
26061
  "business_model_measured_by_unit_economics",
25878
- "business_model_reaches_via_distribution_channel",
26062
+ "business_model_distributes_via_distribution_channel",
25879
26063
  "business_model_maintains_customer_relationship",
25880
26064
  "business_model_requires_key_resource",
25881
26065
  "business_model_performs_key_activity",
@@ -26044,11 +26228,6 @@ var UPG_REGIONS = [
26044
26228
  edge_id: "key_result_quantified_by_metric",
26045
26229
  crosses_into: "strategy_outcomes"
26046
26230
  },
26047
- {
26048
- direction: "import",
26049
- edge_id: "key_result_tracked_by_metric",
26050
- crosses_into: "strategy_outcomes"
26051
- },
26052
26231
  {
26053
26232
  direction: "import",
26054
26233
  edge_id: "revenue_stream_measured_by_metric_cross_domain",
@@ -26160,6 +26339,10 @@ var UPG_REGIONS = [
26160
26339
  type: "infrastructure_component",
26161
26340
  role: "container"
26162
26341
  },
26342
+ {
26343
+ type: "test_plan",
26344
+ role: "container"
26345
+ },
26163
26346
  {
26164
26347
  type: "test_suite",
26165
26348
  role: "container"
@@ -26441,7 +26624,7 @@ var UPG_REGIONS = [
26441
26624
  boundary_edges: [
26442
26625
  {
26443
26626
  direction: "import",
26444
- edge_id: "product_experiences_incident_hierarchy",
26627
+ edge_id: "product_experiences_incident",
26445
26628
  crosses_into: "product_delivery"
26446
26629
  },
26447
26630
  {
@@ -26991,7 +27174,7 @@ function isCanonical(text) {
26991
27174
  }
26992
27175
 
26993
27176
  // src/index.ts
26994
- var UPG_VERSION = "0.9.8";
27177
+ var UPG_VERSION = "0.9.9";
26995
27178
  var UPG_FORMAT_VERSION = "0.4.0";
26996
27179
  var MARKDOWN_FORMAT_VERSION = "0.1";
26997
27180
  var UPG_TYPES = getTypes();