@unified-product-graph/core 0.7.4 → 0.8.0

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
@@ -43,12 +43,14 @@ __export(index_exports, {
43
43
  OPERATIONS_TEAM_RITUALS_PLAYBOOK: () => OPERATIONS_TEAM_RITUALS_PLAYBOOK,
44
44
  PRODUCT_DELIVERY_PLAYBOOK: () => PRODUCT_DELIVERY_PLAYBOOK,
45
45
  PRODUCT_FEEDBACK_SYNTHESIS_PLAYBOOK: () => PRODUCT_FEEDBACK_SYNTHESIS_PLAYBOOK,
46
+ PROPERTY_SCALE_MAP: () => PROPERTY_SCALE_MAP,
46
47
  REFLECT_MODES: () => REFLECT_MODES,
47
48
  STRATEGY_OUTCOMES_PLAYBOOK: () => STRATEGY_OUTCOMES_PLAYBOOK,
48
49
  UPG_ACTIVE_TYPES: () => UPG_ACTIVE_TYPES,
49
50
  UPG_ANTI_PATTERNS: () => UPG_ANTI_PATTERNS,
50
51
  UPG_APPROACHES: () => UPG_APPROACHES,
51
52
  UPG_APPROACHES_BY_ID: () => UPG_APPROACHES_BY_ID,
53
+ UPG_CANONICAL_FORMAT_VERSION: () => UPG_CANONICAL_FORMAT_VERSION,
52
54
  UPG_CONTAINMENT_FREE_TYPES: () => UPG_CONTAINMENT_FREE_TYPES,
53
55
  UPG_COUNT_BENCHMARKS: () => UPG_COUNT_BENCHMARKS,
54
56
  UPG_CROSS_EDGE_TYPES: () => UPG_CROSS_EDGE_TYPES,
@@ -112,8 +114,10 @@ __export(index_exports, {
112
114
  coerceProductStage: () => coerceProductStage,
113
115
  collectSlugsForType: () => collectSlugsForType,
114
116
  compareVersions: () => compareVersions,
117
+ computeBodyChecksum: () => computeBodyChecksum,
115
118
  evaluateAntiPatterns: () => evaluateAntiPatterns,
116
119
  findInvalidStatusMigrationTargets: () => findInvalidStatusMigrationTargets,
120
+ formatUpgText: () => formatUpgText,
117
121
  generateSlug: () => generateSlug,
118
122
  getAnchorEntity: () => getAnchorEntity,
119
123
  getAntiPatternById: () => getAntiPatternById,
@@ -140,6 +144,7 @@ __export(index_exports, {
140
144
  getMigrationMap: () => getMigrationMap,
141
145
  getPlaybookById: () => getPlaybookById,
142
146
  getPlaybooksForRegion: () => getPlaybooksForRegion,
147
+ getPropertiesForScale: () => getPropertiesForScale,
143
148
  getPropertyDefaultScale: () => getPropertyDefaultScale,
144
149
  getPropertyMigrations: () => getPropertyMigrations,
145
150
  getPropertySchema: () => getPropertySchema,
@@ -156,6 +161,7 @@ __export(index_exports, {
156
161
  getValidChildren: () => getValidChildren,
157
162
  getVisibleTypes: () => getVisibleTypes,
158
163
  hasStatusMigration: () => hasStatusMigration,
164
+ isCanonical: () => isCanonical,
159
165
  isCanonicalProductStage: () => isCanonicalProductStage,
160
166
  isContainmentFreeType: () => isContainmentFreeType,
161
167
  isDeprecatedType: () => isDeprecatedType,
@@ -166,6 +172,7 @@ __export(index_exports, {
166
172
  isLifecycleFreeType: () => isLifecycleFreeType,
167
173
  isLifecyclePlannedType: () => isLifecyclePlannedType,
168
174
  isPolymorphicEdge: () => isPolymorphicEdge,
175
+ isPortfolio: () => isPortfolio,
169
176
  isRegisteredPolymorphicEdge: () => isRegisteredPolymorphicEdge,
170
177
  isSubSequenceStep: () => isSubSequenceStep,
171
178
  isUPGDocument: () => isUPGDocument,
@@ -175,6 +182,8 @@ __export(index_exports, {
175
182
  migrateNodeProperties: () => migrateNodeProperties,
176
183
  migrateProductStage: () => migrateProductStage,
177
184
  migrateStatusValue: () => migrateStatusValue,
185
+ normalizeDocument: () => normalizeDocument,
186
+ parseUpg: () => parseUpg,
178
187
  pickCanonicalEdge: () => pickCanonicalEdge,
179
188
  resolveAllEdges: () => resolveAllEdges,
180
189
  resolveContainmentEdge: () => resolveContainmentEdge,
@@ -182,6 +191,7 @@ __export(index_exports, {
182
191
  resolveLabel: () => resolveLabel,
183
192
  resolveSlugCollision: () => resolveSlugCollision,
184
193
  rotateSlug: () => rotateSlug,
194
+ serializeCanonical: () => serializeCanonical,
185
195
  validateProductStageStrict: () => validateProductStageStrict,
186
196
  validateUPGDocument: () => validateUPGDocument,
187
197
  validateUPGFramework: () => validateUPGFramework,
@@ -6350,20 +6360,42 @@ var PROPERTY_SCALE_MAP = {
6350
6360
  // discovery, users, feedback
6351
6361
  // ── Severity ─────────────────────────────────────────────────────────────
6352
6362
  severity: "severity_5",
6353
- // users, engineering, ai, gtm, customer-success
6363
+ // users, engineering, ai, gtm, customer-success, security, accessibility
6354
6364
  severity_of_finding: "severity_5",
6355
6365
  // validation/experiment_run
6366
+ bug_severity: "severity_5",
6367
+ // product-spec/bug (UPG-579 Option C collapse)
6368
+ risk_level: "severity_5",
6369
+ // compliance/risk (UPG-579 Option B)
6370
+ scarcity_risk: "severity_5",
6371
+ // market-intelligence (UPG-579 Option B)
6356
6372
  // ── Pain ─────────────────────────────────────────────────────────────────
6357
6373
  pain: "pain_5",
6358
6374
  // discovery
6375
+ pain_score: "pain_5",
6376
+ // user (UPG-579 Option B)
6377
+ friction_score: "pain_5",
6378
+ // growth/ux (UPG-579 Option B)
6359
6379
  // ── Impact ───────────────────────────────────────────────────────────────
6360
6380
  impact: "impact_5",
6361
6381
  // discovery, market, security, compliance
6362
6382
  projected_impact: "impact_5",
6363
6383
  // validation/experiment_plan
6384
+ revenue_impact: "impact_5",
6385
+ // sales/business-model (UPG-579 Option B)
6386
+ effectiveness: "impact_5",
6387
+ // security/marketing (UPG-579 Option B)
6388
+ opportunity_score: "impact_5",
6389
+ // discovery/opportunity (UPG-579 Option B)
6364
6390
  // ── Confidence ───────────────────────────────────────────────────────────
6365
6391
  confidence: "confidence_5",
6366
6392
  // discovery, validation, sales, product-spec
6393
+ current_confidence: "confidence_5",
6394
+ // validation/hypothesis (UPG-579 Option B)
6395
+ likelihood: "confidence_5",
6396
+ // market/risk (UPG-579 Option B)
6397
+ probability: "confidence_5",
6398
+ // sales/forecast (UPG-579 Option B)
6367
6399
  // ── Effort ───────────────────────────────────────────────────────────────
6368
6400
  effort: "effort_5",
6369
6401
  // discovery
@@ -6371,17 +6403,35 @@ var PROPERTY_SCALE_MAP = {
6371
6403
  // feedback
6372
6404
  effort_to_fix: "effort_5",
6373
6405
  // engineering
6406
+ cost_estimate: "effort_5",
6407
+ // validation/experiment_plan (UPG-579 Option B)
6374
6408
  // ── Importance ───────────────────────────────────────────────────────────
6375
6409
  importance: "importance_5",
6376
6410
  // users
6411
+ influence: "importance_5",
6412
+ // team-org/stakeholder (UPG-579 Option B)
6413
+ interest: "importance_5",
6414
+ // team-org/stakeholder (UPG-579 Option B)
6415
+ relevance: "importance_5",
6416
+ // market/content (UPG-579 Option B)
6417
+ weight: "importance_5",
6418
+ // validation/evidence (UPG-579 Option B)
6377
6419
  // ── Satisfaction ─────────────────────────────────────────────────────────
6378
- current_satisfaction: "satisfaction_5"
6420
+ current_satisfaction: "satisfaction_5",
6379
6421
  // users
6422
+ emotion_score: "satisfaction_5"
6423
+ // ux-design/customer-success journey (UPG-579 Option B)
6424
+ // Intentionally NOT mapped (resolve to the generic scale_5):
6425
+ // rarity : VRIO distinctiveness; no canonical named scale fits.
6426
+ // strength : deprecated (validation/evidence; superseded by weight).
6380
6427
  };
6381
6428
  var DEFAULT_SCALE_ID = "scale_5";
6382
6429
  function getPropertyDefaultScale(_entityType, propertyName) {
6383
6430
  return PROPERTY_SCALE_MAP[propertyName] ?? DEFAULT_SCALE_ID;
6384
6431
  }
6432
+ function getPropertiesForScale(scaleId) {
6433
+ return Object.entries(PROPERTY_SCALE_MAP).filter(([, id]) => id === scaleId).map(([property]) => property);
6434
+ }
6385
6435
 
6386
6436
  // src/grammar/enum-scales.ts
6387
6437
  var UPG_ENUM_SCALES = {
@@ -6467,6 +6517,159 @@ var UPG_ENUM_SCALES = {
6467
6517
  { value: "warning", label: "Warning", description: "Soft signal: should consider and address, but can override with justification." },
6468
6518
  { value: "guideline", label: "Guideline", description: "Recommendation: encouraged and expected in most cases, but not enforced." }
6469
6519
  ]
6520
+ },
6521
+ // ── ProxyConfidence ─────────────────────────────────────────────────
6522
+ ProxyConfidence: {
6523
+ id: "ProxyConfidence",
6524
+ label: "Proxy confidence",
6525
+ description: "How strongly a proxy metric predicts the direct measure it stands in for.",
6526
+ tone_direction: "high-is-good",
6527
+ values: [
6528
+ { value: "strong", label: "Strong", description: "Tracks the direct measure closely; a reliable stand-in." },
6529
+ { value: "moderate", label: "Moderate", description: "Correlates with the direct measure but with meaningful slippage." },
6530
+ { value: "weak", label: "Weak", description: "Loosely related; use with caution and corroborate." }
6531
+ ]
6532
+ },
6533
+ // ── CauseConfidence ─────────────────────────────────────────────────
6534
+ CauseConfidence: {
6535
+ id: "CauseConfidence",
6536
+ label: "Cause confidence",
6537
+ description: "Maturity of a root-cause determination during incident debugging: how far a proposed cause has been validated.",
6538
+ tone_direction: "high-is-good",
6539
+ values: [
6540
+ { value: "hypothesised", label: "Hypothesised", description: "A proposed cause, not yet tested against evidence." },
6541
+ { value: "likely", label: "Likely", description: "Supported by evidence but not conclusively proven." },
6542
+ { value: "confirmed", label: "Confirmed", description: "Verified as the cause; reproduced or otherwise established." }
6543
+ ]
6544
+ },
6545
+ // ── ComfortLevel ────────────────────────────────────────────────────
6546
+ ComfortLevel: {
6547
+ id: "ComfortLevel",
6548
+ label: "Comfort level",
6549
+ description: "A person's comfort with a tool, technology, or practice. Closed set so personas compare on the same axis across products.",
6550
+ tone_direction: "high-is-good",
6551
+ values: [
6552
+ { value: "low", label: "Low", description: "Little or no familiarity; needs guidance to proceed." },
6553
+ { value: "medium", label: "Medium", description: "Functional, everyday competence." },
6554
+ { value: "high", label: "High", description: "Confident, fluent use without assistance." },
6555
+ { value: "expert", label: "Expert", description: "Deep mastery; can teach others or extend the tool." },
6556
+ { value: "other", label: "Other", description: "A comfort profile not captured by the above tiers." }
6557
+ ]
6558
+ },
6559
+ // ── LogLevel ────────────────────────────────────────────────────────
6560
+ LogLevel: {
6561
+ id: "LogLevel",
6562
+ label: "Log level",
6563
+ description: "Operational verbosity of a monitor or alert signal. How loud the signal should be, not how bad an outcome is for the user (kept distinct from severity_5).",
6564
+ tone_direction: "low-is-good",
6565
+ values: [
6566
+ { value: "critical", label: "Critical", description: "Page immediately; the signal is service-affecting." },
6567
+ { value: "warning", label: "Warning", description: "Needs attention soon; not yet service-affecting." },
6568
+ { value: "info", label: "Info", description: "Informational; no action required." }
6569
+ ]
6570
+ },
6571
+ // ── IncidentSeverity ────────────────────────────────────────────────
6572
+ IncidentSeverity: {
6573
+ id: "IncidentSeverity",
6574
+ label: "Incident severity",
6575
+ description: "Paging-tier classification of an incident, driving escalation and response process. Distinct from user-impact severity_5 and from LogLevel.",
6576
+ tone_direction: "low-is-good",
6577
+ values: [
6578
+ { value: "sev1", label: "SEV1", description: "Critical outage; full response, executive-visible." },
6579
+ { value: "sev2", label: "SEV2", description: "Major degradation; urgent response." },
6580
+ { value: "sev3", label: "SEV3", description: "Minor or partial impact; handled within hours." },
6581
+ { value: "sev4", label: "SEV4", description: "Negligible impact; routine handling." }
6582
+ ]
6583
+ },
6584
+ // ── SignalSentiment ─────────────────────────────────────────────────
6585
+ SignalSentiment: {
6586
+ id: "SignalSentiment",
6587
+ label: "Signal sentiment",
6588
+ description: "Sentiment polarity of an inbound signal (feedback, support, customer).",
6589
+ tone_direction: "high-is-good",
6590
+ values: [
6591
+ { value: "positive", label: "Positive", description: "Favourable signal; satisfaction or advocacy." },
6592
+ { value: "neutral", label: "Neutral", description: "No clear positive or negative lean." },
6593
+ { value: "negative", label: "Negative", description: "Unfavourable signal; dissatisfaction or risk." },
6594
+ { value: "mixed", label: "Mixed", description: "Contains both positive and negative elements." }
6595
+ ]
6596
+ },
6597
+ // ── MaturityLevel ───────────────────────────────────────────────────
6598
+ MaturityLevel: {
6599
+ id: "MaturityLevel",
6600
+ label: "Maturity level",
6601
+ description: "Capability or process maturity on the CMMI ladder.",
6602
+ tone_direction: "high-is-good",
6603
+ values: [
6604
+ { value: "initial", label: "Initial", description: "Ad hoc and unpredictable; little process." },
6605
+ { value: "developing", label: "Developing", description: "Basic process emerging but inconsistent." },
6606
+ { value: "defined", label: "Defined", description: "Documented, standardised process in place." },
6607
+ { value: "managed", label: "Managed", description: "Measured and controlled against objectives." },
6608
+ { value: "optimizing", label: "Optimizing", description: "Continuous, data-driven improvement." }
6609
+ ]
6610
+ },
6611
+ // ── ConformanceLevel ────────────────────────────────────────────────
6612
+ ConformanceLevel: {
6613
+ id: "ConformanceLevel",
6614
+ label: "Conformance level",
6615
+ description: "WCAG accessibility conformance level.",
6616
+ tone_direction: "high-is-good",
6617
+ values: [
6618
+ { value: "A", label: "A", description: "Minimum conformance; essential barriers removed." },
6619
+ { value: "AA", label: "AA", description: "Addresses the most common barriers; the usual legal target." },
6620
+ { value: "AAA", label: "AAA", description: "Highest level; not achievable for all content." }
6621
+ ]
6622
+ },
6623
+ // ── DataSensitivity ─────────────────────────────────────────────────
6624
+ DataSensitivity: {
6625
+ id: "DataSensitivity",
6626
+ label: "Data sensitivity",
6627
+ description: "Data classification tier governing handling and access.",
6628
+ tone_direction: "neutral",
6629
+ values: [
6630
+ { value: "public", label: "Public", description: "No restriction; safe to disclose openly." },
6631
+ { value: "internal", label: "Internal", description: "For internal use; not for external release." },
6632
+ { value: "confidential", label: "Confidential", description: "Sensitive; restricted to a need-to-know basis." },
6633
+ { value: "restricted", label: "Restricted", description: "Highly sensitive; strict controls and auditing." }
6634
+ ]
6635
+ },
6636
+ // ── DifficultyLevel ─────────────────────────────────────────────────
6637
+ DifficultyLevel: {
6638
+ id: "DifficultyLevel",
6639
+ label: "Difficulty level",
6640
+ description: "Learner difficulty tier for educational content.",
6641
+ tone_direction: "neutral",
6642
+ values: [
6643
+ { value: "beginner", label: "Beginner", description: "No prior knowledge assumed." },
6644
+ { value: "intermediate", label: "Intermediate", description: "Assumes working familiarity with the basics." },
6645
+ { value: "advanced", label: "Advanced", description: "Assumes deep, expert-level background." }
6646
+ ]
6647
+ },
6648
+ // ── FrequencyRating ─────────────────────────────────────────────────
6649
+ FrequencyRating: {
6650
+ id: "FrequencyRating",
6651
+ label: "Frequency rating",
6652
+ description: "Qualitative how-often rating. Distinct from the numeric frequency_5 scale and from Cadence calendar tiers.",
6653
+ tone_direction: "neutral",
6654
+ values: [
6655
+ { value: "constant", label: "Constant", description: "Effectively always; continuous occurrence." },
6656
+ { value: "regular", label: "Regular", description: "Happens on a predictable, recurring basis." },
6657
+ { value: "occasional", label: "Occasional", description: "Happens sometimes, without a fixed pattern." },
6658
+ { value: "rare", label: "Rare", description: "Happens infrequently." },
6659
+ { value: "other", label: "Other", description: "A frequency not captured by the above tiers." }
6660
+ ]
6661
+ },
6662
+ // ── EvidenceDirection ───────────────────────────────────────────────
6663
+ EvidenceDirection: {
6664
+ id: "EvidenceDirection",
6665
+ label: "Evidence direction",
6666
+ description: "Direction of evidence relative to a claim. Distinct from confidence_impact (strengthens/weakens) per the UPG-579 polysemy verdicts.",
6667
+ tone_direction: "neutral",
6668
+ values: [
6669
+ { value: "supports", label: "Supports", description: "The evidence backs the claim." },
6670
+ { value: "refutes", label: "Refutes", description: "The evidence contradicts the claim." },
6671
+ { value: "neutral", label: "Neutral", description: "The evidence is inconclusive either way." }
6672
+ ]
6470
6673
  }
6471
6674
  };
6472
6675
  function getEnumScale(name) {
@@ -6813,6 +7016,41 @@ function getDeprecatedTypes() {
6813
7016
  return deprecated;
6814
7017
  }
6815
7018
  var UPG_PROPERTY_MIGRATIONS = {
7019
+ // ── v0.8.0: UPG-574 deprecated-property removal pass ───────────────────
7020
+ //
7021
+ // The seven properties tagged `@deprecated since v0.4.0` that survived the
7022
+ // v0.5.0 hygiene pass are removed from the spec in v0.8.0 (a breaking
7023
+ // release). Five of the seven already carry a migration:
7024
+ // - `task.task_status` / `bug.bug_status` lift to `UPGBaseNode.status`
7025
+ // (see `UPG_PROPERTY_MIGRATIONS['0.4.0']`, values identical).
7026
+ // - `learning.metric` drops in favour of the `learning_observed_on_metric`
7027
+ // edge (see `UPG_PROPERTY_MIGRATIONS['0.5.0']`).
7028
+ // The two remaining property surfaces are migrated here:
7029
+ // - `evidence.strength` (simple `strong | moderate | weak` enum) is
7030
+ // dropped in favour of `weight: UPGAssessment` (scale `weight_5pt`).
7031
+ // Authors carrying the legacy key should copy the value to `weight`
7032
+ // before applying this migration: `strong → 5`, `moderate → 3`,
7033
+ // `weak → 1`, with the old word preserved in `weight.label`.
7034
+ // - `story_task.estimate` / `.effort` / `.priority` drop. `story_task`
7035
+ // itself is deprecated (`UPG_MIGRATIONS['0.4.0']` renames it to the
7036
+ // canonical `task`); the type rename copies every property verbatim,
7037
+ // so the values survive on `task` (`TaskProperties.estimate` /
7038
+ // `.effort` / `.priority`, all live). This rule removes the residue
7039
+ // from any node still typed `story_task` at migration time.
7040
+ "0.8.0": [
7041
+ {
7042
+ kind: "drop_props",
7043
+ type: "evidence",
7044
+ drop_props: ["strength"],
7045
+ reason: "UPG-574 (v0.8.0). evidence.strength was `@deprecated since v0.4.0`; the structured `weight: UPGAssessment` (scale `weight_5pt`) replaces the `strong | moderate | weak` enum to align with the spec-wide scoring pattern. Authors should copy the value to `weight` before applying this migration (strong -> 5, moderate -> 3, weak -> 1; carry the old word in `weight.label`). The migration drops the residue."
7046
+ },
7047
+ {
7048
+ kind: "drop_props",
7049
+ type: "story_task",
7050
+ drop_props: ["estimate", "effort", "priority"],
7051
+ reason: 'UPG-574 (v0.8.0). story_task.estimate / .effort / .priority were `@deprecated since v0.4.0`; story_task itself is deprecated (UPG_MIGRATIONS["0.4.0"] renames it to canonical `task`, copying all properties verbatim), so the values survive on TaskProperties.estimate / .effort / .priority (all live). This rule removes the residue from any node still typed `story_task` at migration time.'
7052
+ }
7053
+ ],
6816
7054
  // ── v0.5.0: UPG-509 deprecation hygiene pass ───────────────────────────
6817
7055
  //
6818
7056
  // Properties tagged `@deprecated since="0.4.0" removeIn="0.5.0"` (and the
@@ -8616,7 +8854,18 @@ var UPG_PROPERTY_SCHEMA = {
8616
8854
  },
8617
8855
  // A11yIssueProperties: Accessibility issue.
8618
8856
  a11y_issue: {
8619
- severity: { type: "string", enum: ["minor", "moderate", "serious", "critical"], description: "Severity. Aligned to axe-core's 4-level impact scale (industry standard): 'minor' < 'moderate' < 'serious' < 'critical'. Migration: the previous 3-level scale ('critical' | 'major' | 'minor') maps to ('critical' | 'serious' | 'minor') respectively." },
8857
+ severity: {
8858
+ type: "assessment",
8859
+ scale_id: "severity_5",
8860
+ description: "Impact severity (UPGAssessment on the `severity_5` scale). Migrated from the inline axe-core 4-level enum (`minor|moderate|serious|critical`) (UPG-579 Option C): map `critical` -> 5, `serious` -> 4, `moderate` -> 3, `minor` -> 2; carry the old word in `label`.",
8861
+ properties: {
8862
+ value: { type: "number", description: "The numeric value, used for computation." },
8863
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
8864
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
8865
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
8866
+ },
8867
+ required: ["value", "label"]
8868
+ },
8620
8869
  wcag_criterion: { type: "string", description: 'WCAG success criterion violated (e.g. "1.1.1", "2.4.7")' },
8621
8870
  rule_id: { type: "string", description: "Identifier of the rule that flagged the issue" },
8622
8871
  help_url: { type: "string", description: "URL to documentation explaining the issue and how to fix it" },
@@ -8673,7 +8922,18 @@ var UPG_PROPERTY_SCHEMA = {
8673
8922
  affinity_cluster: {
8674
8923
  theme: { type: "string", description: "Emergent theme label" },
8675
8924
  child_observation_count: { type: "number", description: "Observations in this cluster" },
8676
- confidence: { type: "string", enum: ["high", "medium", "low"], description: "Confidence in the theme's validity" }
8925
+ confidence: {
8926
+ type: "assessment",
8927
+ scale_id: "confidence_5",
8928
+ description: "Confidence in the theme's validity (UPGAssessment on `confidence_5`).",
8929
+ properties: {
8930
+ value: { type: "number", description: "The numeric value, used for computation." },
8931
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
8932
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
8933
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
8934
+ },
8935
+ required: ["value", "label"]
8936
+ }
8677
8937
  },
8678
8938
  // AgentDefinitionProperties: Agent definition.
8679
8939
  agent_definition: {
@@ -8807,7 +9067,7 @@ var UPG_PROPERTY_SCHEMA = {
8807
9067
  // AlertRuleProperties: Alert rule.
8808
9068
  alert_rule: {
8809
9069
  condition: { type: "string", description: 'Triggering query or expression. @example "avg(rate(http_errors_total[5m])) > 0.05"' },
8810
- severity: { type: "string", enum: ["critical", "warning", "info"], description: "Routes notification and escalation. `critical` = page immediately. `warning` = notify, don't page. `info` = log only." },
9070
+ severity: { type: "string", enum: ["critical", "warning", "info"], description: "Routes notification and escalation. Uses the `LogLevel` scale (operational verbosity, distinct from user-impact `severity_5`). `critical` = page immediately. `warning` = notify, don't page. `info` = log only." },
8811
9071
  notification_channel: { type: "string", description: 'Notification destination. @example "pagerduty:sev1-rotation", "slack:#alerts-low"' },
8812
9072
  evaluation_window: { type: "string", description: 'Required duration the condition holds before firing. Prevents flapping on transient spikes. @example "5m", "15m", "1h"' },
8813
9073
  escalation_policy: { type: "string", description: 'Escalation behaviour on unacknowledged alerts. @example "Escalate to engineering lead after 10 minutes"' }
@@ -8846,16 +9106,28 @@ var UPG_PROPERTY_SCHEMA = {
8846
9106
  },
8847
9107
  // AssumptionProperties: Assumption entity.
8848
9108
  assumption: {
8849
- confidence: { type: "string", enum: ["high", "medium", "low"], description: "Confidence before testing. Independent of whether the assumption is validated (tracked in lifecycle)." },
9109
+ confidence: {
9110
+ type: "assessment",
9111
+ scale_id: "confidence_5",
9112
+ description: "Confidence before testing (UPGAssessment on `confidence_5`). Independent of whether the assumption is validated (tracked in lifecycle).",
9113
+ properties: {
9114
+ value: { type: "number", description: "The numeric value, used for computation." },
9115
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9116
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9117
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9118
+ },
9119
+ required: ["value", "label"]
9120
+ },
8850
9121
  validation_method: { type: "string", description: "Validation method, planned or used" },
8851
9122
  risk_level: {
8852
- type: "object",
9123
+ type: "assessment",
9124
+ scale_id: "severity_5",
8853
9125
  description: "Exposure if the assumption turns out wrong",
8854
9126
  properties: {
8855
9127
  value: { type: "number", description: "The numeric value, used for computation." },
8856
9128
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
8857
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
8858
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9129
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9130
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
8859
9131
  },
8860
9132
  required: ["value", "label"]
8861
9133
  },
@@ -8966,8 +9238,18 @@ var UPG_PROPERTY_SCHEMA = {
8966
9238
  },
8967
9239
  // BugProperties: Bug report.
8968
9240
  bug: {
8969
- bug_severity: { type: "string", enum: ["critical", "major", "minor", "trivial"], description: "Impact severity. Independent of priority (which governs when it gets fixed). `critical` = data loss or complete feature failure. `trivial` = cosmetic only." },
8970
- bug_status: { type: "string", enum: ["open", "in_progress", "fixed", "verified", "wont_fix"], description: "@deprecated since v0.4.0. Use top-level `UPGBaseNode.status`. Values map identically: open | in_progress | fixed | verified | wont_fix. Migration: `UPG_PROPERTY_MIGRATIONS['0.4.0']` lifts this to `status`." },
9241
+ bug_severity: {
9242
+ type: "assessment",
9243
+ scale_id: "severity_5",
9244
+ description: "Impact severity (UPGAssessment on the `severity_5` scale). Independent of priority (which governs when it gets fixed). Migrated from the inline `critical|major|minor|trivial` enum (UPG-579 Option C): map `critical` -> 5, `major` -> 4, `minor` -> 2, `trivial` -> 1; carry the old word in `label`.",
9245
+ properties: {
9246
+ value: { type: "number", description: "The numeric value, used for computation." },
9247
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9248
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9249
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9250
+ },
9251
+ required: ["value", "label"]
9252
+ },
8971
9253
  steps_to_reproduce: { type: "string", description: "Step-by-step reproduction" },
8972
9254
  environment: { type: "string", description: 'Observed environment (e.g. "prod", "staging", "iOS 17.4")' },
8973
9255
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Urgency relative to other work. Independent of `bug_severity` (a critical bug can have low priority if rare)." },
@@ -8997,7 +9279,7 @@ var UPG_PROPERTY_SCHEMA = {
8997
9279
  maturity_level: { type: "string", enum: ["initial", "developing", "defined", "managed", "optimizing"], description: "Current maturity" },
8998
9280
  target_maturity: { type: "string", enum: ["initial", "developing", "defined", "managed", "optimizing"], description: "Target maturity" },
8999
9281
  gap: { type: "string", description: "Gap between current and target" },
9000
- evolution_stage: { type: "string", enum: ["genesis", "custom", "product", "commodity"], description: 'Wardley evolution axis: where this capability sits on the genesis \u2192 custom \u2192 product \u2192 commodity spectrum. Used by frameworks like `wardley-map`; generally useful as a maturity-of-the-domain signal independent of `maturity_level` (which measures the team\'s internal capability practice). @example "product"' },
9282
+ evolution_stage: { type: "string", enum: ["genesis", "custom", "product", "commodity"], description: "Wardley evolution axis: where this capability sits on the genesis \u2192 custom \u2192 product \u2192 commodity spectrum. Used by frameworks like `wardley-map`; generally useful as a maturity-of-the-domain signal independent of `maturity_level` (which measures the team's internal capability practice). @example 'product'" },
9001
9283
  visibility: { type: "number", description: "Position on the visibility axis from `0.0` (deepest dependency, infra the user never sees) to `1.0` (user-visible anchor). Used by `wardley-map` for the y-axis position of each capability in the value chain. @example 0.8" }
9002
9284
  },
9003
9285
  // CapacityPlanProperties: CapacityPlan entity.
@@ -9010,7 +9292,7 @@ var UPG_PROPERTY_SCHEMA = {
9010
9292
  // CeremonyProperties: Ceremony entity.
9011
9293
  ceremony: {
9012
9294
  ceremony_type: { type: "string", enum: ["standup", "planning", "review", "retro", "sync", "demo", "other"], description: "Kind of recurring meeting" },
9013
- cadence: { type: "string", description: 'How often the ceremony occurs (e.g. "daily", "biweekly")' },
9295
+ cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "How often the ceremony occurs. Uses the shared `Cadence` scale." },
9014
9296
  duration_minutes: { type: "number", description: "Typical duration of the meeting in minutes" },
9015
9297
  participants: { type: "string", description: "People or roles who attend" }
9016
9298
  },
@@ -9039,9 +9321,9 @@ var UPG_PROPERTY_SCHEMA = {
9039
9321
  category: { type: "string", description: "High-level category of the churn reason" },
9040
9322
  frequency_count: { type: "number", description: "Exact count of times this reason has been cited in `frequency_period`" },
9041
9323
  frequency_period: { type: "string", description: "The recurrence period the count is measured over (ISO-8601 `Duration`, e.g. `'P30D'`)" },
9042
- frequency_rating: { type: "string", enum: ["rare", "occasional", "regular", "constant", "other"], description: "Qualitative frequency tier when an exact count is not known" },
9324
+ frequency_rating: { type: "string", enum: ["constant", "regular", "occasional", "rare", "other"], description: "Qualitative frequency tier when an exact count is not known" },
9043
9325
  contributing_factors: { type: "string[]", description: "Other factors that contributed to the churn" },
9044
- signal_sentiment: { type: "string", enum: ["positive", "negative", "neutral", "mixed"], description: "Detected sentiment of the churn signal" },
9326
+ signal_sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"], description: "Detected sentiment of the churn signal" },
9045
9327
  signal_channel: { type: "string", description: "Channel through which the churn signal was received" },
9046
9328
  signal_urgency: { type: "string", enum: ["low", "medium", "high", "critical"], description: "Urgency of the churn risk" }
9047
9329
  },
@@ -9155,7 +9437,7 @@ var UPG_PROPERTY_SCHEMA = {
9155
9437
  publish_cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "Publishing cadence (canonical `Cadence` since v0.4.0). Retyped from the legacy free-form `publish_cadence: string`. For exact rates (\"3 per week\"), set `frequency_count` + `frequency_period`. BREAKING in v0.4.0: previous string values like `\"3x/week\"` no longer type-check. Map to `'weekly'` + `frequency_count: 3` + `frequency_period: 'P7D'`." },
9156
9438
  frequency_count: { type: "number", description: "Exact count in the period. Pairs with `frequency_period`." },
9157
9439
  frequency_period: { type: "string", description: "Recurrence period (ISO-8601 `Duration`, e.g. `'P7D'`)" },
9158
- frequency_rating: { type: "string", enum: ["rare", "occasional", "regular", "constant", "other"], description: "Qualitative rate tier when an exact rate is unknown" }
9440
+ frequency_rating: { type: "string", enum: ["constant", "regular", "occasional", "rare", "other"], description: "Qualitative rate tier when an exact rate is unknown" }
9159
9441
  },
9160
9442
  // ContentPieceProperties: Content piece.
9161
9443
  content_piece: {
@@ -9170,7 +9452,7 @@ var UPG_PROPERTY_SCHEMA = {
9170
9452
  cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "Publishing cadence (canonical `Cadence` since v0.4.0). Retyped from the legacy free-form `cadence: string` (e.g. \"2x/week\"). For exact rates, set `frequency_count` + `frequency_period`. BREAKING in v0.4.0: previous string values like `\"2x/week\"` no longer type-check. Map to `'weekly'` + `frequency_count: 2` + `frequency_period: 'P7D'`." },
9171
9453
  frequency_count: { type: "number", description: "Exact count of publications in the period. Pairs with `frequency_period`." },
9172
9454
  frequency_period: { type: "string", description: "Recurrence period (ISO-8601 `Duration`, e.g. `'P7D'`)" },
9173
- frequency_rating: { type: "string", enum: ["rare", "occasional", "regular", "constant", "other"], description: "Qualitative rate tier when an exact rate is unknown" }
9455
+ frequency_rating: { type: "string", enum: ["constant", "regular", "occasional", "rare", "other"], description: "Qualitative rate tier when an exact rate is unknown" }
9174
9456
  },
9175
9457
  // ContentThemeProperties: Content theme.
9176
9458
  content_theme: {
@@ -9195,13 +9477,14 @@ var UPG_PROPERTY_SCHEMA = {
9195
9477
  clause_text: { type: "string", description: "Full text of the clause" },
9196
9478
  is_negotiable: { type: "boolean", description: "Whether this clause is open to negotiation" },
9197
9479
  risk_level: {
9198
- type: "object",
9480
+ type: "assessment",
9481
+ scale_id: "severity_5",
9199
9482
  description: "Risk level if the clause is accepted as-is (1 = negligible, 5 = severe exposure)",
9200
9483
  properties: {
9201
9484
  value: { type: "number", description: "The numeric value, used for computation." },
9202
9485
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9203
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9204
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9486
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9487
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9205
9488
  },
9206
9489
  required: ["value", "label"]
9207
9490
  }
@@ -9220,9 +9503,9 @@ var UPG_PROPERTY_SCHEMA = {
9220
9503
  // CustomerFeedbackProperties: Customer feedback.
9221
9504
  customer_feedback: {
9222
9505
  feedback_type: { type: "string", enum: ["survey", "interview", "review", "nps"], description: "How the feedback was collected" },
9223
- sentiment: { type: "string", enum: ["positive", "neutral", "negative"], description: "Overall sentiment of the feedback" },
9506
+ sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"], description: "Overall sentiment of the feedback" },
9224
9507
  verbatim: { type: "string", description: "Exact words from the customer" },
9225
- signal_sentiment: { type: "string", enum: ["positive", "negative", "neutral", "mixed"], description: "Detected sentiment of the underlying signal" },
9508
+ signal_sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"], description: "Detected sentiment of the underlying signal" },
9226
9509
  signal_channel: { type: "string", description: "Channel through which the signal was received" },
9227
9510
  signal_urgency: { type: "string", enum: ["low", "medium", "high", "critical"], description: "Perceived urgency of the feedback" }
9228
9511
  },
@@ -9251,7 +9534,7 @@ var UPG_PROPERTY_SCHEMA = {
9251
9534
  url: { type: "string", description: "URL to the live dashboard" },
9252
9535
  audience: { type: "string", description: "Intended audience for this dashboard" },
9253
9536
  element_count: { type: "number", description: "Number of widgets or panels on the dashboard" },
9254
- refresh_cadence: { type: "string", description: "How often the dashboard data refreshes" },
9537
+ refresh_cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "How often the dashboard data refreshes. Uses the shared `Cadence` scale." },
9255
9538
  filter_count: { type: "number", description: "Number of user-configurable filters" }
9256
9539
  },
9257
9540
  // DataClassificationProperties: Data classification.
@@ -9311,7 +9594,7 @@ var UPG_PROPERTY_SCHEMA = {
9311
9594
  },
9312
9595
  // DataProductProperties: DataProduct entity.
9313
9596
  data_product: {
9314
- data_product_type: { type: "string", description: 'Classification of the data product (e.g. "report", "dataset", "stream")' },
9597
+ data_product_type: { type: "string", enum: ["report", "dataset", "stream", "api", "ml_feature", "other"], description: "Classification of the data product (UPG-579 Option B)." },
9315
9598
  sla_freshness: { type: "string", description: 'Freshness SLA commitment (e.g. "< 1 hour")' }
9316
9599
  },
9317
9600
  // DataQualityRuleProperties: DataQualityRule entity.
@@ -9328,7 +9611,7 @@ var UPG_PROPERTY_SCHEMA = {
9328
9611
  data_source: {
9329
9612
  source_type: { type: "string", enum: ["database", "api", "event_stream", "warehouse"], description: "Kind of data source" },
9330
9613
  connection_status: { type: "string", enum: ["connected", "disconnected", "error"], description: "Current connection health" },
9331
- refresh_cadence: { type: "string", description: 'How often the data is refreshed (e.g. "hourly", "daily")' }
9614
+ refresh_cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "How often the data is refreshed. Uses the shared `Cadence` scale." }
9332
9615
  },
9333
9616
  // DatabaseSchemaProperties: Database schema.
9334
9617
  database_schema: {
@@ -9359,7 +9642,7 @@ var UPG_PROPERTY_SCHEMA = {
9359
9642
  },
9360
9643
  // DeliverableProperties: Deliverable.
9361
9644
  deliverable: {
9362
- deliverable_type: { type: "string", description: 'Kind of deliverable (e.g. "document", "prototype", "release")' },
9645
+ deliverable_type: { type: "string", enum: ["document", "prototype", "release", "design", "report", "other"], description: "Kind of deliverable (UPG-579 Option B)." },
9363
9646
  due_date: { type: "string", description: "Due date for the deliverable (ISO format)" },
9364
9647
  deliverable_status: { type: "string", enum: ["not_started", "in_progress", "in_review", "accepted", "rejected"], description: "Current progress status" },
9365
9648
  acceptance_criteria: { type: "string", description: "Criteria that must be met for the deliverable to be accepted" }
@@ -9437,7 +9720,18 @@ var UPG_PROPERTY_SCHEMA = {
9437
9720
  target_domain: { type: "string", enum: ["ux", "visual", "interaction", "content", "accessibility", "other"], description: "Target design discipline" },
9438
9721
  framing: { type: "string", enum: ["how_might_we", "what_if", "why_do", "how_do", "what_prevents", "other"], description: "Question framing template" },
9439
9722
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Importance against other backlog questions" },
9440
- confidence: { type: "string", enum: ["high", "medium", "low"], description: "Confidence the question is well-framed. Distinct from confidence in any answer." },
9723
+ confidence: {
9724
+ type: "assessment",
9725
+ scale_id: "confidence_5",
9726
+ description: "Confidence the question is well-framed (UPGAssessment on `confidence_5`). Distinct from confidence in any answer.",
9727
+ properties: {
9728
+ value: { type: "number", description: "The numeric value, used for computation." },
9729
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9730
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9731
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9732
+ },
9733
+ required: ["value", "label"]
9734
+ },
9441
9735
  assumptions: { type: "string[]", description: "Underlying assumptions. Surfaced explicitly so they can be challenged or validated." },
9442
9736
  validation_method: { type: "string", enum: ["interview", "survey", "usability_test", "analytics", "a_b_test", "prototype_test", "literature_review", "other"], description: "Primary validation method" }
9443
9737
  },
@@ -9464,24 +9758,26 @@ var UPG_PROPERTY_SCHEMA = {
9464
9758
  desired_outcome: {
9465
9759
  statement: { type: "string", description: "Outcome statement in the user's words" },
9466
9760
  importance: {
9467
- type: "object",
9761
+ type: "assessment",
9762
+ scale_id: "importance_5",
9468
9763
  description: "How important this outcome is to the user (1 = low, 5 = critical)",
9469
9764
  properties: {
9470
9765
  value: { type: "number", description: "The numeric value, used for computation." },
9471
9766
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9472
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9473
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9767
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9768
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9474
9769
  },
9475
9770
  required: ["value", "label"]
9476
9771
  },
9477
9772
  current_satisfaction: {
9478
- type: "object",
9773
+ type: "assessment",
9774
+ scale_id: "satisfaction_5",
9479
9775
  description: "How satisfied the user currently is with this outcome (1 = very unsatisfied, 5 = fully satisfied)",
9480
9776
  properties: {
9481
9777
  value: { type: "number", description: "The numeric value, used for computation." },
9482
9778
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9483
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9484
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9779
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9780
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9485
9781
  },
9486
9782
  required: ["value", "label"]
9487
9783
  }
@@ -9599,20 +9895,20 @@ var UPG_PROPERTY_SCHEMA = {
9599
9895
  evidence_source: { type: "string", enum: ["experiment_run", "observation", "quote", "metric_change", "market_data", "interview"], description: "Origin type. Drives renderer + filter UI; the provenance edge (`derived_from_*`) carries the actual source node reference." },
9600
9896
  direction: { type: "string", enum: ["supports", "refutes", "neutral"], description: "Direction relative to the parent hypothesis." },
9601
9897
  weight: {
9602
- type: "object",
9603
- description: "Strength (UPGAssessment, scale `weight_5pt`).",
9898
+ type: "assessment",
9899
+ scale_id: "importance_5",
9900
+ description: "Strength (UPGAssessment, scale `scale_5`).",
9604
9901
  properties: {
9605
9902
  value: { type: "number", description: "The numeric value, used for computation." },
9606
9903
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9607
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9608
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9904
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9905
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9609
9906
  },
9610
9907
  required: ["value", "label"]
9611
9908
  },
9612
9909
  summary: { type: "string", description: "Plain-English summary." },
9613
9910
  observed_at: { type: "string", description: "ISO date observed." },
9614
- source: { type: "string", description: "Free-text provenance note" },
9615
- strength: { type: "string", enum: ["strong", "moderate", "weak"], description: "@deprecated since v0.4.0. Use `weight: UPGAssessment`. Simple enum replaced by structured assessment to align with UPG scoring patterns." }
9911
+ source: { type: "string", description: "Free-text provenance note" }
9616
9912
  },
9617
9913
  // ExperimentProperties: A structured activity designed to test a hypothesis.
9618
9914
  experiment: {
@@ -9629,46 +9925,50 @@ var UPG_PROPERTY_SCHEMA = {
9629
9925
  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." },
9630
9926
  success_criteria: { type: "string", description: 'Plain-English description of "passing"' },
9631
9927
  projected_reach: {
9632
- type: "object",
9928
+ type: "assessment",
9929
+ scale_id: "reach_5",
9633
9930
  description: "Projected reach: how many people the run is expected to touch (UPGAssessment)",
9634
9931
  properties: {
9635
9932
  value: { type: "number", description: "The numeric value, used for computation." },
9636
9933
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9637
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9638
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9934
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9935
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9639
9936
  },
9640
9937
  required: ["value", "label"]
9641
9938
  },
9642
9939
  projected_impact: {
9643
- type: "object",
9940
+ type: "assessment",
9941
+ scale_id: "impact_5",
9644
9942
  description: "Projected impact on the target metric (UPGAssessment)",
9645
9943
  properties: {
9646
9944
  value: { type: "number", description: "The numeric value, used for computation." },
9647
9945
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9648
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9649
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9946
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9947
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9650
9948
  },
9651
9949
  required: ["value", "label"]
9652
9950
  },
9653
9951
  confidence: {
9654
- type: "object",
9655
- description: "Team confidence at plan-time (UPGAssessment, scale `confidence_5pt`)",
9952
+ type: "assessment",
9953
+ scale_id: "confidence_5",
9954
+ description: "Team confidence at plan-time (UPGAssessment, scale `confidence_5`)",
9656
9955
  properties: {
9657
9956
  value: { type: "number", description: "The numeric value, used for computation." },
9658
9957
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9659
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9660
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9958
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9959
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9661
9960
  },
9662
9961
  required: ["value", "label"]
9663
9962
  },
9664
9963
  cost_estimate: {
9665
- type: "object",
9964
+ type: "assessment",
9965
+ scale_id: "effort_5",
9666
9966
  description: "Cost estimate at plan-time (UPGAssessment)",
9667
9967
  properties: {
9668
9968
  value: { type: "number", description: "The numeric value, used for computation." },
9669
9969
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9670
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9671
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9970
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9971
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9672
9972
  },
9673
9973
  required: ["value", "label"]
9674
9974
  },
@@ -9682,13 +9982,14 @@ var UPG_PROPERTY_SCHEMA = {
9682
9982
  actual_reach: { type: "number", description: "Observed reach: how many people the run actually touched" },
9683
9983
  outcome_summary: { type: "string", description: "Plain-English outcome" },
9684
9984
  severity_of_finding: {
9685
- type: "object",
9985
+ type: "assessment",
9986
+ scale_id: "severity_5",
9686
9987
  description: "Severity / strength of the finding (UPGAssessment)",
9687
9988
  properties: {
9688
9989
  value: { type: "number", description: "The numeric value, used for computation." },
9689
9990
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9690
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9691
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
9991
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
9992
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9692
9993
  },
9693
9994
  required: ["value", "label"]
9694
9995
  },
@@ -9707,7 +10008,18 @@ var UPG_PROPERTY_SCHEMA = {
9707
10008
  feasibility_study: {
9708
10009
  study_type: { type: "string", enum: ["technical", "business", "market", "resource"], description: 'Type of feasibility being assessed. @example "technical" for assessing engineering viability' },
9709
10010
  conclusion: { type: "string", enum: ["feasible", "not_feasible", "conditional", "needs_more_data"], description: "Outcome conclusion of the study" },
9710
- confidence: { type: "string", enum: ["high", "medium", "low"], description: "Confidence in the conclusion" }
10011
+ confidence: {
10012
+ type: "assessment",
10013
+ scale_id: "confidence_5",
10014
+ description: "Confidence in the conclusion (UPGAssessment on `confidence_5`).",
10015
+ properties: {
10016
+ value: { type: "number", description: "The numeric value, used for computation." },
10017
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10018
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10019
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10020
+ },
10021
+ required: ["value", "label"]
10022
+ }
9711
10023
  },
9712
10024
  // FeatureProperties: A discrete, user-facing capability of the product.
9713
10025
  feature: {
@@ -9742,28 +10054,30 @@ var UPG_PROPERTY_SCHEMA = {
9742
10054
  feature_request: {
9743
10055
  request_source: { type: "string", enum: ["customer", "internal", "prospect", "support", "community"], description: "Where the request originated" },
9744
10056
  vote_count: { type: "number", description: "Number of votes or upvotes from users" },
9745
- signal_sentiment: { type: "string", enum: ["positive", "negative", "neutral", "mixed"], description: "Detected sentiment of the request" },
10057
+ signal_sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"], description: "Detected sentiment of the request" },
9746
10058
  signal_channel: { type: "string", description: "Channel through which the request was received" },
9747
10059
  signal_urgency: { type: "string", enum: ["low", "medium", "high", "critical"], description: "Perceived urgency of the request" },
9748
10060
  revenue_impact: {
9749
- type: "object",
10061
+ type: "assessment",
10062
+ scale_id: "impact_5",
9750
10063
  description: "Estimated revenue impact if implemented (1-5)",
9751
10064
  properties: {
9752
10065
  value: { type: "number", description: "The numeric value, used for computation." },
9753
10066
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9754
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9755
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10067
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10068
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9756
10069
  },
9757
10070
  required: ["value", "label"]
9758
10071
  },
9759
10072
  effort_estimate: {
9760
- type: "object",
10073
+ type: "assessment",
10074
+ scale_id: "effort_5",
9761
10075
  description: "Estimated implementation effort (1-5)",
9762
10076
  properties: {
9763
10077
  value: { type: "number", description: "The numeric value, used for computation." },
9764
10078
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9765
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9766
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10079
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10080
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9767
10081
  },
9768
10082
  required: ["value", "label"]
9769
10083
  },
@@ -9780,13 +10094,14 @@ var UPG_PROPERTY_SCHEMA = {
9780
10094
  sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"], description: "Overall sentiment across mentions" },
9781
10095
  actionable: { type: "boolean", description: "Whether this theme can be acted upon" },
9782
10096
  frequency: {
9783
- type: "object",
10097
+ type: "assessment",
10098
+ scale_id: "frequency_5",
9784
10099
  description: "How often the theme is mentioned (1-5)",
9785
10100
  properties: {
9786
10101
  value: { type: "number", description: "The numeric value, used for computation." },
9787
10102
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9788
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9789
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10103
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10104
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9790
10105
  },
9791
10106
  required: ["value", "label"]
9792
10107
  },
@@ -9804,7 +10119,7 @@ var UPG_PROPERTY_SCHEMA = {
9804
10119
  fix_type: { type: "string", enum: ["hotfix", "permanent", "workaround", "configuration", "process_change"], description: "Kind of fix. `hotfix` = urgent patch. `permanent` = proper structural fix. `workaround` = mitigates symptom without addressing root cause; should track a follow-up." },
9805
10120
  commit: { type: "string", description: "Git commit SHA" },
9806
10121
  files_changed: { type: "string[]", description: "Files changed" },
9807
- deployed_at: { type: "string", description: "ISO timestamp landed in the target environment. Pairs with `fix_status === 'deployed'`." },
10122
+ deployed_at: { type: "string", description: "ISO timestamp landed in the target environment." },
9808
10123
  fixed_date: { type: "string", description: "Application date (ISO date). Coarser-grained complement to `deployed_at`." },
9809
10124
  verified: { type: "boolean", description: "Verified in production" },
9810
10125
  verified_by_test: { type: "boolean", description: "Validated by an automated regression or integration test. Distinct from `verified` (human judgment)." }
@@ -9814,13 +10129,14 @@ var UPG_PROPERTY_SCHEMA = {
9814
10129
  forecast_period: { type: "string", description: 'Time period the forecast covers (e.g. "Q2 2026")' },
9815
10130
  predicted_revenue: { type: "number", description: "Predicted revenue amount" },
9816
10131
  confidence: {
9817
- type: "object",
10132
+ type: "assessment",
10133
+ scale_id: "confidence_5",
9818
10134
  description: "Confidence in the prediction (1 = speculative, 5 = high conviction)",
9819
10135
  properties: {
9820
10136
  value: { type: "number", description: "The numeric value, used for computation." },
9821
10137
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9822
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9823
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10138
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10139
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9824
10140
  },
9825
10141
  required: ["value", "label"]
9826
10142
  },
@@ -9872,13 +10188,14 @@ var UPG_PROPERTY_SCHEMA = {
9872
10188
  hallucination_report: {
9873
10189
  report_type: { type: "string", enum: ["factual", "logical", "fabrication", "inconsistency"], description: "Classification" },
9874
10190
  severity: {
9875
- type: "object",
10191
+ type: "assessment",
10192
+ scale_id: "severity_5",
9876
10193
  description: "Impact severity (1 = trivial, 5 = dangerous misinformation)",
9877
10194
  properties: {
9878
10195
  value: { type: "number", description: "The numeric value, used for computation." },
9879
10196
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9880
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9881
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10197
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10198
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9882
10199
  },
9883
10200
  required: ["value", "label"]
9884
10201
  },
@@ -9900,13 +10217,14 @@ var UPG_PROPERTY_SCHEMA = {
9900
10217
  we_know_when: { type: "string", description: "The measurable signal that confirms or refutes the claim." },
9901
10218
  risk_if_wrong: { type: "string", description: "Risk surface for prioritisation if the claim turns out wrong." },
9902
10219
  current_confidence: {
9903
- type: "object",
10220
+ type: "assessment",
10221
+ scale_id: "confidence_5",
9904
10222
  description: "Team confidence at the current point in time. Derived from the weighted sum of attached `hypothesis_evidence` rows (formula spec'd separately). Authors may set explicitly; loaders may overwrite from derivation.",
9905
10223
  properties: {
9906
10224
  value: { type: "number", description: "The numeric value, used for computation." },
9907
10225
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9908
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9909
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10226
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10227
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9910
10228
  },
9911
10229
  required: ["value", "label"]
9912
10230
  }
@@ -9916,13 +10234,14 @@ var UPG_PROPERTY_SCHEMA = {
9916
10234
  evidence_type: { type: "string", enum: ["experiment_run", "observation", "quote", "metric_change", "market_data", "interview"], description: "Kind of evidence. Drives renderer + filter UI. The provenance edge (`derived_from_*`) carries the actual source node reference per P14; this enum is typing/UI metadata." },
9917
10235
  direction: { type: "string", enum: ["supports", "refutes", "neutral"], description: "Direction relative to the parent claim. Canonical direction axis aligned to `Evidence.direction` so every direction-of-evidence property uses the same vocab. `supports` = paired with the `supports` edge. `refutes` = paired with the `refutes` edge. `neutral` = data insufficient or noisy. BREAKING in v0.4.0: legacy `'confirms'`, `'disconfirms'`, `'inconclusive'` no longer type-check. Migration: `confirms \u2192 supports`, `disconfirms \u2192 refutes`, `inconclusive \u2192 neutral`." },
9918
10236
  weight: {
9919
- type: "object",
9920
- description: "Strength of the evidence (UPGAssessment, scale `weight_5pt`).",
10237
+ type: "assessment",
10238
+ scale_id: "importance_5",
10239
+ description: "Strength of the evidence (UPGAssessment, scale `scale_5`).",
9921
10240
  properties: {
9922
10241
  value: { type: "number", description: "The numeric value, used for computation." },
9923
10242
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
9924
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
9925
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10243
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10244
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
9926
10245
  },
9927
10246
  required: ["value", "label"]
9928
10247
  },
@@ -9940,8 +10259,8 @@ var UPG_PROPERTY_SCHEMA = {
9940
10259
  // IncidentProperties: Incident.
9941
10260
  incident: {
9942
10261
  incident_type: { type: "string", enum: ["operational", "security", "data_breach", "performance", "dependency", "other"], description: 'Discriminator. Absorbs the deprecated `security_incident` type. When `incident_type === \'security\'`, this node replaces the former `security_incident`. @example "security" for a data breach, "operational" for a service outage, "performance" for degradation' },
9943
- severity_level: { type: "string", enum: ["sev1", "sev2", "sev3", "sev4"], description: 'Severity. `sev1` = critical/system down. `sev2` = major impact. `sev3` = minor impact. `sev4` = minimal impact. @example "sev1" for complete service unavailability' },
9944
- urgency: { type: "string", enum: ["high", "low"], description: "Notification urgency. Independent of severity. `high` triggers phone calls. `low` sends a message." },
10262
+ severity_level: { type: "string", enum: ["sev1", "sev2", "sev3", "sev4"], description: 'Incident severity tier (paging classification). `sev1` = critical/system down. `sev2` = major impact. `sev3` = minor impact. `sev4` = minimal. Uses the `IncidentSeverity` scale; distinct from user-impact `severity_5`. @example "sev1" for complete service unavailability' },
10263
+ urgency: { type: "string", enum: ["low", "medium", "high", "critical"], description: "Notification urgency. Independent of severity. Uses the shared `SignalUrgency` scale (`low` | `medium` | `high` | `critical`); higher tiers escalate the notification channel." },
9945
10264
  started_at: { type: "string", description: "ISO timestamp the incident started or was first detected." },
9946
10265
  acknowledged_at: { type: "string", description: "ISO timestamp first acknowledged by a responder. Used to compute time-to-acknowledge." },
9947
10266
  contained_at: { type: "string", description: "ISO timestamp contained. Blast radius limited, bleeding stopped. Containment precedes full resolution, especially for security incidents." },
@@ -9967,7 +10286,18 @@ var UPG_PROPERTY_SCHEMA = {
9967
10286
  // InsightProperties: Unified insight, synthesised from evidence and observations.
9968
10287
  insight: {
9969
10288
  insight_level: { type: "string", enum: ["pattern", "finding", "actionable", "strategic"], description: "Maturity level. `pattern` = recurring observation, not yet interpreted. `finding` = interpreted pattern with a clear meaning. `actionable` = finding with a clear next step. `strategic` = finding that affects product direction." },
9970
- confidence: { type: "string", enum: ["high", "medium", "low"], description: "Confidence. Reflects the strength and diversity of supporting evidence." },
10289
+ confidence: {
10290
+ type: "assessment",
10291
+ scale_id: "confidence_5",
10292
+ description: "Confidence (UPGAssessment on `confidence_5`). Reflects the strength and diversity of supporting evidence.",
10293
+ properties: {
10294
+ value: { type: "number", description: "The numeric value, used for computation." },
10295
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10296
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10297
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10298
+ },
10299
+ required: ["value", "label"]
10300
+ },
9971
10301
  evidence_count: { type: "number", description: "Supporting observations, quotes, or evidence items. Higher counts increase confidence." },
9972
10302
  novelty: { type: "string", enum: ["known", "surprising", "contradictory"], description: "Novelty against existing knowledge. `known` = confirms what we already believed. `surprising` = challenges or extends our understanding. `contradictory` = directly conflicts with a prior assumption." },
9973
10303
  actionability: { type: "string", enum: ["immediate", "needs_validation", "informational"], description: "Current actionability. `immediate` = clear action, no further research needed. `needs_validation` = promising but requires more evidence. `informational` = important context, no direct action." },
@@ -10005,20 +10335,21 @@ var UPG_PROPERTY_SCHEMA = {
10005
10335
  // InvestigationProperties: Active thread of inquiry: debugging, architecture exploration, RCA.
10006
10336
  investigation: {
10007
10337
  severity: {
10008
- type: "object",
10338
+ type: "assessment",
10339
+ scale_id: "severity_5",
10009
10340
  description: "Believed severity of the underlying issue. Drives prioritisation across investigations. Canonicalised in v0.4.0: the ad-hoc `'low' | 'medium' | 'high' | 'critical'` shape was replaced by `UPGAssessment` so every \"severity\" property reports on the same axis. Migration: `low \u2192 2`, `medium \u2192 3`, `high \u2192 4`, `critical \u2192 5`.",
10010
10341
  properties: {
10011
10342
  value: { type: "number", description: "The numeric value, used for computation." },
10012
10343
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10013
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10014
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10344
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10345
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10015
10346
  },
10016
10347
  required: ["value", "label"]
10017
10348
  },
10018
10349
  hypothesis: { type: "string", description: "Working hypothesis about the root cause" },
10019
10350
  findings: { type: "string", description: "Findings discovered so far" },
10020
10351
  started_at: { type: "string", description: "ISO timestamp the investigation began" },
10021
- resolved_at: { type: "string", description: "ISO timestamp the investigation was concluded. Pairs with `investigation_status === 'resolved' | 'abandoned'`." },
10352
+ resolved_at: { type: "string", description: "ISO timestamp the investigation was concluded. Pairs with `status === 'resolved' | 'abandoned'`." },
10022
10353
  lead_investigator: { type: "string", description: "Lead investigator (email or handle). Distinct from the team owning the affected service." },
10023
10354
  session_id: { type: "string", description: "Originating session" },
10024
10355
  category: { type: "string", enum: ["performance", "security", "data_quality", "reliability", "cost", "compliance", "other"], description: "Kind of issue under investigation. Distinct from `RootCauseProperties.cause_category`, which captures *why something went wrong*." }
@@ -10044,24 +10375,26 @@ var UPG_PROPERTY_SCHEMA = {
10044
10375
  statement: { type: "string", description: 'Job statement: "When I... I want to... So I can..."' },
10045
10376
  job_type: { type: "string", enum: ["functional", "emotional", "social", "supporting"], description: "Classification by motivation dimension" },
10046
10377
  importance: {
10047
- type: "object",
10378
+ type: "assessment",
10379
+ scale_id: "importance_5",
10048
10380
  description: "Importance to the user (1 = low, 5 = critical)",
10049
10381
  properties: {
10050
10382
  value: { type: "number", description: "The numeric value, used for computation." },
10051
10383
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10052
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10053
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10384
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10385
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10054
10386
  },
10055
10387
  required: ["value", "label"]
10056
10388
  },
10057
10389
  current_satisfaction: {
10058
- type: "object",
10390
+ type: "assessment",
10391
+ scale_id: "satisfaction_5",
10059
10392
  description: "Current satisfaction with how this job gets done (1 = very unsatisfied, 5 = fully satisfied)",
10060
10393
  properties: {
10061
10394
  value: { type: "number", description: "The numeric value, used for computation." },
10062
10395
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10063
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10064
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10396
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10397
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10065
10398
  },
10066
10399
  required: ["value", "label"]
10067
10400
  },
@@ -10079,24 +10412,26 @@ var UPG_PROPERTY_SCHEMA = {
10079
10412
  action_description: { type: "string", description: "Plain-language description. Primary content of the action." },
10080
10413
  channel: { type: "string", enum: ["in-app", "email", "web", "mobile", "phone", "in-person", "sms", "social", "other"], description: "Channel or surface. Keeps service-blueprint columns consistent across the journey." },
10081
10414
  pain_score: {
10082
- type: "object",
10415
+ type: "assessment",
10416
+ scale_id: "pain_5",
10083
10417
  description: "Pain (1 = effortless, 5 = very painful). Drives opportunity discovery.",
10084
10418
  properties: {
10085
10419
  value: { type: "number", description: "The numeric value, used for computation." },
10086
10420
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10087
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10088
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10421
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10422
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10089
10423
  },
10090
10424
  required: ["value", "label"]
10091
10425
  },
10092
10426
  opportunity_score: {
10093
- type: "object",
10427
+ type: "assessment",
10428
+ scale_id: "impact_5",
10094
10429
  description: "Opportunity (1 = low leverage, 5 = high leverage). Pairs with `pain_score` to rank investment.",
10095
10430
  properties: {
10096
10431
  value: { type: "number", description: "The numeric value, used for computation." },
10097
10432
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10098
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10099
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10433
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10434
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10100
10435
  },
10101
10436
  required: ["value", "label"]
10102
10437
  },
@@ -10120,24 +10455,26 @@ var UPG_PROPERTY_SCHEMA = {
10120
10455
  touchpoint: { type: "string", description: "Interaction touchpoint" },
10121
10456
  channel: { type: "string", description: 'Channel (e.g. "web", "email", "in-store")' },
10122
10457
  emotion_score: {
10123
- type: "object",
10458
+ type: "assessment",
10459
+ scale_id: "satisfaction_5",
10124
10460
  description: "User emotion (1 = very negative, 5 = very positive)",
10125
10461
  properties: {
10126
10462
  value: { type: "number", description: "The numeric value, used for computation." },
10127
10463
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10128
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10129
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10464
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10465
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10130
10466
  },
10131
10467
  required: ["value", "label"]
10132
10468
  },
10133
10469
  friction_score: {
10134
- type: "object",
10470
+ type: "assessment",
10471
+ scale_id: "pain_5",
10135
10472
  description: "Friction (1 = effortless, 5 = very painful)",
10136
10473
  properties: {
10137
10474
  value: { type: "number", description: "The numeric value, used for computation." },
10138
10475
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10139
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10140
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10476
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10477
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10141
10478
  },
10142
10479
  required: ["value", "label"]
10143
10480
  },
@@ -10150,7 +10487,7 @@ var UPG_PROPERTY_SCHEMA = {
10150
10487
  cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: 'Canonical `Cadence`. Replaces the legacy free-form `frequency: string` in v0.4.0. For exact rates (e.g. "3 times per week") set `frequency_count` + `frequency_period`. For qualitative tiers ("rare" \u2192 "constant") use `frequency_rating`.' },
10151
10488
  frequency_count: { type: "number", description: "Exact count of runs in the period. Pairs with `frequency_period`." },
10152
10489
  frequency_period: { type: "string", description: "Recurrence period (ISO-8601 `Duration`, e.g. `'P7D'`)" },
10153
- frequency_rating: { type: "string", enum: ["rare", "occasional", "regular", "constant", "other"], description: "Qualitative tier. Use when an exact rate is unknown." },
10490
+ frequency_rating: { type: "string", enum: ["constant", "regular", "occasional", "rare", "other"], description: "Qualitative tier. Use when an exact rate is unknown." },
10154
10491
  operational_owner: { type: "string", description: "Operationally accountable team or individual" },
10155
10492
  capacity_constraint: { type: "string", description: "Bottleneck or scaling constraint" },
10156
10493
  automation_level: { type: "string", enum: ["manual", "assisted", "automated"], description: "How much runs without human intervention" }
@@ -10161,13 +10498,14 @@ var UPG_PROPERTY_SCHEMA = {
10161
10498
  criticality: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Criticality to the business model" },
10162
10499
  owner: { type: "string", description: "Accountable person or team" },
10163
10500
  scarcity_risk: {
10164
- type: "object",
10501
+ type: "assessment",
10502
+ scale_id: "severity_5",
10165
10503
  description: "Replacement difficulty",
10166
10504
  properties: {
10167
10505
  value: { type: "number", description: "The numeric value, used for computation." },
10168
10506
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10169
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10170
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10507
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10508
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10171
10509
  },
10172
10510
  required: ["value", "label"]
10173
10511
  },
@@ -10201,7 +10539,6 @@ var UPG_PROPERTY_SCHEMA = {
10201
10539
  // LearningProperties: Result of an experiment. Evidence that updates a hypothesis.
10202
10540
  learning: {
10203
10541
  result: { type: "string", description: "Summary" },
10204
- metric: { type: "string", description: 'Observed metric. @deprecated since="0.4.0" removeIn="0.5.0". Use the canonical edge `learning_observed_on_metric` to link to the metric structurally. Free-text was the wrong shape: a metric is a queryable graph entity.' },
10205
10542
  result_value: { type: "number", description: "Measured value of the result" },
10206
10543
  result_unit: { type: "string", description: 'Unit (e.g. "%" or "ms")' },
10207
10544
  result_direction: { type: "string", enum: ["supports", "refutes", "neutral"], description: "Direction relative to the parent hypothesis. Canonical direction axis. BREAKING in v0.4.0: legacy `'positive'`, `'negative'`, `'neutral'` are replaced by `'supports'`, `'refutes'`, `'neutral'` to align with `Evidence.direction` and `HypothesisEvidence.direction`. Migration: `positive \u2192 supports`, `negative \u2192 refutes`, `neutral \u2192 neutral`." },
@@ -10257,25 +10594,27 @@ var UPG_PROPERTY_SCHEMA = {
10257
10594
  // MarketTrendProperties: Market trend.
10258
10595
  market_trend: {
10259
10596
  relevance: {
10260
- type: "object",
10597
+ type: "assessment",
10598
+ scale_id: "importance_5",
10261
10599
  description: "Relevance to our product (1 = low, 5 = critical).",
10262
10600
  properties: {
10263
10601
  value: { type: "number", description: "The numeric value, used for computation." },
10264
10602
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10265
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10266
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10603
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10604
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10267
10605
  },
10268
10606
  required: ["value", "label"]
10269
10607
  },
10270
10608
  timeframe: { type: "string", description: 'Expected peak or mainstream window. @example "12-18 months", "2027"' },
10271
10609
  impact: {
10272
- type: "object",
10610
+ type: "assessment",
10611
+ scale_id: "impact_5",
10273
10612
  description: "Expected impact on our market or category.",
10274
10613
  properties: {
10275
10614
  value: { type: "number", description: "The numeric value, used for computation." },
10276
10615
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10277
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10278
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10616
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10617
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10279
10618
  },
10280
10619
  required: ["value", "label"]
10281
10620
  },
@@ -10386,35 +10725,38 @@ var UPG_PROPERTY_SCHEMA = {
10386
10725
  valence: { type: "string", enum: ["pain", "gap", "constraint"], description: "What kind of experience: pain (friction), gap (unmet), constraint (limitation)" },
10387
10726
  maturity: { type: "string", enum: ["raw", "validated", "prioritized"], description: "How mature is this need in our understanding" },
10388
10727
  frequency: {
10389
- type: "object",
10728
+ type: "assessment",
10729
+ scale_id: "frequency_5",
10390
10730
  description: "How often the user encounters this need (1 = rarely, 5 = constantly)",
10391
10731
  properties: {
10392
10732
  value: { type: "number", description: "The numeric value, used for computation." },
10393
10733
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10394
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10395
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10734
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10735
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10396
10736
  },
10397
10737
  required: ["value", "label"]
10398
10738
  },
10399
10739
  severity: {
10400
- type: "object",
10740
+ type: "assessment",
10741
+ scale_id: "severity_5",
10401
10742
  description: "How painful or disruptive the need is when unaddressed (1 = minor, 5 = critical)",
10402
10743
  properties: {
10403
10744
  value: { type: "number", description: "The numeric value, used for computation." },
10404
10745
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10405
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10406
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10746
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10747
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10407
10748
  },
10408
10749
  required: ["value", "label"]
10409
10750
  },
10410
10751
  importance: {
10411
- type: "object",
10752
+ type: "assessment",
10753
+ scale_id: "importance_5",
10412
10754
  description: "How important resolving this need is to the user (1 = low, 5 = critical)",
10413
10755
  properties: {
10414
10756
  value: { type: "number", description: "The numeric value, used for computation." },
10415
10757
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10416
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10417
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10758
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10759
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10418
10760
  },
10419
10761
  required: ["value", "label"]
10420
10762
  },
@@ -10435,13 +10777,14 @@ var UPG_PROPERTY_SCHEMA = {
10435
10777
  statement: { type: "string", description: "The objection as stated by the source" },
10436
10778
  source_type: { type: "string", enum: ["prospect", "competitor", "internal", "market"], description: "Where this objection originated" },
10437
10779
  severity: {
10438
- type: "object",
10780
+ type: "assessment",
10781
+ scale_id: "severity_5",
10439
10782
  description: "How frequently or strongly this objection comes up (1-5)",
10440
10783
  properties: {
10441
10784
  value: { type: "number", description: "The numeric value, used for computation." },
10442
10785
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10443
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10444
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10786
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10787
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10445
10788
  },
10446
10789
  required: ["value", "label"]
10447
10790
  },
@@ -10460,7 +10803,7 @@ var UPG_PROPERTY_SCHEMA = {
10460
10803
  session_ref: { type: "string", description: "Capturing session reference. Convenience field; the canonical relationship to study/session is an edge per P14. Retained as a lightweight context anchor for AI inference." },
10461
10804
  is_highlighted: { type: "boolean", description: "Flagged as a highlight. Absorbed from the deprecated Highlight entity." },
10462
10805
  highlight_tag: { type: "string", description: 'Free-form highlight type tag. @example "pain", "delight", "behaviour", "moment of clarity"' },
10463
- sentiment: { type: "string", enum: ["positive", "negative", "neutral", "mixed"], description: "Structured sentiment. Tools like Dovetail and EnjoyHQ converge on these four values. More precise than `highlight_tag` for aggregation." }
10806
+ sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"], description: "Structured sentiment. Tools like Dovetail and EnjoyHQ converge on these four values. More precise than `highlight_tag` for aggregation." }
10464
10807
  },
10465
10808
  // OnCallRotationProperties: On-call rotation.
10466
10809
  on_call_rotation: {
@@ -10472,35 +10815,38 @@ var UPG_PROPERTY_SCHEMA = {
10472
10815
  // OpportunityProperties: A problem worth solving, grounded in user need and business value
10473
10816
  opportunity: {
10474
10817
  reach: {
10475
- type: "object",
10818
+ type: "assessment",
10819
+ scale_id: "reach_5",
10476
10820
  description: "How many users experience this problem",
10477
10821
  properties: {
10478
10822
  value: { type: "number", description: "The numeric value, used for computation." },
10479
10823
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10480
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10481
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10824
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10825
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10482
10826
  },
10483
10827
  required: ["value", "label"]
10484
10828
  },
10485
10829
  frequency: {
10486
- type: "object",
10830
+ type: "assessment",
10831
+ scale_id: "frequency_5",
10487
10832
  description: "How often users experience this problem",
10488
10833
  properties: {
10489
10834
  value: { type: "number", description: "The numeric value, used for computation." },
10490
10835
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10491
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10492
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10836
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10837
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10493
10838
  },
10494
10839
  required: ["value", "label"]
10495
10840
  },
10496
10841
  pain: {
10497
- type: "object",
10842
+ type: "assessment",
10843
+ scale_id: "pain_5",
10498
10844
  description: "How painful it is when unaddressed",
10499
10845
  properties: {
10500
10846
  value: { type: "number", description: "The numeric value, used for computation." },
10501
10847
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10502
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10503
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10848
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10849
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10504
10850
  },
10505
10851
  required: ["value", "label"]
10506
10852
  },
@@ -10521,17 +10867,18 @@ var UPG_PROPERTY_SCHEMA = {
10521
10867
  current_state: { type: "string", description: "Baseline or latest read" },
10522
10868
  evidence_summary: { type: "string", description: "Evidence gathered so far (quotes, metrics, studies)" },
10523
10869
  confidence: {
10524
- type: "object",
10870
+ type: "assessment",
10871
+ scale_id: "confidence_5",
10525
10872
  description: "Confidence this is the right outcome to pursue",
10526
10873
  properties: {
10527
10874
  value: { type: "number", description: "The numeric value, used for computation." },
10528
10875
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10529
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10530
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10876
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10877
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10531
10878
  },
10532
10879
  required: ["value", "label"]
10533
10880
  },
10534
- outcome_status: { type: "string", description: "Current lifecycle phase. Free-form until an outcome lifecycle is defined in `grammar/lifecycles.ts`." }
10881
+ outcome_status: { type: "string", enum: ["identified", "measuring", "achieved", "abandoned"], description: "Current lifecycle phase, mirroring the `outcome` lifecycle in `grammar/lifecycles.ts` (UPG-579 Option B): `identified` -> `measuring` -> `achieved` | `abandoned`." }
10535
10882
  },
10536
10883
  // ParticipantProperties: Research participant.
10537
10884
  participant: {
@@ -10562,13 +10909,14 @@ var UPG_PROPERTY_SCHEMA = {
10562
10909
  value_exchange: { type: "string", description: "What each party gives and receives" },
10563
10910
  partnership_tier: { type: "string", enum: ["strategic", "preferred", "standard", "trial"], description: "Commercial significance. Drives attention and exec sponsorship." },
10564
10911
  risk_level: {
10565
- type: "object",
10912
+ type: "assessment",
10913
+ scale_id: "severity_5",
10566
10914
  description: "Exposure if the partnership fails (concentration risk, IP risk, etc.)",
10567
10915
  properties: {
10568
10916
  value: { type: "number", description: "The numeric value, used for computation." },
10569
10917
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10570
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10571
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
10918
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
10919
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10572
10920
  },
10573
10921
  required: ["value", "label"]
10574
10922
  },
@@ -10609,7 +10957,7 @@ var UPG_PROPERTY_SCHEMA = {
10609
10957
  },
10610
10958
  // PipelineSalesProperties: Sales pipeline.
10611
10959
  pipeline_sales: {
10612
- pipeline_type: { type: "string", description: 'Classification of the pipeline (e.g. "new business", "expansion")' },
10960
+ pipeline_type: { type: "string", enum: ["new_business", "expansion", "renewal", "partner", "other"], description: "Classification of the pipeline (UPG-579 Option B)." },
10613
10961
  avg_cycle_days: { type: "number", description: "Average days from opportunity creation to close" }
10614
10962
  },
10615
10963
  // PipelineStageProperties: Pipeline stage.
@@ -10651,7 +10999,7 @@ var UPG_PROPERTY_SCHEMA = {
10651
10999
  // PricingStrategyProperties: Pricing strategy.
10652
11000
  pricing_strategy: {
10653
11001
  strategy_type: { type: "string", enum: ["value_based", "cost_plus", "competitor_based", "penetration", "freemium"], description: "Pricing methodology used" },
10654
- review_cadence: { type: "string", description: "How often pricing is reviewed" },
11002
+ review_cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "How often pricing is reviewed. Uses the shared `Cadence` scale." },
10655
11003
  last_change: { type: "string", description: "Date of the last pricing change (ISO format)" }
10656
11004
  },
10657
11005
  // PricingTierProperties: PricingTier: the central pricing concept (the plan a customer buys).
@@ -10760,13 +11108,14 @@ var UPG_PROPERTY_SCHEMA = {
10760
11108
  rebuttal: {
10761
11109
  statement: { type: "string", description: "The counter-argument or response to an objection" },
10762
11110
  strength: {
10763
- type: "object",
11111
+ type: "assessment",
11112
+ scale_id: "scale_5",
10764
11113
  description: "How convincing this rebuttal is (1-5)",
10765
11114
  properties: {
10766
11115
  value: { type: "number", description: "The numeric value, used for computation." },
10767
11116
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10768
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10769
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11117
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11118
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10770
11119
  },
10771
11120
  required: ["value", "label"]
10772
11121
  },
@@ -10859,24 +11208,26 @@ var UPG_PROPERTY_SCHEMA = {
10859
11208
  risk: {
10860
11209
  risk_type: { type: "string", enum: ["technical", "business", "legal", "security", "operational"], description: "Domain the risk belongs to" },
10861
11210
  probability: {
10862
- type: "object",
11211
+ type: "assessment",
11212
+ scale_id: "confidence_5",
10863
11213
  description: "How likely this risk is to materialise (1 = unlikely, 5 = near certain)",
10864
11214
  properties: {
10865
11215
  value: { type: "number", description: "The numeric value, used for computation." },
10866
11216
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10867
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10868
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11217
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11218
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10869
11219
  },
10870
11220
  required: ["value", "label"]
10871
11221
  },
10872
11222
  impact: {
10873
- type: "object",
11223
+ type: "assessment",
11224
+ scale_id: "impact_5",
10874
11225
  description: "Severity of consequences if the risk materialises (1 = negligible, 5 = catastrophic)",
10875
11226
  properties: {
10876
11227
  value: { type: "number", description: "The numeric value, used for computation." },
10877
11228
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10878
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10879
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11229
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11230
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10880
11231
  },
10881
11232
  required: ["value", "label"]
10882
11233
  },
@@ -10897,7 +11248,18 @@ var UPG_PROPERTY_SCHEMA = {
10897
11248
  quarter: { type: "string", description: 'Planning quarter (e.g. "Q2 2026"). Pair with `start_date`/`target_date` for precise scheduling.' },
10898
11249
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Importance against other items" },
10899
11250
  item_status: { type: "string", enum: ["planned", "in_progress", "shipped", "deferred"], description: "Status. `deferred` = explicitly pushed to a later period." },
10900
- confidence: { type: "string", enum: ["high", "medium", "low"], description: "Delivery confidence within the planned period." },
11251
+ confidence: {
11252
+ type: "assessment",
11253
+ scale_id: "confidence_5",
11254
+ description: "Delivery confidence within the planned period (UPGAssessment on `confidence_5`).",
11255
+ properties: {
11256
+ value: { type: "number", description: "The numeric value, used for computation." },
11257
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11258
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11259
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11260
+ },
11261
+ required: ["value", "label"]
11262
+ },
10901
11263
  start_date: { type: "string", description: "ISO date work begins. More precise than `quarter` for continuous planning." },
10902
11264
  target_date: { type: "string", description: "ISO date completion is expected. For shipped items, the actual completion date." }
10903
11265
  },
@@ -10911,13 +11273,14 @@ var UPG_PROPERTY_SCHEMA = {
10911
11273
  // RootCauseProperties: Underlying architectural or systemic issue.
10912
11274
  root_cause: {
10913
11275
  severity: {
10914
- type: "object",
11276
+ type: "assessment",
11277
+ scale_id: "severity_5",
10915
11278
  description: "Severity (1 = minor, 5 = critical)",
10916
11279
  properties: {
10917
11280
  value: { type: "number", description: "The numeric value, used for computation." },
10918
11281
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10919
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10920
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11282
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11283
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10921
11284
  },
10922
11285
  required: ["value", "label"]
10923
11286
  },
@@ -10967,13 +11330,14 @@ var UPG_PROPERTY_SCHEMA = {
10967
11330
  security_control: {
10968
11331
  control_type: { type: "string", enum: ["preventive", "detective", "corrective", "compensating"], description: "Functional role. `preventive` = stops attacks (MFA, input validation). `detective` = identifies attacks in progress (intrusion detection, audit logs). `corrective` = reduces impact after an attack (incident response, backup restore). `compensating` = alternative when primary isn't feasible." },
10969
11332
  effectiveness: {
10970
- type: "object",
11333
+ type: "assessment",
11334
+ scale_id: "impact_5",
10971
11335
  description: "Mitigation effectiveness (1 = minimal, 5 = fully effective)",
10972
11336
  properties: {
10973
11337
  value: { type: "number", description: "The numeric value, used for computation." },
10974
11338
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
10975
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
10976
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11339
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11340
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
10977
11341
  },
10978
11342
  required: ["value", "label"]
10979
11343
  },
@@ -10984,7 +11348,7 @@ var UPG_PROPERTY_SCHEMA = {
10984
11348
  // SecurityPolicyProperties: Security policy.
10985
11349
  security_policy: {
10986
11350
  scope: { type: "string", description: "Systems or processes covered" },
10987
- review_cadence: { type: "string", description: 'Review cadence (e.g. "annually", "quarterly")' },
11351
+ review_cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "Review cadence (e.g. `yearly`, `quarterly`). Uses the shared `Cadence` scale." },
10988
11352
  version: { type: "string", description: "Version" },
10989
11353
  effective_date: { type: "string", description: "ISO effective date" },
10990
11354
  url: { type: "string", description: "Policy document URL" },
@@ -11055,17 +11419,18 @@ var UPG_PROPERTY_SCHEMA = {
11055
11419
  },
11056
11420
  // SkillProperties: Skill entity.
11057
11421
  skill: {
11058
- skill_category: { type: "string", description: 'Category of the skill (e.g. "technical", "leadership", "design")' },
11422
+ skill_category: { type: "string", enum: ["technical", "leadership", "design", "product", "business", "operations", "other"], description: "Category of the skill (UPG-579 Option B)." },
11059
11423
  proficiency_levels: { type: "string[]", description: "Description of proficiency levels for this skill" },
11060
11424
  domain: { type: "string", description: 'Problem domain the skill applies to (e.g. "payments", "accessibility")' },
11061
11425
  rarity: {
11062
- type: "object",
11426
+ type: "assessment",
11427
+ scale_id: "scale_5",
11063
11428
  description: "How scarce this skill is in the labour market this team hires from",
11064
11429
  properties: {
11065
11430
  value: { type: "number", description: "The numeric value, used for computation." },
11066
11431
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11067
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11068
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11432
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11433
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11069
11434
  },
11070
11435
  required: ["value", "label"]
11071
11436
  },
@@ -11081,46 +11446,50 @@ var UPG_PROPERTY_SCHEMA = {
11081
11446
  solution: {
11082
11447
  timeline: { type: "string", description: "Estimated delivery or target timeline" },
11083
11448
  reach: {
11084
- type: "object",
11449
+ type: "assessment",
11450
+ scale_id: "reach_5",
11085
11451
  description: "How many users this solution reaches (1 = few, 5 = most)",
11086
11452
  properties: {
11087
11453
  value: { type: "number", description: "The numeric value, used for computation." },
11088
11454
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11089
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11090
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11455
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11456
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11091
11457
  },
11092
11458
  required: ["value", "label"]
11093
11459
  },
11094
11460
  impact: {
11095
- type: "object",
11461
+ type: "assessment",
11462
+ scale_id: "impact_5",
11096
11463
  description: "Expected impact on the target outcome (1 = minimal, 5 = transformative)",
11097
11464
  properties: {
11098
11465
  value: { type: "number", description: "The numeric value, used for computation." },
11099
11466
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11100
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11101
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11467
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11468
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11102
11469
  },
11103
11470
  required: ["value", "label"]
11104
11471
  },
11105
11472
  confidence: {
11106
- type: "object",
11473
+ type: "assessment",
11474
+ scale_id: "confidence_5",
11107
11475
  description: "How confident the team is in this solution (1 = speculative, 5 = proven)",
11108
11476
  properties: {
11109
11477
  value: { type: "number", description: "The numeric value, used for computation." },
11110
11478
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11111
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11112
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11479
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11480
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11113
11481
  },
11114
11482
  required: ["value", "label"]
11115
11483
  },
11116
11484
  effort: {
11117
- type: "object",
11485
+ type: "assessment",
11486
+ scale_id: "effort_5",
11118
11487
  description: "Level of effort required to implement (1 = trivial, 5 = very large)",
11119
11488
  properties: {
11120
11489
  value: { type: "number", description: "The numeric value, used for computation." },
11121
11490
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11122
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11123
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11491
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11492
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11124
11493
  },
11125
11494
  required: ["value", "label"]
11126
11495
  },
@@ -11130,24 +11499,26 @@ var UPG_PROPERTY_SCHEMA = {
11130
11499
  stakeholder: {
11131
11500
  stakeholder_type: { type: "string", enum: ["internal", "external", "investor", "regulator"], description: "Relationship of the stakeholder to the organisation" },
11132
11501
  influence: {
11133
- type: "object",
11502
+ type: "assessment",
11503
+ scale_id: "importance_5",
11134
11504
  description: "How much influence this stakeholder has over decisions (1 = minimal, 5 = decisive)",
11135
11505
  properties: {
11136
11506
  value: { type: "number", description: "The numeric value, used for computation." },
11137
11507
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11138
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11139
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11508
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11509
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11140
11510
  },
11141
11511
  required: ["value", "label"]
11142
11512
  },
11143
11513
  interest: {
11144
- type: "object",
11514
+ type: "assessment",
11515
+ scale_id: "importance_5",
11145
11516
  description: "How much interest this stakeholder has in the outcome (1 = passive, 5 = deeply invested)",
11146
11517
  properties: {
11147
11518
  value: { type: "number", description: "The numeric value, used for computation." },
11148
11519
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11149
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11150
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11520
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11521
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11151
11522
  },
11152
11523
  required: ["value", "label"]
11153
11524
  }
@@ -11159,19 +11530,6 @@ var UPG_PROPERTY_SCHEMA = {
11159
11530
  risks_flagged: { type: "number", description: "Number of risks flagged in this report" },
11160
11531
  blockers: { type: "string", description: "Description of current blockers" }
11161
11532
  },
11162
- // UserStoryProperties: "As X, I want Y so Z" templated promise, a user story (UCS pattern P5).
11163
- user_story: {
11164
- as_a: { type: "string", description: '"As a [persona], \u2026". Free-text persona name or slug ref.' },
11165
- i_want_to: { type: "string", description: "Capability or action the persona wants." },
11166
- so_that: { type: "string", description: "Benefit or outcome the persona expects." },
11167
- text: { type: "string", description: "Free-form story text. Used as a single-line rendered view." }
11168
- },
11169
- // StoryTaskProperties
11170
- story_task: {
11171
- estimate: { type: "string", description: "@deprecated Use TaskProperties.estimate" },
11172
- effort: { type: "number", description: "@deprecated Use TaskProperties.effort (string form)" },
11173
- priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "@deprecated Use TaskProperties.priority" }
11174
- },
11175
11533
  // StrategicPillarProperties: StrategicPillar entity. Durable multi-year direction the product commits to.
11176
11534
  strategic_pillar: {
11177
11535
  owner: { type: "string", description: "Owning person or team" },
@@ -11205,19 +11563,20 @@ var UPG_PROPERTY_SCHEMA = {
11205
11563
  support_ticket: {
11206
11564
  ticket_type: { type: "string", enum: ["bug", "question", "feature_request"], description: "Classification of the ticket" },
11207
11565
  severity: {
11208
- type: "object",
11566
+ type: "assessment",
11567
+ scale_id: "severity_5",
11209
11568
  description: "Impact severity of the issue (1 = cosmetic, 5 = service down)",
11210
11569
  properties: {
11211
11570
  value: { type: "number", description: "The numeric value, used for computation." },
11212
11571
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11213
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11214
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11572
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11573
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11215
11574
  },
11216
11575
  required: ["value", "label"]
11217
11576
  },
11218
11577
  resolution: { type: "string", description: "Description of how the ticket was resolved" },
11219
11578
  source: { type: "string", description: 'Where the ticket originated (e.g. "email", "in-app", "chat")' },
11220
- signal_sentiment: { type: "string", enum: ["positive", "negative", "neutral", "mixed"], description: "Detected sentiment of the customer's message" },
11579
+ signal_sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"], description: "Detected sentiment of the customer's message" },
11221
11580
  signal_channel: { type: "string", description: "Channel through which the signal was received" },
11222
11581
  signal_urgency: { type: "string", enum: ["low", "medium", "high", "critical"], description: "Perceived urgency of the customer's request" }
11223
11582
  },
@@ -11238,19 +11597,20 @@ var UPG_PROPERTY_SCHEMA = {
11238
11597
  symptom_description: { type: "string", description: "Plain-language description of observed behaviour. Primary content of the entity." },
11239
11598
  first_observed_at: { type: "string", description: "ISO timestamp first observed in the wild. Pairs with `frequency_rating` and `reproducibility` for triage." },
11240
11599
  severity: {
11241
- type: "object",
11600
+ type: "assessment",
11601
+ scale_id: "severity_5",
11242
11602
  description: "Severity for affected users. Independent of how widespread the symptom is. Canonicalised in v0.4.0: the ad-hoc `'low' | 'medium' | 'high' | 'critical'` shape was replaced by `UPGAssessment`.",
11243
11603
  properties: {
11244
11604
  value: { type: "number", description: "The numeric value, used for computation." },
11245
11605
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11246
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11247
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11606
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11607
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11248
11608
  },
11249
11609
  required: ["value", "label"]
11250
11610
  },
11251
11611
  frequency_count: { type: "number", description: "Exact observation count in the period. Pairs with `frequency_period` for a precise rate." },
11252
11612
  frequency_period: { type: "string", description: "Recurrence period (ISO-8601 `Duration`). @example 'P7D' (per week), 'P1D' (per day), 'PT1H' (per hour)" },
11253
- frequency_rating: { type: "string", enum: ["rare", "occasional", "regular", "constant", "other"], description: "Qualitative frequency tier. Canonical replacement for the legacy `'once' | 'sporadic' | 'frequent' | 'constant' | string` shape. Use when an exact rate is unknown. Migration: `once \u2192 rare`, `sporadic \u2192 occasional`, `frequent \u2192 regular`, `constant \u2192 constant`." },
11613
+ frequency_rating: { type: "string", enum: ["constant", "regular", "occasional", "rare", "other"], description: "Qualitative frequency tier. Canonical replacement for the legacy `'once' | 'sporadic' | 'frequent' | 'constant' | string` shape. Use when an exact rate is unknown. Migration: `once \u2192 rare`, `sporadic \u2192 occasional`, `frequent \u2192 regular`, `constant \u2192 constant`." },
11254
11614
  affected_users_estimate: { type: "number", description: "Approximate count of users affected. Snapshot estimate." },
11255
11615
  reproducibility: { type: "string", enum: ["always", "frequent", "intermittent", "rare", "once"], description: "Reproduction reliability" },
11256
11616
  steps_to_reproduce: { type: "string", description: "Steps to reproduce" }
@@ -11263,7 +11623,6 @@ var UPG_PROPERTY_SCHEMA = {
11263
11623
  },
11264
11624
  // TaskProperties: Task: a discrete unit of work, smaller than a story.
11265
11625
  task: {
11266
- task_status: { type: "string", enum: ["todo", "in_progress", "in_review", "done"], description: "@deprecated since v0.4.0. Use top-level `UPGBaseNode.status`. Values map identically: todo | in_progress | in_review | done. Migration: `UPG_PROPERTY_MIGRATIONS['0.4.0']` lifts this to `status`." },
11267
11626
  assignee: { type: "string", description: "Assigned person" },
11268
11627
  effort: { type: "string", description: 'Effort estimate (e.g. "2h", "1d", "3 points"). Use a consistent unit within your team.' },
11269
11628
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Relative importance against other tasks" },
@@ -11288,24 +11647,26 @@ var UPG_PROPERTY_SCHEMA = {
11288
11647
  technical_debt_item: {
11289
11648
  debt_type: { type: "string", enum: ["code", "architecture", "security", "test", "docs", "dependency"], description: "Type of debt. `code` = quality issues. `architecture` = structural problems. `security` = unpatched vulnerabilities. `test` = missing/flaky tests. `docs` = missing or stale documentation. `dependency` = outdated packages." },
11290
11649
  severity: {
11291
- type: "object",
11650
+ type: "assessment",
11651
+ scale_id: "severity_5",
11292
11652
  description: "Severity on system or team. Requires human evaluation.",
11293
11653
  properties: {
11294
11654
  value: { type: "number", description: "The numeric value, used for computation." },
11295
11655
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11296
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11297
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11656
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11657
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11298
11658
  },
11299
11659
  required: ["value", "label"]
11300
11660
  },
11301
11661
  effort_to_fix: {
11302
- type: "object",
11662
+ type: "assessment",
11663
+ scale_id: "effort_5",
11303
11664
  description: "Estimated effort to resolve. Requires team knowledge of the codebase.",
11304
11665
  properties: {
11305
11666
  value: { type: "number", description: "The numeric value, used for computation." },
11306
11667
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11307
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11308
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11668
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11669
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11309
11670
  },
11310
11671
  required: ["value", "label"]
11311
11672
  },
@@ -11388,24 +11749,26 @@ var UPG_PROPERTY_SCHEMA = {
11388
11749
  threat: {
11389
11750
  category: { type: "string", description: 'Attack or threat scenario. @example "injection", "misconfiguration", "social engineering", "supply chain"' },
11390
11751
  likelihood: {
11391
- type: "object",
11752
+ type: "assessment",
11753
+ scale_id: "confidence_5",
11392
11754
  description: "Likelihood (1 = theoretical, 5 = actively exploited). @example value 5 for a known, exploitable, commonly targeted pattern",
11393
11755
  properties: {
11394
11756
  value: { type: "number", description: "The numeric value, used for computation." },
11395
11757
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11396
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11397
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11758
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11759
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11398
11760
  },
11399
11761
  required: ["value", "label"]
11400
11762
  },
11401
11763
  impact: {
11402
- type: "object",
11764
+ type: "assessment",
11765
+ scale_id: "impact_5",
11403
11766
  description: "Impact (1 = minimal, 5 = catastrophic). @example value 5 for threats that expose PII or cause complete service compromise",
11404
11767
  properties: {
11405
11768
  value: { type: "number", description: "The numeric value, used for computation." },
11406
11769
  label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11407
- scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)" },
11408
- normalized: { type: "number", description: "Normalized 0\u20131 value for cross-tool comparison (optional)" }
11770
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11771
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11409
11772
  },
11410
11773
  required: ["value", "label"]
11411
11774
  },
@@ -11463,7 +11826,7 @@ var UPG_PROPERTY_SCHEMA = {
11463
11826
  // UserAdvisoryBoardProperties: User advisory board.
11464
11827
  user_advisory_board: {
11465
11828
  member_count: { type: "number", description: "Number of members on the board" },
11466
- meeting_cadence: { type: "string", description: 'How often the board meets (e.g. "monthly", "quarterly")' },
11829
+ meeting_cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "How often the board meets. Uses the shared `Cadence` scale." },
11467
11830
  board_focus: { type: "string", description: "Primary topic or area the board advises on" }
11468
11831
  },
11469
11832
  // UserFlowProperties: User flow.
@@ -11479,6 +11842,13 @@ var UPG_PROPERTY_SCHEMA = {
11479
11842
  journey_type: { type: "string", enum: ["current_state", "future_state", "day_in_the_life", "service_blueprint"], description: "Maps current or future state" },
11480
11843
  scenario: { type: "string", description: "Scenario context" }
11481
11844
  },
11845
+ // UserStoryProperties: "As X, I want Y so Z" templated promise on a user story (UCS pattern P5).
11846
+ user_story: {
11847
+ as_a: { type: "string", description: '"As a [persona], \u2026". Free-text persona name or slug ref.' },
11848
+ i_want_to: { type: "string", description: "Capability or action the persona wants." },
11849
+ so_that: { type: "string", description: "Benefit or outcome the persona expects." },
11850
+ text: { type: "string", description: "Free-form story text. Used as a single-line rendered view." }
11851
+ },
11482
11852
  // ValueObjectProperties: DDD value object.
11483
11853
  value_object: {
11484
11854
  immutable: { type: "boolean", description: "Whether immutable" },
@@ -11493,7 +11863,7 @@ var UPG_PROPERTY_SCHEMA = {
11493
11863
  },
11494
11864
  // ValueStreamProperties: ValueStream entity.
11495
11865
  value_stream: {
11496
- stream_stage: { type: "string", description: "Current stage in the value delivery pipeline" },
11866
+ stream_stage: { type: "string", enum: ["discovery", "definition", "build", "delivery", "operation", "other"], description: "Current stage in the value delivery pipeline (UPG-579 Option B)." },
11497
11867
  lead_time: { type: "string", description: 'End-to-end lead time. @example "2 weeks"' },
11498
11868
  throughput: { type: "string", description: 'Throughput measure. @example "5 features/sprint"' }
11499
11869
  },
@@ -11513,7 +11883,18 @@ var UPG_PROPERTY_SCHEMA = {
11513
11883
  vulnerability: {
11514
11884
  cve_id: { type: "string", description: 'CVE identifier from the National Vulnerability Database. @example "CVE-2024-1234"' },
11515
11885
  cvss_score: { type: "number", description: "CVSS numeric score (0.0\u201310.0). Computed from the CVSS vector. Distinct from the categorical `severity`. @example 9.8 (critical), 6.5 (medium), 3.7 (low)" },
11516
- severity: { type: "string", enum: ["critical", "high", "medium", "low", "informational"], description: 'Categorical severity derived from CVSS. `critical` = 9.0\u201310.0. `high` = 7.0\u20138.9. `medium` = 4.0\u20136.9. `low` = 0.1\u20133.9. `informational` = 0.0. Score alone is insufficient for triage; severity drives filtering and prioritisation. @example "critical" for a remotely exploitable, no-auth-required vulnerability' },
11886
+ severity: {
11887
+ type: "assessment",
11888
+ scale_id: "severity_5",
11889
+ description: "Categorical severity derived from CVSS. Impact severity (UPGAssessment on the `severity_5` scale). Score alone is insufficient for triage; severity drives filtering and prioritisation. Migrated from the inline `critical|high|medium|low|informational` enum (UPG-579 Option C): map `critical` -> 5, `high` -> 4, `medium` -> 3, `low` -> 2, `informational` -> 1; carry the old word in `label`. @example value 5, label 'Critical', scale_id 'severity_5' (a remotely exploitable, no-auth vuln)",
11890
+ properties: {
11891
+ value: { type: "number", description: "The numeric value, used for computation." },
11892
+ label: { type: "string", description: "The qualitative label (what the assessor meant)." },
11893
+ scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
11894
+ normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
11895
+ },
11896
+ required: ["value", "label"]
11897
+ },
11517
11898
  cvss_version: { type: "string", enum: ["v3.1", "v4.0"], description: 'CVSS scoring version. v3.1 and v4.0 differ significantly for the same vulnerability. @example "v4.0" for vulnerabilities scored after the v4.0 release in 2023' },
11518
11899
  affected_component: { type: "string", description: 'Affected component, library, or system. @example "lodash", "openssl", "login service"' },
11519
11900
  affected_package: { type: "string", description: 'Affected package with version. More precise than `affected_component`. @example "lodash@4.17.20", "openssl@1.1.1q"' },
@@ -12821,26 +13202,6 @@ var UPG_FRAMEWORKS = [
12821
13202
  "label": "Items to score",
12822
13203
  "entityTypeId": "feature",
12823
13204
  "description": "Features, opportunities, or solutions being evaluated"
12824
- },
12825
- {
12826
- "label": "Reach",
12827
- "entityTypeId": "feature",
12828
- "description": "How many users does this affect per quarter?"
12829
- },
12830
- {
12831
- "label": "Impact",
12832
- "entityTypeId": "feature",
12833
- "description": "How much does this move the target outcome?"
12834
- },
12835
- {
12836
- "label": "Confidence",
12837
- "entityTypeId": "feature",
12838
- "description": "How sure are we about these estimates?"
12839
- },
12840
- {
12841
- "label": "Effort",
12842
- "entityTypeId": "feature",
12843
- "description": "How many person-months to build?"
12844
13205
  }
12845
13206
  ],
12846
13207
  "data": {
@@ -12854,31 +13215,35 @@ var UPG_FRAMEWORKS = [
12854
13215
  "feature": [
12855
13216
  {
12856
13217
  "property": "reach",
12857
- "type": "number",
13218
+ "type": "assessment",
13219
+ "scale_id": "reach_5",
12858
13220
  "required": true,
12859
13221
  "label": "Reach",
12860
13222
  "description": "How many users will this impact per quarter?"
12861
13223
  },
12862
13224
  {
12863
13225
  "property": "impact",
12864
- "type": "number",
13226
+ "type": "assessment",
13227
+ "scale_id": "impact_5",
12865
13228
  "required": true,
12866
13229
  "label": "Impact",
12867
- "description": "How much will this impact each user? (1=minimal, 2=low, 3=medium, 4=high, 5=massive)"
13230
+ "description": "How much will this impact each user, on the impact scale?"
12868
13231
  },
12869
13232
  {
12870
13233
  "property": "confidence",
12871
- "type": "number",
13234
+ "type": "assessment",
13235
+ "scale_id": "confidence_5",
12872
13236
  "required": true,
12873
13237
  "label": "Confidence",
12874
- "description": "How confident are you in the estimates? (0.5=low, 0.8=medium, 1.0=high)"
13238
+ "description": "How confident are you in the reach, impact, and effort estimates?"
12875
13239
  },
12876
13240
  {
12877
13241
  "property": "effort",
12878
- "type": "number",
13242
+ "type": "assessment",
13243
+ "scale_id": "effort_5",
12879
13244
  "required": true,
12880
13245
  "label": "Effort",
12881
- "description": "Person-months of effort required"
13246
+ "description": "How much work is required to build and ship this, on the effort scale?"
12882
13247
  }
12883
13248
  ]
12884
13249
  },
@@ -13072,7 +13437,7 @@ var UPG_FRAMEWORKS = [
13072
13437
  {
13073
13438
  "label": "Performance",
13074
13439
  "entityTypeId": "feature",
13075
- "description": "More is better, with linear satisfaction increase"
13440
+ "description": "More is better: linear satisfaction increase"
13076
13441
  },
13077
13442
  {
13078
13443
  "label": "Delighters",
@@ -13321,24 +13686,9 @@ var UPG_FRAMEWORKS = [
13321
13686
  ],
13322
13687
  "slots": [
13323
13688
  {
13324
- "label": "Must Have",
13325
- "entityTypeId": "feature",
13326
- "description": "Non-negotiable requirements for this release"
13327
- },
13328
- {
13329
- "label": "Should Have",
13330
- "entityTypeId": "feature",
13331
- "description": "Important but not critical; a workaround exists"
13332
- },
13333
- {
13334
- "label": "Could Have",
13689
+ "label": "Requirements to categorise",
13335
13690
  "entityTypeId": "feature",
13336
- "description": "Nice to have; include if time permits"
13337
- },
13338
- {
13339
- "label": "Won't Have",
13340
- "entityTypeId": "feature",
13341
- "description": "Explicitly out of scope for now"
13691
+ "description": "Features or requirements sorted into the four MoSCoW buckets"
13342
13692
  }
13343
13693
  ],
13344
13694
  "data": {
@@ -13348,7 +13698,23 @@ var UPG_FRAMEWORKS = [
13348
13698
  "role": "scored_item"
13349
13699
  }
13350
13700
  ],
13351
- "required_properties": {},
13701
+ "required_properties": {
13702
+ "feature": [
13703
+ {
13704
+ "property": "moscow",
13705
+ "type": "enum",
13706
+ "required": true,
13707
+ "label": "MoSCoW priority",
13708
+ "description": "Which scope bucket this requirement falls into for the current release",
13709
+ "enum_values": [
13710
+ "must",
13711
+ "should",
13712
+ "could",
13713
+ "wont"
13714
+ ]
13715
+ }
13716
+ ]
13717
+ },
13352
13718
  "computed_properties": [
13353
13719
  {
13354
13720
  "property": "must_have_ratio",
@@ -13368,28 +13734,19 @@ var UPG_FRAMEWORKS = [
13368
13734
  "columns": [
13369
13735
  {
13370
13736
  "property": "title",
13371
- "label": "Must Have",
13737
+ "label": "Requirement",
13372
13738
  "sortable": true
13373
13739
  },
13374
13740
  {
13375
- "property": "title",
13376
- "label": "Should Have",
13377
- "sortable": true
13378
- },
13379
- {
13380
- "property": "title",
13381
- "label": "Could Have",
13382
- "sortable": true
13383
- },
13384
- {
13385
- "property": "title",
13386
- "label": "Won't Have",
13387
- "sortable": true
13741
+ "property": "moscow",
13742
+ "label": "Priority",
13743
+ "sortable": true,
13744
+ "format": "badge"
13388
13745
  }
13389
13746
  ]
13390
13747
  },
13391
13748
  "sort_by": {
13392
- "property": "title",
13749
+ "property": "moscow",
13393
13750
  "direction": "asc"
13394
13751
  },
13395
13752
  "colour_by": "type",
@@ -13940,24 +14297,9 @@ var UPG_FRAMEWORKS = [
13940
14297
  ],
13941
14298
  "slots": [
13942
14299
  {
13943
- "label": "Deployment Frequency",
14300
+ "label": "Delivery metric",
13944
14301
  "entityTypeId": "metric",
13945
- "description": "How often you deploy to production"
13946
- },
13947
- {
13948
- "label": "Lead Time for Changes",
13949
- "entityTypeId": "metric",
13950
- "description": "Time from commit to production"
13951
- },
13952
- {
13953
- "label": "Change Failure Rate",
13954
- "entityTypeId": "metric",
13955
- "description": "Percentage of deployments causing failures"
13956
- },
13957
- {
13958
- "label": "Time to Restore",
13959
- "entityTypeId": "metric",
13960
- "description": "How long to recover from a failure"
14302
+ "description": "One of the four DORA software-delivery performance metrics"
13961
14303
  }
13962
14304
  ],
13963
14305
  "data": {
@@ -13967,7 +14309,36 @@ var UPG_FRAMEWORKS = [
13967
14309
  "role": "item"
13968
14310
  }
13969
14311
  ],
13970
- "required_properties": {},
14312
+ "required_properties": {
14313
+ "metric": [
14314
+ {
14315
+ "property": "dora_metric",
14316
+ "type": "enum",
14317
+ "required": true,
14318
+ "label": "DORA metric",
14319
+ "description": "Which of the four DORA metrics this measures",
14320
+ "enum_values": [
14321
+ "deployment_frequency",
14322
+ "lead_time_for_changes",
14323
+ "change_failure_rate",
14324
+ "time_to_restore"
14325
+ ]
14326
+ },
14327
+ {
14328
+ "property": "performance_tier",
14329
+ "type": "enum",
14330
+ "required": false,
14331
+ "label": "Performance tier",
14332
+ "description": "Where this metric sits on the DORA elite-to-low benchmark",
14333
+ "enum_values": [
14334
+ "elite",
14335
+ "high",
14336
+ "medium",
14337
+ "low"
14338
+ ]
14339
+ }
14340
+ ]
14341
+ },
13971
14342
  "computed_properties": [
13972
14343
  {
13973
14344
  "property": "stability_index",
@@ -14123,29 +14494,9 @@ var UPG_FRAMEWORKS = [
14123
14494
  ],
14124
14495
  "slots": [
14125
14496
  {
14126
- "label": "Acquisition",
14127
- "entityTypeId": "metric",
14128
- "description": "How do users find you?"
14129
- },
14130
- {
14131
- "label": "Activation",
14132
- "entityTypeId": "metric",
14133
- "description": "Do users have a great first experience?"
14134
- },
14135
- {
14136
- "label": "Retention",
14137
- "entityTypeId": "metric",
14138
- "description": "Do users come back?"
14139
- },
14140
- {
14141
- "label": "Revenue",
14497
+ "label": "Lifecycle metric",
14142
14498
  "entityTypeId": "metric",
14143
- "description": "Can you monetise the behaviour?"
14144
- },
14145
- {
14146
- "label": "Referral",
14147
- "entityTypeId": "metric",
14148
- "description": "Do users tell others?"
14499
+ "description": "A metric tracking one stage of the customer lifecycle funnel"
14149
14500
  }
14150
14501
  ],
14151
14502
  "data": {
@@ -14155,7 +14506,24 @@ var UPG_FRAMEWORKS = [
14155
14506
  "role": "item"
14156
14507
  }
14157
14508
  ],
14158
- "required_properties": {},
14509
+ "required_properties": {
14510
+ "metric": [
14511
+ {
14512
+ "property": "lifecycle_stage",
14513
+ "type": "enum",
14514
+ "required": true,
14515
+ "label": "Lifecycle stage",
14516
+ "description": "Which AARRR funnel stage this metric measures",
14517
+ "enum_values": [
14518
+ "acquisition",
14519
+ "activation",
14520
+ "retention",
14521
+ "revenue",
14522
+ "referral"
14523
+ ]
14524
+ }
14525
+ ]
14526
+ },
14159
14527
  "computed_properties": [
14160
14528
  {
14161
14529
  "property": "conversion_rate",
@@ -14167,7 +14535,39 @@ var UPG_FRAMEWORKS = [
14167
14535
  ]
14168
14536
  },
14169
14537
  "structure": {
14170
- "pattern": "funnel"
14538
+ "pattern": "funnel",
14539
+ "stages": [
14540
+ {
14541
+ "id": "acquisition",
14542
+ "label": "Acquisition",
14543
+ "order": 0,
14544
+ "entity_type": "metric"
14545
+ },
14546
+ {
14547
+ "id": "activation",
14548
+ "label": "Activation",
14549
+ "order": 1,
14550
+ "entity_type": "metric"
14551
+ },
14552
+ {
14553
+ "id": "retention",
14554
+ "label": "Retention",
14555
+ "order": 2,
14556
+ "entity_type": "metric"
14557
+ },
14558
+ {
14559
+ "id": "revenue",
14560
+ "label": "Revenue",
14561
+ "order": 3,
14562
+ "entity_type": "metric"
14563
+ },
14564
+ {
14565
+ "id": "referral",
14566
+ "label": "Referral",
14567
+ "order": 4,
14568
+ "entity_type": "metric"
14569
+ }
14570
+ ]
14171
14571
  },
14172
14572
  "presentation": {
14173
14573
  "layout": {
@@ -14214,24 +14614,9 @@ var UPG_FRAMEWORKS = [
14214
14614
  ],
14215
14615
  "slots": [
14216
14616
  {
14217
- "label": "North Star",
14218
- "entityTypeId": "metric",
14219
- "description": "The single metric reflecting core value delivered"
14220
- },
14221
- {
14222
- "label": "Input Metric 1",
14223
- "entityTypeId": "metric",
14224
- "description": "A driver metric you can directly influence"
14225
- },
14226
- {
14227
- "label": "Input Metric 2",
14228
- "entityTypeId": "metric",
14229
- "description": "A driver metric you can directly influence"
14230
- },
14231
- {
14232
- "label": "Input Metric 3",
14617
+ "label": "Metric",
14233
14618
  "entityTypeId": "metric",
14234
- "description": "A driver metric you can directly influence"
14619
+ "description": "The North Star metric or one of its 3-5 input (driver) metrics"
14235
14620
  }
14236
14621
  ],
14237
14622
  "data": {
@@ -14241,7 +14626,28 @@ var UPG_FRAMEWORKS = [
14241
14626
  "role": "item"
14242
14627
  }
14243
14628
  ],
14244
- "required_properties": {},
14629
+ "required_properties": {
14630
+ "metric": [
14631
+ {
14632
+ "property": "metric_role",
14633
+ "type": "enum",
14634
+ "required": true,
14635
+ "label": "Metric role",
14636
+ "description": "Whether this is the single North Star or a driver that feeds it",
14637
+ "enum_values": [
14638
+ "north_star",
14639
+ "input"
14640
+ ]
14641
+ },
14642
+ {
14643
+ "property": "leverage",
14644
+ "type": "number",
14645
+ "required": false,
14646
+ "label": "Leverage",
14647
+ "description": "How strongly this input metric moves the North Star (input metrics only)"
14648
+ }
14649
+ ]
14650
+ },
14245
14651
  "computed_properties": [
14246
14652
  {
14247
14653
  "property": "nsm_impact",
@@ -14541,7 +14947,7 @@ var UPG_FRAMEWORKS = [
14541
14947
  ]
14542
14948
  },
14543
14949
  "education": {
14544
- "purpose": "Design the product itself as the primary growth driver (free tier, self-serve onboarding, in-product virality), reducing dependence on sales-led acquisition.",
14950
+ "purpose": "Design the product itself as the primary growth driver (free tier, self-serve onboarding, in-product virality) to reduce dependence on sales-led acquisition.",
14545
14951
  "core_question": "Can our product acquire, activate, and expand users without human intervention, and where in the loop do we still need sales?",
14546
14952
  "when_to_use": [
14547
14953
  "You are launching a new product, feature, or entering a new market",
@@ -14951,7 +15357,7 @@ var UPG_FRAMEWORKS = [
14951
15357
  {
14952
15358
  "label": "Team",
14953
15359
  "entityTypeId": "team",
14954
- "description": "Team conducting the health check: all members vote anonymously on each dimension"
15360
+ "description": "Team conducting the health check; all members vote anonymously on each dimension"
14955
15361
  },
14956
15362
  {
14957
15363
  "label": "Retrospective",
@@ -15040,7 +15446,7 @@ var UPG_FRAMEWORKS = [
15040
15446
  },
15041
15447
  "education": {
15042
15448
  "purpose": "Regularly assess team health across dimensions (psychological safety, autonomy, mission clarity, fun, speed, learning) to catch problems early and celebrate strengths.",
15043
- "core_question": "How is the team really doing? Where do they feel strong, where do they feel stuck, and what has changed since last check?",
15449
+ "core_question": "How is the team really doing: where do they feel strong, where do they feel stuck, and what has changed since last check?",
15044
15450
  "when_to_use": [
15045
15451
  "You need to improve team collaboration, clarity, or effectiveness",
15046
15452
  "Roles and responsibilities are unclear or causing friction",
@@ -15056,7 +15462,7 @@ var UPG_FRAMEWORKS = [
15056
15462
  "id": "raid-log",
15057
15463
  "name": "RAID Log",
15058
15464
  "version": "1.0.0",
15059
- "description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies: the four categories most likely to derail a project if left unmanaged.",
15465
+ "description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies, the four categories most likely to derail a project if left unmanaged.",
15060
15466
  "category": "program_mgmt",
15061
15467
  "origin": {
15062
15468
  "type": "practitioner",
@@ -16130,6 +16536,7 @@ var STRATEGY_OUTCOMES_PLAYBOOK = {
16130
16536
  description: "Cascade vision through themes, outcomes, objectives, key results, and the bets you are making to get there.",
16131
16537
  region: "strategy_outcomes",
16132
16538
  is_canonical: true,
16539
+ related_framework_ids: ["okr-framework", "three-horizons", "north-star-metric", "metrics-tree", "wardley-map"],
16133
16540
  target_anchor_entity: "objective",
16134
16541
  creation_sequence: [
16135
16542
  seqStep(
@@ -16184,6 +16591,7 @@ var USERS_NEEDS_PLAYBOOK = {
16184
16591
  description: "Bootstrap personas, jobs, needs, and desired outcomes: the user side of every product graph.",
16185
16592
  region: "users_needs",
16186
16593
  is_canonical: true,
16594
+ related_framework_ids: ["persona-canvas", "empathy-map", "value-proposition-canvas", "kano-model"],
16187
16595
  target_anchor_entity: "persona",
16188
16596
  creation_sequence: [
16189
16597
  seqStep(
@@ -16237,6 +16645,7 @@ var DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK = {
16237
16645
  description: "Evidence-first discovery sequence: plan \u2192 recruit \u2192 observe \u2192 synthesize \u2192 insight \u2192 opportunity \u2192 hypothesis \u2192 test.",
16238
16646
  region: "discovery_research_validation",
16239
16647
  is_canonical: true,
16648
+ related_framework_ids: ["double-diamond", "opportunity-solution-tree", "build-measure-learn", "hypothesis-board"],
16240
16649
  target_anchor_entity: "opportunity",
16241
16650
  creation_sequence: [
16242
16651
  seqStep(
@@ -16313,6 +16722,7 @@ var MARKET_COMPETITIVE_PLAYBOOK = {
16313
16722
  description: "Map the competitive landscape: define the market, name the alternatives, read trends, find moves.",
16314
16723
  region: "market_competitive",
16315
16724
  is_canonical: true,
16725
+ related_framework_ids: ["porter-five-forces", "swot-analysis", "value-chain-analysis", "wardley-map", "bullseye-framework"],
16316
16726
  target_anchor_entity: "competitor",
16317
16727
  creation_sequence: [
16318
16728
  seqStep(
@@ -16360,6 +16770,7 @@ var EXPERIENCE_DESIGN_BRAND_PLAYBOOK = {
16360
16770
  description: "Journey-first design sequence: research \u2192 personas \u2192 journeys \u2192 define \u2192 ideate \u2192 prototype \u2192 test \u2192 design system.",
16361
16771
  region: "experience_design_brand",
16362
16772
  is_canonical: true,
16773
+ related_framework_ids: ["double-diamond", "atomic-design", "story-map"],
16363
16774
  target_anchor_entity: "user_journey",
16364
16775
  creation_sequence: [
16365
16776
  seqStep(
@@ -16467,6 +16878,7 @@ var PRODUCT_DELIVERY_PLAYBOOK = {
16467
16878
  description: "Shape what gets built: features, epics, user stories, releases, milestones, and the dependencies between them.",
16468
16879
  region: "product_delivery",
16469
16880
  is_canonical: true,
16881
+ related_framework_ids: ["story-map", "rice-scoring", "moscow", "now-next-later", "shape-up", "kano-model"],
16470
16882
  target_anchor_entity: "feature",
16471
16883
  creation_sequence: [
16472
16884
  seqStep(
@@ -16530,6 +16942,7 @@ var ENGINEERING_PLATFORM_PLAYBOOK = {
16530
16942
  description: "Architecture-first engineering sequence: architecture \u2192 services \u2192 data \u2192 build \u2192 test \u2192 deploy \u2192 monitor \u2192 security.",
16531
16943
  region: "engineering_platform",
16532
16944
  is_canonical: true,
16945
+ related_framework_ids: ["c4-model", "adr-log", "dora-metrics"],
16533
16946
  target_anchor_entity: "service",
16534
16947
  creation_sequence: [
16535
16948
  seqStep(
@@ -16604,6 +17017,7 @@ var BUSINESS_GTM_GROWTH_PLAYBOOK = {
16604
17017
  description: "Viability-first business sequence: value prop \u2192 customer \u2192 revenue \u2192 costs \u2192 unit economics \u2192 GTM \u2192 competitive advantage.",
16605
17018
  region: "business_gtm_growth",
16606
17019
  is_canonical: true,
17020
+ related_framework_ids: ["business-model-canvas", "pirate-metrics-aarrr", "north-star-metric", "product-led-growth-framework", "marketing-mix-4ps"],
16607
17021
  target_anchor_entity: "value_proposition",
16608
17022
  creation_sequence: [
16609
17023
  seqStep(
@@ -16823,6 +17237,7 @@ var ANALYTICS_DATA_PLAYBOOK = {
16823
17237
  description: "Bootstrap the measurement plane: sources, schemas, pipelines, metrics, dashboards, and the rules that keep them honest.",
16824
17238
  region: "analytics_data",
16825
17239
  is_canonical: true,
17240
+ related_framework_ids: ["metrics-tree", "north-star-metric", "pirate-metrics-aarrr", "dora-metrics"],
16826
17241
  target_anchor_entity: "metric",
16827
17242
  creation_sequence: [
16828
17243
  seqStep(
@@ -16870,6 +17285,7 @@ var OPERATIONS_QUALITY_PLAYBOOK = {
16870
17285
  description: "The operational backbone: pipelines, monitoring, incident response, security, quality gates, compliance, support. Specialised playbooks cover team rituals.",
16871
17286
  region: "operations_quality",
16872
17287
  is_canonical: true,
17288
+ related_framework_ids: ["raci-matrix", "retrospective", "team-health-check", "raid-log"],
16873
17289
  target_anchor_entity: "incident",
16874
17290
  creation_sequence: [
16875
17291
  seqStep(
@@ -23507,8 +23923,275 @@ function validateExpression(expr) {
23507
23923
  return null;
23508
23924
  }
23509
23925
 
23926
+ // src/format/canonical.ts
23927
+ var import_node_crypto = require("crypto");
23928
+ var UPG_CANONICAL_FORMAT_VERSION = "1.0.0";
23929
+ var INTEGRITY_HASH_PRIMITIVE = "sha256";
23930
+ var INTEGRITY_DIGEST_HEX = 32;
23931
+ var INTEGRITY_ALGORITHM = "sha256-128";
23932
+ function canonicalizeOpen(value) {
23933
+ if (Array.isArray(value)) return value.map(canonicalizeOpen);
23934
+ if (value !== null && typeof value === "object") {
23935
+ const out = {};
23936
+ for (const key of Object.keys(value).sort(byCodeUnit)) {
23937
+ out[key] = canonicalizeOpen(value[key]);
23938
+ }
23939
+ return out;
23940
+ }
23941
+ return value;
23942
+ }
23943
+ function byCodeUnit(a, b) {
23944
+ return a < b ? -1 : a > b ? 1 : 0;
23945
+ }
23946
+ function isEmpty(value) {
23947
+ if (value === null || value === void 0) return true;
23948
+ if (typeof value === "string") return value.length === 0;
23949
+ if (Array.isArray(value)) return value.length === 0;
23950
+ if (typeof value === "object") return Object.keys(value).length === 0;
23951
+ return false;
23952
+ }
23953
+ function orderedObject(source, keyOrder, opts = {}) {
23954
+ const force = new Set(opts.forceKeys ?? []);
23955
+ const open = new Set(opts.openKeys ?? []);
23956
+ const out = {};
23957
+ const emit = (key) => {
23958
+ if (!(key in source)) return;
23959
+ const raw = source[key];
23960
+ if (!force.has(key) && isEmpty(raw)) return;
23961
+ out[key] = open.has(key) ? canonicalizeOpen(raw) : raw;
23962
+ };
23963
+ for (const key of keyOrder) emit(key);
23964
+ for (const key of Object.keys(source).sort(byCodeUnit)) {
23965
+ if (!keyOrder.includes(key)) emit(key);
23966
+ }
23967
+ return out;
23968
+ }
23969
+ function repairNodeDrift(node) {
23970
+ const out = { ...node };
23971
+ if (typeof out.properties === "string") {
23972
+ out.properties = parseDriftString(out.properties, "object", `node ${node.id}.properties`);
23973
+ }
23974
+ if (typeof out.tags === "string") {
23975
+ out.tags = parseDriftString(out.tags, "array", `node ${node.id}.tags`);
23976
+ }
23977
+ return out;
23978
+ }
23979
+ function parseDriftString(raw, expect, where) {
23980
+ let parsed;
23981
+ try {
23982
+ parsed = JSON.parse(raw);
23983
+ } catch {
23984
+ throw new Error(`[upg fmt] ${where} is a string that is not valid JSON: ${truncate(raw)}`);
23985
+ }
23986
+ const ok = expect === "array" ? Array.isArray(parsed) : parsed !== null && typeof parsed === "object" && !Array.isArray(parsed);
23987
+ if (!ok) throw new Error(`[upg fmt] ${where} is a string that does not parse to a JSON ${expect}: ${truncate(raw)}`);
23988
+ return parsed;
23989
+ }
23990
+ function truncate(s) {
23991
+ return s.length > 60 ? s.slice(0, 57) + "..." : s;
23992
+ }
23993
+ function deriveSummary(description) {
23994
+ if (!description) return void 0;
23995
+ const firstLine = description.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
23996
+ if (!firstLine) return void 0;
23997
+ return firstLine.length > 200 ? firstLine.slice(0, 197) + "..." : firstLine;
23998
+ }
23999
+ function sortNodes(nodes) {
24000
+ return [...nodes].sort(
24001
+ (a, b) => byCodeUnit(a.type ?? "", b.type ?? "") || byCodeUnit(a.slug ?? a.title ?? "", b.slug ?? b.title ?? "") || byCodeUnit(a.id ?? "", b.id ?? "")
24002
+ );
24003
+ }
24004
+ function sortEdges(edges) {
24005
+ return [...edges].sort(
24006
+ (a, b) => byCodeUnit(a.source ?? "", b.source ?? "") || byCodeUnit(a.target ?? "", b.target ?? "") || byCodeUnit(a.type ?? "", b.type ?? "") || byCodeUnit(a.id ?? "", b.id ?? "")
24007
+ );
24008
+ }
24009
+ var NODE_KEY_ORDER = [
24010
+ "id",
24011
+ "type",
24012
+ "title",
24013
+ "slug",
24014
+ "aliases",
24015
+ "description",
24016
+ "tags",
24017
+ "status",
24018
+ "lifecycle_status",
24019
+ "source_id",
24020
+ "source_type",
24021
+ "mapping_confidence",
24022
+ "external_tool",
24023
+ "external_ref",
24024
+ "external_id",
24025
+ "sort_order",
24026
+ "properties"
24027
+ ];
24028
+ var EDGE_KEY_ORDER = ["id", "source", "target", "type", "mapping_confidence"];
24029
+ var CROSS_EDGE_KEY_ORDER = ["id", "source", "target", "type", "source_product_id", "target_product_id", "mapping_confidence"];
24030
+ var PRODUCT_KEY_ORDER = ["id", "title", "description", "stage", "properties"];
24031
+ function canonicalNode(node) {
24032
+ const repaired = repairNodeDrift(node);
24033
+ if (Array.isArray(repaired.tags)) {
24034
+ repaired.tags = [...new Set(repaired.tags)].sort(byCodeUnit);
24035
+ }
24036
+ return orderedObject(repaired, NODE_KEY_ORDER, { forceKeys: ["id", "type", "title"], openKeys: ["properties"] });
24037
+ }
24038
+ function canonicalEdge(edge) {
24039
+ return orderedObject(edge, EDGE_KEY_ORDER, {
24040
+ forceKeys: ["id", "source", "target", "type"]
24041
+ });
24042
+ }
24043
+ function canonicalCrossEdge(edge) {
24044
+ return orderedObject(edge, CROSS_EDGE_KEY_ORDER, {
24045
+ forceKeys: ["id", "source", "target", "type"]
24046
+ });
24047
+ }
24048
+ function canonicalProduct(product) {
24049
+ return orderedObject(product, PRODUCT_KEY_ORDER, { forceKeys: ["id", "title"], openKeys: ["properties"] });
24050
+ }
24051
+ function singleBody(doc) {
24052
+ return {
24053
+ product: canonicalProduct(doc.product),
24054
+ nodes: sortNodes(doc.nodes ?? []).map(canonicalNode),
24055
+ edges: sortEdges(doc.edges ?? []).map(canonicalEdge)
24056
+ };
24057
+ }
24058
+ function portfolioBody(doc) {
24059
+ const products = [...doc.products ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((p) => {
24060
+ const { nodes, edges, ...rest } = p;
24061
+ return {
24062
+ ...canonicalProduct(rest),
24063
+ nodes: sortNodes(nodes ?? []).map(canonicalNode),
24064
+ edges: sortEdges(edges ?? []).map(canonicalEdge)
24065
+ };
24066
+ });
24067
+ return {
24068
+ organization: orderedObject(doc.organization, ["id", "title", "description", "logo_url", "industry"], { forceKeys: ["id", "title"] }),
24069
+ product_areas: [...doc.product_areas ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((a) => orderedObject(a, ["id", "title", "description", "parent_area_id", "strategic_priority", "products"], { forceKeys: ["id", "title"] })),
24070
+ portfolios: [...doc.portfolios ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((p) => orderedObject(p, ["id", "title", "description", "parent_portfolio_id", "hierarchy_model", "products"], { forceKeys: ["id", "title"] })),
24071
+ products,
24072
+ cross_edges: sortEdges(doc.cross_edges ?? []).map(canonicalCrossEdge)
24073
+ };
24074
+ }
24075
+ function computeBodyChecksum(doc) {
24076
+ const body = isPortfolio(doc) ? portfolioBody(doc) : singleBody(doc);
24077
+ const content = JSON.stringify(body);
24078
+ return (0, import_node_crypto.createHash)(INTEGRITY_HASH_PRIMITIVE).update(content).digest("hex").slice(0, INTEGRITY_DIGEST_HEX);
24079
+ }
24080
+ function isPortfolio(doc) {
24081
+ return doc.type === "portfolio" || "cross_edges" in doc;
24082
+ }
24083
+ function serializeCanonical(doc, opts = {}) {
24084
+ if (isPortfolio(doc)) return serializePortfolioWithHeader(doc, opts);
24085
+ return serializeSingleWithHeader(doc, opts);
24086
+ }
24087
+ function buildProvenance(doc, opts) {
24088
+ const source = opts.source ?? doc.source ?? { tool: "unknown" };
24089
+ return orderedObject(
24090
+ {
24091
+ tool: source.tool,
24092
+ tool_version: source.tool_version,
24093
+ workspace_id: source.workspace_id,
24094
+ exported_at: opts.exportedAt ?? doc.exported_at
24095
+ },
24096
+ ["tool", "tool_version", "workspace_id", "exported_at"],
24097
+ { forceKeys: ["tool"] }
24098
+ );
24099
+ }
24100
+ function serializeSingleWithHeader(doc, opts) {
24101
+ const body = singleBody(doc);
24102
+ const product = doc.product;
24103
+ const header = {
24104
+ format_version: UPG_CANONICAL_FORMAT_VERSION,
24105
+ spec_version: doc.upg_version,
24106
+ product: orderedObject(
24107
+ { id: product.id, title: product.title, stage: product.stage },
24108
+ ["id", "title", "stage"],
24109
+ { forceKeys: ["id", "title"] }
24110
+ )
24111
+ };
24112
+ const summary = deriveSummary(product.description);
24113
+ if (summary) header.summary = summary;
24114
+ header.counts = { nodes: doc.nodes?.length ?? 0, edges: doc.edges?.length ?? 0 };
24115
+ header.provenance = buildProvenance(doc, opts);
24116
+ header.integrity = { algorithm: INTEGRITY_ALGORITHM, body: computeBodyChecksum(doc) };
24117
+ return JSON.stringify({ $upg: header, ...body }, null, 2) + "\n";
24118
+ }
24119
+ function serializePortfolioWithHeader(doc, opts) {
24120
+ const body = portfolioBody(doc);
24121
+ const org = doc.organization;
24122
+ const header = {
24123
+ format_version: UPG_CANONICAL_FORMAT_VERSION,
24124
+ spec_version: doc.upg_version,
24125
+ kind: "portfolio",
24126
+ organization: orderedObject(
24127
+ { id: org.id, title: org.title },
24128
+ ["id", "title"],
24129
+ { forceKeys: ["id", "title"] }
24130
+ )
24131
+ };
24132
+ const summary = deriveSummary(org.description);
24133
+ if (summary) header.summary = summary;
24134
+ header.counts = {
24135
+ products: doc.products?.length ?? 0,
24136
+ product_areas: doc.product_areas?.length ?? 0,
24137
+ portfolios: doc.portfolios?.length ?? 0,
24138
+ cross_edges: doc.cross_edges?.length ?? 0
24139
+ };
24140
+ header.provenance = buildProvenance(doc, opts);
24141
+ header.integrity = { algorithm: INTEGRITY_ALGORITHM, body: computeBodyChecksum(doc) };
24142
+ return JSON.stringify({ $upg: header, ...body }, null, 2) + "\n";
24143
+ }
24144
+ function parseUpg(text) {
24145
+ return normalizeDocument(JSON.parse(text));
24146
+ }
24147
+ function normalizeDocument(obj) {
24148
+ const raw = obj;
24149
+ const header = raw.$upg;
24150
+ const upg_version = header?.spec_version ?? raw.upg_version;
24151
+ const provenance = header?.provenance;
24152
+ const exported_at = provenance?.exported_at ?? raw.exported_at;
24153
+ const source = provenance ? { tool: provenance.tool, tool_version: provenance.tool_version, workspace_id: provenance.workspace_id } : raw.source;
24154
+ const _integrity = header ? void 0 : raw._integrity;
24155
+ const isPortfolioDoc = raw.type === "portfolio" || header?.kind === "portfolio" || "cross_edges" in raw;
24156
+ if (isPortfolioDoc) {
24157
+ const out2 = {
24158
+ upg_version,
24159
+ type: "portfolio",
24160
+ exported_at: exported_at ?? "",
24161
+ source: source ?? { tool: "unknown" },
24162
+ organization: raw.organization,
24163
+ product_areas: raw.product_areas ?? [],
24164
+ portfolios: raw.portfolios ?? [],
24165
+ products: (raw.products ?? []).map((p) => ({
24166
+ ...p,
24167
+ nodes: (p.nodes ?? []).map((n) => repairNodeDrift(n)),
24168
+ edges: p.edges ?? []
24169
+ })),
24170
+ cross_edges: raw.cross_edges ?? []
24171
+ };
24172
+ return out2;
24173
+ }
24174
+ const out = {
24175
+ upg_version,
24176
+ exported_at: exported_at ?? "",
24177
+ source: source ?? { tool: "unknown" },
24178
+ product: raw.product,
24179
+ nodes: (raw.nodes ?? []).map((n) => repairNodeDrift(n)),
24180
+ edges: raw.edges ?? []
24181
+ };
24182
+ if (_integrity) out._integrity = _integrity;
24183
+ return out;
24184
+ }
24185
+ function formatUpgText(text, opts = {}) {
24186
+ return serializeCanonical(parseUpg(text), opts);
24187
+ }
24188
+ function isCanonical(text) {
24189
+ const parsed = parseUpg(text);
24190
+ return text === serializeCanonical(parsed);
24191
+ }
24192
+
23510
24193
  // src/index.ts
23511
- var UPG_VERSION = "0.7.3";
24194
+ var UPG_VERSION = "0.8.0";
23512
24195
  var UPG_FORMAT_VERSION = "0.4.0";
23513
24196
  var MARKDOWN_FORMAT_VERSION = "0.1";
23514
24197
  var UPG_TYPES = getTypes();
@@ -23596,12 +24279,14 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
23596
24279
  OPERATIONS_TEAM_RITUALS_PLAYBOOK,
23597
24280
  PRODUCT_DELIVERY_PLAYBOOK,
23598
24281
  PRODUCT_FEEDBACK_SYNTHESIS_PLAYBOOK,
24282
+ PROPERTY_SCALE_MAP,
23599
24283
  REFLECT_MODES,
23600
24284
  STRATEGY_OUTCOMES_PLAYBOOK,
23601
24285
  UPG_ACTIVE_TYPES,
23602
24286
  UPG_ANTI_PATTERNS,
23603
24287
  UPG_APPROACHES,
23604
24288
  UPG_APPROACHES_BY_ID,
24289
+ UPG_CANONICAL_FORMAT_VERSION,
23605
24290
  UPG_CONTAINMENT_FREE_TYPES,
23606
24291
  UPG_COUNT_BENCHMARKS,
23607
24292
  UPG_CROSS_EDGE_TYPES,
@@ -23665,8 +24350,10 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
23665
24350
  coerceProductStage,
23666
24351
  collectSlugsForType,
23667
24352
  compareVersions,
24353
+ computeBodyChecksum,
23668
24354
  evaluateAntiPatterns,
23669
24355
  findInvalidStatusMigrationTargets,
24356
+ formatUpgText,
23670
24357
  generateSlug,
23671
24358
  getAnchorEntity,
23672
24359
  getAntiPatternById,
@@ -23693,6 +24380,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
23693
24380
  getMigrationMap,
23694
24381
  getPlaybookById,
23695
24382
  getPlaybooksForRegion,
24383
+ getPropertiesForScale,
23696
24384
  getPropertyDefaultScale,
23697
24385
  getPropertyMigrations,
23698
24386
  getPropertySchema,
@@ -23709,6 +24397,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
23709
24397
  getValidChildren,
23710
24398
  getVisibleTypes,
23711
24399
  hasStatusMigration,
24400
+ isCanonical,
23712
24401
  isCanonicalProductStage,
23713
24402
  isContainmentFreeType,
23714
24403
  isDeprecatedType,
@@ -23719,6 +24408,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
23719
24408
  isLifecycleFreeType,
23720
24409
  isLifecyclePlannedType,
23721
24410
  isPolymorphicEdge,
24411
+ isPortfolio,
23722
24412
  isRegisteredPolymorphicEdge,
23723
24413
  isSubSequenceStep,
23724
24414
  isUPGDocument,
@@ -23728,6 +24418,8 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
23728
24418
  migrateNodeProperties,
23729
24419
  migrateProductStage,
23730
24420
  migrateStatusValue,
24421
+ normalizeDocument,
24422
+ parseUpg,
23731
24423
  pickCanonicalEdge,
23732
24424
  resolveAllEdges,
23733
24425
  resolveContainmentEdge,
@@ -23735,6 +24427,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
23735
24427
  resolveLabel,
23736
24428
  resolveSlugCollision,
23737
24429
  rotateSlug,
24430
+ serializeCanonical,
23738
24431
  validateProductStageStrict,
23739
24432
  validateUPGDocument,
23740
24433
  validateUPGFramework,