@unified-product-graph/core 0.34.0 → 0.36.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/CHANGELOG.md +66 -0
- package/README.md +1 -1
- package/dist/index.cjs +649 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +387 -27
- package/dist/index.d.ts +387 -27
- package/dist/index.js +646 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,7 @@ __export(index_exports, {
|
|
|
60
60
|
PROPERTY_MODIFIERS: () => PROPERTY_MODIFIERS,
|
|
61
61
|
PROPERTY_MODIFIER_SEMANTICS: () => PROPERTY_MODIFIER_SEMANTICS,
|
|
62
62
|
PROPERTY_SCALE_MAP: () => PROPERTY_SCALE_MAP,
|
|
63
|
+
PROPERTY_SCALE_MAP_BY_ENTITY: () => PROPERTY_SCALE_MAP_BY_ENTITY,
|
|
63
64
|
QUALIFIABLE_EDGE_TYPES: () => QUALIFIABLE_EDGE_TYPES,
|
|
64
65
|
RECORD_ENTITY_TYPES: () => RECORD_ENTITY_TYPES,
|
|
65
66
|
REFLECT_MODES: () => REFLECT_MODES,
|
|
@@ -96,6 +97,7 @@ __export(index_exports, {
|
|
|
96
97
|
UPG_EDGE_PAIR_MAP: () => UPG_EDGE_PAIR_MAP,
|
|
97
98
|
UPG_EDGE_TYPES: () => UPG_EDGE_TYPES,
|
|
98
99
|
UPG_ENTITY_COUNT: () => UPG_ENTITY_COUNT,
|
|
100
|
+
UPG_ENTITY_DESCRIPTIONS: () => UPG_ENTITY_DESCRIPTIONS,
|
|
99
101
|
UPG_ENTITY_FILTER_SPECS: () => UPG_ENTITY_FILTER_SPECS,
|
|
100
102
|
UPG_ENTITY_META: () => UPG_ENTITY_META,
|
|
101
103
|
UPG_ENTITY_META_BY_ID: () => UPG_ENTITY_META_BY_ID,
|
|
@@ -197,6 +199,7 @@ __export(index_exports, {
|
|
|
197
199
|
getDomainIdForType: () => getDomainIdForType,
|
|
198
200
|
getDomainsInRing: () => getDomainsInRing,
|
|
199
201
|
getEdgePropertySchema: () => getEdgePropertySchema,
|
|
202
|
+
getEntityDescription: () => getEntityDescription,
|
|
200
203
|
getEntityModifierSummary: () => getEntityModifierSummary,
|
|
201
204
|
getEnumScale: () => getEnumScale,
|
|
202
205
|
getEnumValueMeta: () => getEnumValueMeta,
|
|
@@ -1491,6 +1494,19 @@ var UPG_EDGE_CATALOG = {
|
|
|
1491
1494
|
product_contains_research_study: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "product", target_type: "research_study" },
|
|
1492
1495
|
research_study_enrolls_participant: { forward_verb: "enrolls", reverse_verb: "enrolled_in", classification: "hierarchy", source_type: "research_study", target_type: "participant" },
|
|
1493
1496
|
research_study_captures_observation: { forward_verb: "captures", reverse_verb: "captured_in", classification: "hierarchy", source_type: "research_study", target_type: "observation" },
|
|
1497
|
+
// (0.35.0) Provenance for a verbatim quote: which study produced it. The
|
|
1498
|
+
// quote card wanted an outbound `quote_from_research_study`; canon answers it
|
|
1499
|
+
// study-first, matching the whole `research_study_*` family (enrolls ·
|
|
1500
|
+
// captures · clusters_into · produces · investigates · follows · collects),
|
|
1501
|
+
// so the quote renders it INBOUND on its connections rail. The `captures`
|
|
1502
|
+
// verb is deliberately reused from the observation twin above: the F3
|
|
1503
|
+
// duplicate gate is on the (source, target) pair, which is new, and a study
|
|
1504
|
+
// captures raw material by one verb regardless of which raw shape it is.
|
|
1505
|
+
// Companion: `UPG_VALID_CHILDREN.research_study` gains 'quote'. A quote may
|
|
1506
|
+
// now hang from observation (raw-capture parent), insight (synthesis parent)
|
|
1507
|
+
// or research_study (provenance parent) — multi-parent grammar is explicitly
|
|
1508
|
+
// sanctioned, and per-INSTANCE parentage stays single via `parent_id`.
|
|
1509
|
+
research_study_captures_quote: { forward_verb: "captures", reverse_verb: "captured_in", classification: "hierarchy", source_type: "research_study", target_type: "quote" },
|
|
1494
1510
|
research_study_clusters_into_affinity_cluster: { forward_verb: "clusters_into", reverse_verb: "clustered_from", classification: "hierarchy", source_type: "research_study", target_type: "affinity_cluster" },
|
|
1495
1511
|
research_study_produces_insight: { forward_verb: "produces", reverse_verb: "produced_by", classification: "hierarchy", source_type: "research_study", target_type: "insight" },
|
|
1496
1512
|
research_study_investigates_research_question: { forward_verb: "investigates", reverse_verb: "investigated_by", classification: "hierarchy", source_type: "research_study", target_type: "research_question" },
|
|
@@ -1772,6 +1788,22 @@ var UPG_EDGE_CATALOG = {
|
|
|
1772
1788
|
// the work into `task`; v0.7.0/UPG-571 re-canonicalised the statement
|
|
1773
1789
|
// story_statement → user_story; see UPG_EDGE_MIGRATIONS['0.7.0'].)
|
|
1774
1790
|
epic_specified_by_user_story: { forward_verb: "specified_by", reverse_verb: "specifies", classification: "hierarchy", source_type: "epic", target_type: "user_story" },
|
|
1791
|
+
// (0.35.0) The feature-level rung of the same ladder. `epic` is OPTIONAL in
|
|
1792
|
+
// UPG — `feature_decomposed_into_epic` is not mandatory — so a feature that
|
|
1793
|
+
// skips the epic level had no way to reach the stories that specify it, and
|
|
1794
|
+
// `user_story` had exactly one declared parent. Verb pair is cloned verbatim
|
|
1795
|
+
// from the epic rung above so the two rungs read identically; it is NOT
|
|
1796
|
+
// `groups`, which would invent a second verb for one relationship.
|
|
1797
|
+
//
|
|
1798
|
+
// This is also the reason `feature_contains_acceptance_criterion` is NOT
|
|
1799
|
+
// here: with this rung the path is feature → user_story → acceptance_
|
|
1800
|
+
// criterion, and a direct containment edge would be the "X_contains_Y verb
|
|
1801
|
+
// that duplicates the parent link" ARCHITECTURE.md forbids.
|
|
1802
|
+
//
|
|
1803
|
+
// Companion: `UPG_VALID_CHILDREN.feature` gains 'user_story'. Per-instance
|
|
1804
|
+
// parentage stays single (`parent_id`), so the dual declared parent is a
|
|
1805
|
+
// grammar widening, not a dual-parent instance.
|
|
1806
|
+
feature_specified_by_user_story: { forward_verb: "specified_by", reverse_verb: "specifies", classification: "hierarchy", source_type: "feature", target_type: "user_story" },
|
|
1775
1807
|
user_story_verified_by_acceptance_criterion: { forward_verb: "verified_by", reverse_verb: "verifies", classification: "hierarchy", source_type: "user_story", target_type: "acceptance_criterion" },
|
|
1776
1808
|
task_implements_user_story: { forward_verb: "implements", reverse_verb: "implemented_by", classification: "cross-domain", source_type: "task", target_type: "user_story" },
|
|
1777
1809
|
feature_affected_by_bug: { forward_verb: "affected_by", reverse_verb: "affects", classification: "hierarchy", source_type: "feature", target_type: "bug" },
|
|
@@ -1870,6 +1902,25 @@ var UPG_EDGE_CATALOG = {
|
|
|
1870
1902
|
// (graph traversal) treat 'semantic' as the catch-all for non-causal,
|
|
1871
1903
|
// non-hierarchical relationships.
|
|
1872
1904
|
journey_step_precedes_journey_step: { forward_verb: "precedes", reverse_verb: "follows", classification: "semantic", source_type: "journey_step", target_type: "journey_step" },
|
|
1905
|
+
// (0.36.0, UPG feedback 510c4bfa) A user_flow states which user_journey it
|
|
1906
|
+
// maps onto (`user_flow_maps_user_journey`) but not which of that journey's
|
|
1907
|
+
// steps, in what order, it actually walks — the only recovery without this
|
|
1908
|
+
// edge is inferring scope from shared screens, which both over-matches
|
|
1909
|
+
// (co-located read-only steps) and under-matches (steps rendered off-screen).
|
|
1910
|
+
// `journey_step` stays owned by its journey via `user_journey_contains_journey_step`;
|
|
1911
|
+
// this is a reference into that ownership, not a second containment.
|
|
1912
|
+
// `semantic`, not `cross-domain`: both `user_flow` and `journey_step` are in
|
|
1913
|
+
// the ux_design domain, and T1.7's guardrail treats same-domain
|
|
1914
|
+
// `cross-domain` edges as reclassification debt to pay down, not a pattern
|
|
1915
|
+
// to extend — `journey_phase_spans_journey_step` carries that debt already;
|
|
1916
|
+
// this edge does not repeat it. Same reasoning as `journey_step_precedes_journey_step`:
|
|
1917
|
+
// 'semantic' is the catch-all for a non-causal, non-hierarchical, in-domain
|
|
1918
|
+
// relationship. No `carries_properties`: the walked steps' own `step_order`
|
|
1919
|
+
// is already the source of truth for sequence, so ordering is read off the
|
|
1920
|
+
// target, not a second order system that could drift from the journey's. A
|
|
1921
|
+
// flow walking steps out of journey order is a future `carries_properties`
|
|
1922
|
+
// widening, not this one.
|
|
1923
|
+
user_flow_walks_journey_step: { forward_verb: "walks", reverse_verb: "walked_by", classification: "semantic", source_type: "user_flow", target_type: "journey_step" },
|
|
1873
1924
|
user_flow_routes_through_screen: { forward_verb: "routes_through", reverse_verb: "routed_in", classification: "hierarchy", source_type: "user_flow", target_type: "screen" },
|
|
1874
1925
|
screen_renders_as_screen_state: { forward_verb: "renders_as", reverse_verb: "rendered_by", classification: "hierarchy", source_type: "screen", target_type: "screen_state" },
|
|
1875
1926
|
need_reframed_as_design_question: { forward_verb: "reframed_as", reverse_verb: "reframes", classification: "causal", source_type: "need", target_type: "design_question" },
|
|
@@ -2868,6 +2919,16 @@ var UPG_EDGE_CATALOG = {
|
|
|
2868
2919
|
team_decides_decision: { forward_verb: "decides", reverse_verb: "decided_by", classification: "hierarchy", source_type: "team", target_type: "decision" },
|
|
2869
2920
|
decision_references_decision: { forward_verb: "references", reverse_verb: "referenced_by", classification: "cross-domain", source_type: "decision", target_type: "decision" },
|
|
2870
2921
|
stakeholder_maps_to_persona: { forward_verb: "maps_to", reverse_verb: "mapped_by", classification: "cross-domain", source_type: "stakeholder", target_type: "persona" },
|
|
2922
|
+
// (0.35.0) The stakeholder-map relation: who cares about which outcome.
|
|
2923
|
+
// Deliberately a verb-only edge and NOT a `kind: 'scale'` property on a
|
|
2924
|
+
// generic `stakeholder_relates_to_node` — UPG edges are payload-free outside
|
|
2925
|
+
// the gated `carries_properties` set, and the MAGNITUDE of the stake is
|
|
2926
|
+
// already `influence` / `interest` on the stakeholder itself. The reverse
|
|
2927
|
+
// reading is what makes the pair right: "outcome matters_to stakeholder".
|
|
2928
|
+
// Sits beside `persona_pursues_outcome` (the persona twin) and
|
|
2929
|
+
// `product_influenced_by_stakeholder`. Retires the manifest's denormalised
|
|
2930
|
+
// `stake_in` string: the subtitle renders the target outcome's title.
|
|
2931
|
+
stakeholder_invested_in_outcome: { forward_verb: "invested_in", reverse_verb: "matters_to", classification: "cross-domain", source_type: "stakeholder", target_type: "outcome" },
|
|
2871
2932
|
team_okr_aligns_with_objective: { forward_verb: "aligns_with", reverse_verb: "aligned_by", classification: "cross-domain", source_type: "team_okr", target_type: "objective" },
|
|
2872
2933
|
team_okr_aligns_with_key_result: { forward_verb: "aligns_with", reverse_verb: "aligned_by", classification: "cross-domain", source_type: "team_okr", target_type: "key_result" },
|
|
2873
2934
|
ceremony_involves_team: { forward_verb: "involves", reverse_verb: "involved_in", classification: "cross-domain", source_type: "ceremony", target_type: "team" },
|
|
@@ -2946,6 +3007,27 @@ var UPG_EDGE_CATALOG = {
|
|
|
2946
3007
|
compliance_requirement_constrains_feature: { forward_verb: "constrains", reverse_verb: "constrained_by", classification: "cross-domain", source_type: "compliance_requirement", target_type: "feature" },
|
|
2947
3008
|
compliance_requirement_constrains_decision: { forward_verb: "constrains", reverse_verb: "constrained_by", classification: "cross-domain", source_type: "compliance_requirement", target_type: "decision" },
|
|
2948
3009
|
risk_manifests_as_technical_debt_item: { forward_verb: "manifests_as", reverse_verb: "manifested_by", classification: "cross-domain", source_type: "risk", target_type: "technical_debt_item" },
|
|
3010
|
+
// ── Risk exposure (0.35.0) ──────────────────────────────────────────────────
|
|
3011
|
+
// Typed source, wildcard target: the 16th registered polymorphic family, and
|
|
3012
|
+
// structurally the "decision-to-anything" construction with `risk` in the
|
|
3013
|
+
// source slot. Both are `cross-domain`, so neither enters UPG_VALID_CHILDREN
|
|
3014
|
+
// and neither can be walked as containment.
|
|
3015
|
+
//
|
|
3016
|
+
// Why wildcard rather than typed-per-target: what a risk puts at stake is
|
|
3017
|
+
// genuinely unbounded (outcome, key_result, release, service, contract,
|
|
3018
|
+
// launch, metric, feature), and so is what mitigates it (decision, feature,
|
|
3019
|
+
// experiment, security_control, compliance_requirement). Enumerating either
|
|
3020
|
+
// side balloons the catalogue across two open sets while the endpoint carries
|
|
3021
|
+
// no structural role — the exact condition ARCHITECTURE.md admits polymorphism
|
|
3022
|
+
// for. The typed `security_control_mitigates_threat` stays as the
|
|
3023
|
+
// security-domain edge; `risk_mitigated_by_node` is the product generalisation.
|
|
3024
|
+
//
|
|
3025
|
+
// These two replace three manifest fields that were properties pretending to
|
|
3026
|
+
// be relations: `scope_entities` (an entity-reference list) is
|
|
3027
|
+
// risk_threatens_node, and `mitigation_actions` (a top-list of action
|
|
3028
|
+
// strings) is risk_mitigated_by_node. The prose `mitigation?: string` stays.
|
|
3029
|
+
risk_threatens_node: { forward_verb: "threatens", reverse_verb: "threatened_by", classification: "cross-domain", source_type: "risk", target_type: "node" },
|
|
3030
|
+
risk_mitigated_by_node: { forward_verb: "mitigated_by", reverse_verb: "mitigates", classification: "cross-domain", source_type: "risk", target_type: "node" },
|
|
2949
3031
|
data_contract_governs_data_source: { forward_verb: "governs", reverse_verb: "governed_by", classification: "cross-domain", source_type: "data_contract", target_type: "data_source" },
|
|
2950
3032
|
compliance_framework_requires_security_control: { forward_verb: "requires", reverse_verb: "required_by", classification: "cross-domain", source_type: "compliance_framework", target_type: "security_control" },
|
|
2951
3033
|
security_audit_validates_compliance_framework: { forward_verb: "validates", reverse_verb: "validated_by", classification: "cross-domain", source_type: "security_audit", target_type: "compliance_framework" },
|
|
@@ -4232,7 +4314,12 @@ var UPG_POLYMORPHIC_EDGE_KEYS = [
|
|
|
4232
4314
|
// Document transclusion (0.34.0): a document embeds any renderable node's live
|
|
4233
4315
|
// value at a position in its prose. Open by nature rather than by an
|
|
4234
4316
|
// enumeration nobody wanted to write out.
|
|
4235
|
-
"document_transcludes_node"
|
|
4317
|
+
"document_transcludes_node",
|
|
4318
|
+
// Risk exposure (0.35.0): what a risk threatens, and what mitigates it.
|
|
4319
|
+
// Typed source, wildcard target — the decision-to-anything construction with
|
|
4320
|
+
// `risk` in the source slot. Both cross-domain, so containment is untouched.
|
|
4321
|
+
"risk_threatens_node",
|
|
4322
|
+
"risk_mitigated_by_node"
|
|
4236
4323
|
];
|
|
4237
4324
|
var UPG_POLYMORPHIC_EDGE_FAMILIES = {
|
|
4238
4325
|
"universal semantic verbs": ["node_informs_node", "node_constrains_node", "node_inspires_node"],
|
|
@@ -4271,7 +4358,14 @@ var UPG_POLYMORPHIC_EDGE_FAMILIES = {
|
|
|
4271
4358
|
// `document_describes_*` set, which is not polymorphic at all: those are
|
|
4272
4359
|
// enumerated edges saying a document is ABOUT a thing. Transclusion says it
|
|
4273
4360
|
// RENDERS the thing, and the difference is the whole reason the edge exists.
|
|
4274
|
-
"document transclusion": ["document_transcludes_node"]
|
|
4361
|
+
"document transclusion": ["document_transcludes_node"],
|
|
4362
|
+
// 0.35.0. Its own family and deliberately NOT merged into
|
|
4363
|
+
// 'decision-to-anything', which it mirrors structurally. The two say different
|
|
4364
|
+
// things: a decision INFLUENCES what it touches, a risk ENDANGERS what it
|
|
4365
|
+
// threatens, and a partition that merged them on shape alone would assert
|
|
4366
|
+
// those are one relation. Two members because exposure has two directions,
|
|
4367
|
+
// and collapsing them would lose which way an edge points.
|
|
4368
|
+
"risk exposure": ["risk_threatens_node", "risk_mitigated_by_node"]
|
|
4275
4369
|
};
|
|
4276
4370
|
var _POLY_KEY_SET = new Set(UPG_POLYMORPHIC_EDGE_KEYS);
|
|
4277
4371
|
function isPolymorphicEdge(key) {
|
|
@@ -6836,6 +6930,378 @@ function isLegacyProductStage(stage) {
|
|
|
6836
6930
|
return typeof stage === "string" && stage.toLowerCase() in LEGACY_PRODUCT_STAGES;
|
|
6837
6931
|
}
|
|
6838
6932
|
|
|
6933
|
+
// src/catalog/entity-descriptions.ts
|
|
6934
|
+
var UPG_ENTITY_DESCRIPTIONS = Object.freeze({
|
|
6935
|
+
// Strategy
|
|
6936
|
+
product: "The product being created, the root of the graph",
|
|
6937
|
+
outcome: "A desired business or user outcome",
|
|
6938
|
+
objective: "A strategic goal (OKR)",
|
|
6939
|
+
key_result: "A measurable result tied to an objective",
|
|
6940
|
+
metric: "A unified metric that measures progress, health, or behaviour across the product",
|
|
6941
|
+
metric_quality_assessment: "An assessment of whether a metric is well-defined, measurable, and aligned to a meaningful outcome.",
|
|
6942
|
+
vision: "A long-term aspirational statement of the future state",
|
|
6943
|
+
mission: "The purpose and reason the product exists",
|
|
6944
|
+
strategic_theme: "A high-level strategic focus area for a planning period. Its own time_horizon is now deprecated: promote the period to a planning_cycle and link the two with strategic_theme_scoped_to_planning_cycle, so themes share one dated, nestable interval instead of a drifting per-theme label.",
|
|
6945
|
+
initiative: "A large coordinated effort to achieve a strategic goal",
|
|
6946
|
+
capability: "An ability that enables value delivery",
|
|
6947
|
+
value_stream: "An end-to-end flow delivering value to the customer",
|
|
6948
|
+
strategic_pillar: "A foundational principle that guides decisions",
|
|
6949
|
+
strategic_question: "An open strategic question a plan must resolve (of coordination, ownership, sequencing, or which bet to place), held explicitly until a decision retires it; the strategy sibling of research_question and design_question",
|
|
6950
|
+
assumption: "A belief taken as true that underpins a strategy",
|
|
6951
|
+
decision: "A recorded decision with context, rationale, and consequences",
|
|
6952
|
+
constraint: "A limit, requirement, or ceiling the product must respect, whether a self-imposed principle or an externally imposed boundary",
|
|
6953
|
+
// User
|
|
6954
|
+
persona: "An archetype representing a user segment",
|
|
6955
|
+
job: "Job To Be Done: what the user is trying to accomplish",
|
|
6956
|
+
need: "A user need, pain, desire, or constraint",
|
|
6957
|
+
desired_outcome: "What the user hopes to achieve",
|
|
6958
|
+
job_step: "A discrete step within a Job To Be Done",
|
|
6959
|
+
switching_cost: "A barrier preventing users from switching alternatives",
|
|
6960
|
+
// Discovery
|
|
6961
|
+
opportunity: "A validated gap worth solving",
|
|
6962
|
+
solution: "A proposed approach to address an opportunity",
|
|
6963
|
+
feasibility_study: "An assessment of technical or business viability",
|
|
6964
|
+
design_sprint: "A time-boxed sprint to prototype and validate ideas",
|
|
6965
|
+
// Validation
|
|
6966
|
+
hypothesis: "A testable belief about a solution",
|
|
6967
|
+
experiment: "A test designed to validate a hypothesis",
|
|
6968
|
+
experiment_plan: "An experiment plan describing the hypothesis, setup, success criteria, and methodology before a test runs.",
|
|
6969
|
+
experiment_run: "An execution instance of an experiment that records actual conditions, observations, and raw results.",
|
|
6970
|
+
learning: "An insight gained from an experiment",
|
|
6971
|
+
test_plan: "A structured plan for testing a hypothesis",
|
|
6972
|
+
evidence: "Data supporting or refuting a hypothesis",
|
|
6973
|
+
research_plan: "A plan for gathering evidence to validate a draft entity",
|
|
6974
|
+
// Market Intelligence
|
|
6975
|
+
competitor: "A competing product or company",
|
|
6976
|
+
competitor_feature: "A feature offered by a competitor",
|
|
6977
|
+
market_trend: "An emerging trend in the market",
|
|
6978
|
+
market_segment: "A distinct group of potential customers",
|
|
6979
|
+
competitive_analysis: "A structured analysis of the competitive landscape",
|
|
6980
|
+
classification_axis: "A single dimension in a competitive classification matrix that defines one axis of comparison across alternatives.",
|
|
6981
|
+
classification_value: "A position or value on a classification axis, representing where one option lands on that dimension.",
|
|
6982
|
+
competitor_signal: "An observed competitor move or datapoint (a pricing change, launch, or hire) that feeds competitive analysis",
|
|
6983
|
+
// User Research
|
|
6984
|
+
research_study: "A planned research activity",
|
|
6985
|
+
insight: "A synthesised finding from research",
|
|
6986
|
+
participant: "A person participating in research",
|
|
6987
|
+
observation: "A specific behaviour or statement observed",
|
|
6988
|
+
quote: "A direct quote from a research participant",
|
|
6989
|
+
affinity_cluster: "A group of related observations",
|
|
6990
|
+
research_question: "A question guiding a research study",
|
|
6991
|
+
interview_guide: "A structured guide for conducting interviews",
|
|
6992
|
+
survey_response: "A response to a survey instrument",
|
|
6993
|
+
// Experience Design
|
|
6994
|
+
user_journey: "An end-to-end map of a user experience",
|
|
6995
|
+
journey_step: "A single step in a user journey",
|
|
6996
|
+
journey_phase: "A named phase in a user journey that groups a sequence of actions the user performs to reach a milestone.",
|
|
6997
|
+
journey_action: "A single step a user takes within a journey phase, the atomic unit of observable user behaviour.",
|
|
6998
|
+
user_flow: "A navigation path through the product",
|
|
6999
|
+
screen: "A distinct screen or view in the product",
|
|
7000
|
+
screen_state: "A specific state of a screen (e.g., empty, loading, error)",
|
|
7001
|
+
surface: "A place inside a screen, the features that occupy it, and the rule that decides who wins it",
|
|
7002
|
+
design_question: "An open design problem to explore",
|
|
7003
|
+
design_concept: "A possible design direction or approach",
|
|
7004
|
+
prototype: "An interactive mockup for testing",
|
|
7005
|
+
wireframe: "A low-fidelity structural layout",
|
|
7006
|
+
annotation: "A note or markup on a design artifact",
|
|
7007
|
+
interaction_spec: "A specification for an interaction behaviour",
|
|
7008
|
+
// Design System
|
|
7009
|
+
design_component: "A reusable UI component",
|
|
7010
|
+
design_token: "A design token (colour, spacing, typography)",
|
|
7011
|
+
design_system: "The root design system entity",
|
|
7012
|
+
design_pattern: "A documented design pattern",
|
|
7013
|
+
design_guideline: "A design usage guideline",
|
|
7014
|
+
// Brand Identity
|
|
7015
|
+
brand_identity: "The root brand identity entity",
|
|
7016
|
+
brand_colour: "A brand colour definition",
|
|
7017
|
+
brand_typography: "Brand typography specifications",
|
|
7018
|
+
brand_voice: "Brand voice and tone guidelines",
|
|
7019
|
+
brand_logo: "A brand logo or mark",
|
|
7020
|
+
brand_imagery: "Brand imagery and photography guidelines",
|
|
7021
|
+
// Product Specification
|
|
7022
|
+
feature: "A product capability or feature",
|
|
7023
|
+
feature_area: "A grouping of related features",
|
|
7024
|
+
epic: "A large body of work that can be broken into stories",
|
|
7025
|
+
user_story: `A user's goal and the value they expect, in the "As a\u2026 I want\u2026 So that\u2026" format. Now also a first-class plannable unit (priority, effort, assignee, due_date) that schedules into a planning_cycle, and it round-trips an external board's column via workflow_state.`,
|
|
7026
|
+
acceptance_criterion: "A condition that must be met for a story to be done",
|
|
7027
|
+
release: "A shipped version of the product",
|
|
7028
|
+
task: "A unit of work within a story or epic",
|
|
7029
|
+
bug: "A defect or unexpected behaviour",
|
|
7030
|
+
roadmap: "A strategic plan of features and milestones",
|
|
7031
|
+
roadmap_item: "An item on the product roadmap",
|
|
7032
|
+
theme: "A strategic grouping of related features",
|
|
7033
|
+
roadmap_theme: "A customer problem used as the organising unit of a roadmap",
|
|
7034
|
+
changelog: "A record of changes shipped in a release",
|
|
7035
|
+
planning_cycle: "The cadence axis of a plan: a dated interval a team plans in, be it a sprint, iteration, quarter, or program increment. Cycles self-nest, so a fine iteration sits inside a coarse period to form a granularity ladder.",
|
|
7036
|
+
configuration_axis: "A named dimension along which the product's composition differs: a feature flag, a plan tier, a permission level, a beta programme. The stored graph is the union of the family; one configuration is a projection of it.",
|
|
7037
|
+
// Engineering
|
|
7038
|
+
bounded_context: "A DDD bounded context defining a service boundary",
|
|
7039
|
+
service: "A deployable service or microservice",
|
|
7040
|
+
domain_event: "An event published when something happens in the domain",
|
|
7041
|
+
api_contract: "An API contract or specification",
|
|
7042
|
+
technical_debt_item: "A known piece of technical debt",
|
|
7043
|
+
feature_flag: "A feature toggle for controlled rollout",
|
|
7044
|
+
deployment: "A deployment event",
|
|
7045
|
+
aggregate: "A DDD aggregate root",
|
|
7046
|
+
domain_entity: "A DDD domain entity: a core object with a distinct identity and lifecycle inside a bounded context",
|
|
7047
|
+
value_object: "A DDD value object",
|
|
7048
|
+
command: "A DDD command (write operation)",
|
|
7049
|
+
read_model: "A DDD read model (query projection)",
|
|
7050
|
+
api_endpoint: "A specific API endpoint",
|
|
7051
|
+
database_schema: "A database schema definition",
|
|
7052
|
+
queue_topic: "A message queue topic",
|
|
7053
|
+
build_artifact: "A build output (binary, container image)",
|
|
7054
|
+
code_repository: "A source code repository",
|
|
7055
|
+
library_dependency: "A third-party library dependency",
|
|
7056
|
+
integration_pattern: "An integration pattern (saga, event sourcing, etc.)",
|
|
7057
|
+
external_api: "An external API consumed by the product",
|
|
7058
|
+
data_flow: "A data flow between systems",
|
|
7059
|
+
investigation: "An investigation into an issue or incident",
|
|
7060
|
+
root_cause: "An identified root cause of an issue",
|
|
7061
|
+
symptom: "A symptom of a problem",
|
|
7062
|
+
fix: "A fix applied to resolve an issue",
|
|
7063
|
+
// Growth
|
|
7064
|
+
funnel: "A conversion funnel tracking user progression",
|
|
7065
|
+
funnel_step: "A stage within a conversion funnel",
|
|
7066
|
+
acquisition_channel: "A channel through which users are acquired",
|
|
7067
|
+
growth_campaign: "A growth-focused campaign",
|
|
7068
|
+
cohort: "A group of users sharing a common characteristic",
|
|
7069
|
+
behavioral_segment: "A user segment based on behaviour",
|
|
7070
|
+
growth_loop: "A self-reinforcing growth cycle",
|
|
7071
|
+
variant: "A variant in an A/B test",
|
|
7072
|
+
attribution_model: "A model for attributing conversions to touchpoints",
|
|
7073
|
+
// Business Model
|
|
7074
|
+
business_model: "The business model canvas or definition",
|
|
7075
|
+
value_proposition: "A unique value offered to customers",
|
|
7076
|
+
revenue_stream: "A source of revenue",
|
|
7077
|
+
pricing_tier: "A pricing tier or plan",
|
|
7078
|
+
cost_structure: "A cost category or structure",
|
|
7079
|
+
unit_economics: "Per-unit economic metrics (CAC, LTV, etc.)",
|
|
7080
|
+
partnership: "A strategic partnership",
|
|
7081
|
+
key_resource: "A key resource required by the business",
|
|
7082
|
+
key_activity: "A key activity the business performs",
|
|
7083
|
+
target_customer_segment: "A target customer segment",
|
|
7084
|
+
customer_relationship: "A type of customer relationship",
|
|
7085
|
+
distribution_channel: "A channel for delivering value to customers",
|
|
7086
|
+
// Go-To-Market
|
|
7087
|
+
gtm_strategy: "A go-to-market strategy",
|
|
7088
|
+
ideal_customer_profile: "The ideal customer profile (ICP)",
|
|
7089
|
+
positioning: "Product positioning statement",
|
|
7090
|
+
messaging: "Messaging framework and key messages",
|
|
7091
|
+
launch: "A product launch event",
|
|
7092
|
+
content_strategy: "A content strategy for thought leadership",
|
|
7093
|
+
sales_motion: "A repeatable sales motion",
|
|
7094
|
+
competitive_battle_card: "A competitive battle card for sales enablement",
|
|
7095
|
+
demand_gen_program: "A demand generation program",
|
|
7096
|
+
territory: "A sales territory",
|
|
7097
|
+
objection: "A common sales objection",
|
|
7098
|
+
rebuttal: "A rebuttal to a sales objection",
|
|
7099
|
+
proof_point: "Evidence supporting a sales claim",
|
|
7100
|
+
// Team & Organisation
|
|
7101
|
+
team: "A cross-functional team",
|
|
7102
|
+
role: "A role within a team",
|
|
7103
|
+
stakeholder: "A person with influence over the product",
|
|
7104
|
+
team_okr: "A team-level OKR",
|
|
7105
|
+
retrospective: "A team retrospective",
|
|
7106
|
+
dependency: "A cross-team or system dependency",
|
|
7107
|
+
department: "An organisational department",
|
|
7108
|
+
skill: "A skill or competency within a team",
|
|
7109
|
+
ceremony: "A recurring team ritual (standup, planning)",
|
|
7110
|
+
capacity_plan: "A plan for allocating team capacity",
|
|
7111
|
+
person: "A named individual (owner, stakeholder, or participant) referenced across the graph",
|
|
7112
|
+
// Data & Analytics
|
|
7113
|
+
data_source: "A data source or integration",
|
|
7114
|
+
event_schema: "An event schema for tracking",
|
|
7115
|
+
dashboard: "An analytics dashboard",
|
|
7116
|
+
data_model: "A data model or schema",
|
|
7117
|
+
data_quality_rule: "A data quality validation rule",
|
|
7118
|
+
data_product: "A curated, reusable data asset",
|
|
7119
|
+
data_pipeline: "An automated pipeline for data transformation",
|
|
7120
|
+
data_lineage: "A record of data origin and transformations",
|
|
7121
|
+
glossary_term: "A defined term for shared understanding",
|
|
7122
|
+
data_domain: "A logical grouping of related data assets",
|
|
7123
|
+
report: "A structured analytical report",
|
|
7124
|
+
// Customer Success
|
|
7125
|
+
support_ticket: "Customer support request or issue",
|
|
7126
|
+
customer_feedback: "Voice-of-customer input",
|
|
7127
|
+
churn_reason: "Why a customer left",
|
|
7128
|
+
customer_health_score: "A composite health score for a customer",
|
|
7129
|
+
playbook: "A standard operating procedure for CS",
|
|
7130
|
+
service_level_agreement: "A service level agreement with customers",
|
|
7131
|
+
customer_journey_stage: "A stage in the post-sale customer journey",
|
|
7132
|
+
touchpoint: "A customer interaction touchpoint",
|
|
7133
|
+
success_milestone: "A customer success milestone",
|
|
7134
|
+
service_blueprint: "A blueprint of the full service delivery",
|
|
7135
|
+
// Content & Knowledge
|
|
7136
|
+
content_piece: "A piece of content (article, video, etc.)",
|
|
7137
|
+
knowledge_base_article: "A knowledge base article",
|
|
7138
|
+
brand_asset: "A brand asset (logo, image, etc.)",
|
|
7139
|
+
internal_doc: "An internal documentation page",
|
|
7140
|
+
prompt_template: "A reusable prompt template",
|
|
7141
|
+
content_calendar: "A content publication calendar",
|
|
7142
|
+
content_theme: "A thematic grouping for content",
|
|
7143
|
+
documentation_template: "A template for documentation",
|
|
7144
|
+
document: "A general-purpose document",
|
|
7145
|
+
// Legal
|
|
7146
|
+
legal_entity: "A legal entity (company, subsidiary)",
|
|
7147
|
+
ip_asset: "An intellectual property asset",
|
|
7148
|
+
contract: "A legal contract",
|
|
7149
|
+
contract_clause: "A clause within a contract",
|
|
7150
|
+
privacy_policy: "A privacy policy",
|
|
7151
|
+
// Compliance
|
|
7152
|
+
compliance_requirement: "A compliance requirement",
|
|
7153
|
+
risk: "A risk to the product or business",
|
|
7154
|
+
data_contract: "A data-sharing contract",
|
|
7155
|
+
audit_log_policy: "An audit logging policy",
|
|
7156
|
+
compliance_framework: "A compliance framework (SOC 2, GDPR, etc.)",
|
|
7157
|
+
security_audit: "A security audit",
|
|
7158
|
+
// DevOps & Platform
|
|
7159
|
+
service_level_indicator: "A service level indicator (SLI)",
|
|
7160
|
+
service_level_objective: "A service level objective (SLO)",
|
|
7161
|
+
error_budget: "An error budget for a service",
|
|
7162
|
+
incident: "A production incident",
|
|
7163
|
+
postmortem: "A post-incident review",
|
|
7164
|
+
runbook: "A runbook for incident response",
|
|
7165
|
+
monitor: "A monitoring check",
|
|
7166
|
+
alert_rule: "An alerting rule",
|
|
7167
|
+
ci_pipeline: "A CI/CD pipeline",
|
|
7168
|
+
release_strategy: "A release strategy (canary, blue-green, etc.)",
|
|
7169
|
+
on_call_rotation: "An on-call rotation schedule",
|
|
7170
|
+
infrastructure_component: "An infrastructure component (server, CDN, etc.)",
|
|
7171
|
+
// Security
|
|
7172
|
+
threat_model: "A threat model for the system",
|
|
7173
|
+
threat: "A specific security threat",
|
|
7174
|
+
vulnerability: "A known vulnerability",
|
|
7175
|
+
security_control: "A security control or mitigation",
|
|
7176
|
+
security_policy: "A security policy",
|
|
7177
|
+
penetration_test: "A penetration test",
|
|
7178
|
+
security_review: "A security review",
|
|
7179
|
+
data_classification: "A data classification level",
|
|
7180
|
+
access_policy: "An access control policy",
|
|
7181
|
+
// Accessibility
|
|
7182
|
+
a11y_standard: "An accessibility standard (WCAG, Section 508)",
|
|
7183
|
+
a11y_guideline: "An accessibility guideline",
|
|
7184
|
+
a11y_audit: "An accessibility audit",
|
|
7185
|
+
a11y_issue: "An accessibility issue",
|
|
7186
|
+
a11y_annotation: "An accessibility annotation on a design",
|
|
7187
|
+
// Quality Assurance
|
|
7188
|
+
test_suite: "A suite of related tests",
|
|
7189
|
+
test_case: "An individual test case",
|
|
7190
|
+
qa_session: "An exploratory QA session",
|
|
7191
|
+
regression_test: "A regression test",
|
|
7192
|
+
test_coverage_report: "A test coverage report",
|
|
7193
|
+
test_environment: "A testing environment",
|
|
7194
|
+
test_result: "A test execution result",
|
|
7195
|
+
// Customer Feedback
|
|
7196
|
+
feedback_program: "A structured feedback collection program",
|
|
7197
|
+
feature_request: "A user-submitted feature request",
|
|
7198
|
+
feedback_vote: "A vote on a feature request or feedback item",
|
|
7199
|
+
nps_campaign: "A Net Promoter Score survey campaign",
|
|
7200
|
+
user_advisory_board: "A panel of users providing ongoing feedback",
|
|
7201
|
+
beta_program: "A beta testing program",
|
|
7202
|
+
feedback_theme: "A recurring theme across feedback items",
|
|
7203
|
+
// Pricing & Packaging
|
|
7204
|
+
pricing_strategy: "An overarching pricing strategy",
|
|
7205
|
+
package: "A bundled package of features at a price point",
|
|
7206
|
+
discount_strategy: "A strategy for offering discounts",
|
|
7207
|
+
trial_config: "Configuration for a free trial",
|
|
7208
|
+
paywall: "A paywall gating premium features",
|
|
7209
|
+
// AI & Machine Learning
|
|
7210
|
+
ai_model: "An AI or ML model used within the product",
|
|
7211
|
+
prompt_version: "A version of a prompt template",
|
|
7212
|
+
eval_benchmark: "A benchmark for evaluating AI quality",
|
|
7213
|
+
eval_run: "An evaluation run against a benchmark",
|
|
7214
|
+
ai_cost_tracker: "A tracker for AI inference costs",
|
|
7215
|
+
hallucination_report: "A report of an AI hallucination",
|
|
7216
|
+
ai_guardrail: "A guardrail for AI safety",
|
|
7217
|
+
model_comparison: "A comparison between AI models",
|
|
7218
|
+
ai_experiment: "An AI-focused experiment",
|
|
7219
|
+
ai_dataset: "A dataset for AI training or evaluation",
|
|
7220
|
+
ai_trace: "An AI inference trace",
|
|
7221
|
+
// Workflows & Agents
|
|
7222
|
+
workflow_template: "A reusable workflow template",
|
|
7223
|
+
workflow_run: "An execution of a workflow",
|
|
7224
|
+
agent_definition: "An autonomous agent definition",
|
|
7225
|
+
agent_session: "An agent working session",
|
|
7226
|
+
review_gate: "A human review checkpoint in a workflow",
|
|
7227
|
+
approval_record: "A record of an approval decision",
|
|
7228
|
+
agent_skill: "A capability of an agent",
|
|
7229
|
+
agent_hook: "A trigger connecting an agent to an event",
|
|
7230
|
+
workflow_artifact: "An output artifact from a workflow run",
|
|
7231
|
+
agent_task: "A discrete unit of agent work",
|
|
7232
|
+
// Portfolio
|
|
7233
|
+
organization: "The top-level organisational entity",
|
|
7234
|
+
portfolio: "A grouping of products by strategic axis",
|
|
7235
|
+
product_area: "A grouping of products by organisational axis",
|
|
7236
|
+
// Sales & Revenue
|
|
7237
|
+
account: "A customer account",
|
|
7238
|
+
contact: "A person within an account",
|
|
7239
|
+
lead: "An inbound lead",
|
|
7240
|
+
deal: "An active sales opportunity",
|
|
7241
|
+
pipeline_sales: "A sales pipeline",
|
|
7242
|
+
pipeline_stage: "A stage in the sales pipeline",
|
|
7243
|
+
quote_document: "A formal quote document",
|
|
7244
|
+
subscription: "A recurring subscription",
|
|
7245
|
+
invoice: "An invoice for billing",
|
|
7246
|
+
forecast: "A revenue forecast",
|
|
7247
|
+
// Program Management
|
|
7248
|
+
program: "A program coordinating multiple projects",
|
|
7249
|
+
project: "A project within a program",
|
|
7250
|
+
milestone: "A key date or achievement",
|
|
7251
|
+
risk_register: "A register of project risks",
|
|
7252
|
+
change_request: "A request to change project scope",
|
|
7253
|
+
deliverable: "A deliverable from a project",
|
|
7254
|
+
resource_allocation: "An allocation of team capacity",
|
|
7255
|
+
status_report: "A project status report",
|
|
7256
|
+
// Marketing
|
|
7257
|
+
marketing_strategy: "A marketing strategy",
|
|
7258
|
+
marketing_channel: "A marketing channel",
|
|
7259
|
+
marketing_campaign_plan: "A marketing campaign plan",
|
|
7260
|
+
email_sequence: "An email nurture sequence",
|
|
7261
|
+
social_post: "A social media post",
|
|
7262
|
+
seo_keyword: "An SEO keyword target",
|
|
7263
|
+
ad_creative: "An ad creative",
|
|
7264
|
+
press_release: "A press release",
|
|
7265
|
+
event: "A marketing event",
|
|
7266
|
+
community_initiative: "A community engagement initiative",
|
|
7267
|
+
// Localisation
|
|
7268
|
+
locale: "A supported locale (language + region)",
|
|
7269
|
+
translation_key: "A translatable string key",
|
|
7270
|
+
translation_bundle: "A bundle of translation keys",
|
|
7271
|
+
locale_config: "Per-locale configuration settings",
|
|
7272
|
+
cultural_adaptation: "A region-specific cultural adaptation",
|
|
7273
|
+
regional_pricing: "Location-based pricing",
|
|
7274
|
+
// Customer Education
|
|
7275
|
+
education_program: "A customer education program",
|
|
7276
|
+
tutorial: "A step-by-step tutorial",
|
|
7277
|
+
walkthrough: "A guided product walkthrough",
|
|
7278
|
+
webinar: "A live or recorded webinar",
|
|
7279
|
+
certification: "A certification program",
|
|
7280
|
+
help_video: "A help video",
|
|
7281
|
+
learning_path: "A structured learning path",
|
|
7282
|
+
// Partners & Ecosystem
|
|
7283
|
+
partner_program: "A partner program",
|
|
7284
|
+
partner_tier: "A partner tier level",
|
|
7285
|
+
api_ecosystem: "An API ecosystem",
|
|
7286
|
+
marketplace_listing: "A marketplace listing",
|
|
7287
|
+
developer_portal: "A developer portal",
|
|
7288
|
+
integration_partner: "An integration partner",
|
|
7289
|
+
partner_revenue_share: "A partner revenue sharing arrangement",
|
|
7290
|
+
// Foundations
|
|
7291
|
+
operating_lifecycle: "A canonical, ordered (often cyclic) operating process a product's journey phases map onto",
|
|
7292
|
+
operating_stage: "One ordered stage of an operating_lifecycle",
|
|
7293
|
+
specification: "A canonical open standard or specification that primitives and products conform to",
|
|
7294
|
+
primitive: "A canonical reusable building block defined by a specification",
|
|
7295
|
+
// Workspace
|
|
7296
|
+
workspace: "A spatial thinking space for arranging entities",
|
|
7297
|
+
framework_exercise: "A recorded run of a product framework (RICE, OST, and the like) over specific entities, holding its inputs and outputs",
|
|
7298
|
+
composition: "A published arrangement of graph entities that has an identity of its own: a gallery, a board, a deck. Membership can be derived by running its query, position is authored by a person, and both are stored because both are real.",
|
|
7299
|
+
capture: "A dated, hashed rendition of something already in the graph: a screenshot of a surface, a PDF of a report, an export of a canvas. The subject stays a graph node; the capture records what it looked like at one moment, and the content hash says whether it still does."
|
|
7300
|
+
});
|
|
7301
|
+
function getEntityDescription(type) {
|
|
7302
|
+
return UPG_ENTITY_DESCRIPTIONS[type];
|
|
7303
|
+
}
|
|
7304
|
+
|
|
6839
7305
|
// src/shapes/base-node.ts
|
|
6840
7306
|
var BASE_NODE_FIELD_PRESENCE = {
|
|
6841
7307
|
id: true,
|
|
@@ -7262,6 +7728,11 @@ var UPG_VALID_CHILDREN = {
|
|
|
7262
7728
|
competitor: ["competitor_feature", "competitor_signal"],
|
|
7263
7729
|
classification_axis: ["classification_value"],
|
|
7264
7730
|
// ── UX Research hierarchy ───────────────────────────────────────────────────
|
|
7731
|
+
// (0.35.0) 'quote' added: a study is the provenance parent of the verbatim
|
|
7732
|
+
// quotes it captured, pairing with `research_study_captures_quote`. A quote
|
|
7733
|
+
// now has three declared parents — observation (raw capture), insight
|
|
7734
|
+
// (synthesis) and research_study (provenance) — which is multi-parent
|
|
7735
|
+
// GRAMMAR; per-instance parentage stays single via `parent_id`.
|
|
7265
7736
|
research_study: [
|
|
7266
7737
|
"participant",
|
|
7267
7738
|
"observation",
|
|
@@ -7269,7 +7740,8 @@ var UPG_VALID_CHILDREN = {
|
|
|
7269
7740
|
"research_question",
|
|
7270
7741
|
"interview_guide",
|
|
7271
7742
|
"insight",
|
|
7272
|
-
"survey_response"
|
|
7743
|
+
"survey_response",
|
|
7744
|
+
"quote"
|
|
7273
7745
|
],
|
|
7274
7746
|
observation: ["quote"],
|
|
7275
7747
|
// F6 (UPG-672): affinity_cluster owns the observations it groups, in addition
|
|
@@ -7326,7 +7798,12 @@ var UPG_VALID_CHILDREN = {
|
|
|
7326
7798
|
// story_task collapsed into canonical task. feature now owns
|
|
7327
7799
|
// task directly; story-derived tasks implement user_story via
|
|
7328
7800
|
// task_implements_user_story edge.
|
|
7329
|
-
|
|
7801
|
+
// (0.35.0) 'user_story' added, pairing with `feature_specified_by_user_story`.
|
|
7802
|
+
// The epic rung is optional in UPG, so a feature that skips it could not
|
|
7803
|
+
// reach the stories that specify it and `user_story` had a single declared
|
|
7804
|
+
// parent. Same widening shape as the 0.23.0 epic twins: an existing leaf type
|
|
7805
|
+
// gains a second, SHALLOWER parent, verbs inherited from the deeper edge.
|
|
7806
|
+
feature: ["epic", "bug", "task", "user_story"],
|
|
7330
7807
|
feature_area: ["feature", "feature_area"],
|
|
7331
7808
|
// epic owns user_story (the templated promise) and, mirroring feature, may
|
|
7332
7809
|
// also directly contain bug/task — heterogeneous imported tickets that belong
|
|
@@ -8112,6 +8589,37 @@ var UPG_SCALES = {
|
|
|
8112
8589
|
// the existing classify-edge population, so nothing needs re-stamping.
|
|
8113
8590
|
friendly_aliases: { low: 2, medium: 3, high: 4 }
|
|
8114
8591
|
},
|
|
8592
|
+
// ── Likelihood ─────────────────────────────────────────────────────────────
|
|
8593
|
+
/**
|
|
8594
|
+
* (0.35.0) How likely a future event is to occur.
|
|
8595
|
+
*
|
|
8596
|
+
* Added because the nine pre-existing ladders had no probability ladder, and
|
|
8597
|
+
* `likelihood` was resolving to `confidence_5` — which is EPISTEMIC. Confidence
|
|
8598
|
+
* says how sure you are of a judgment ("Guessing → Data-backed"); likelihood
|
|
8599
|
+
* says how probable the event is. A risk you are certain about and a risk that
|
|
8600
|
+
* is certain to happen are different facts, and one ladder cannot hold both.
|
|
8601
|
+
*
|
|
8602
|
+
* Vocabulary is ISO 31000's, which is also what `risk.likelihood`,
|
|
8603
|
+
* `threat.likelihood` and the Notion fixtures already say in prose.
|
|
8604
|
+
*
|
|
8605
|
+
* Polarity is `low-is-good` (rendered by the design system's direction map,
|
|
8606
|
+
* which is where numeric-scale polarity lives): 5 on this ladder is bad news.
|
|
8607
|
+
*/
|
|
8608
|
+
likelihood_5: {
|
|
8609
|
+
id: "likelihood_5",
|
|
8610
|
+
label: "Likelihood (5-point)",
|
|
8611
|
+
description: "How likely this event is to occur",
|
|
8612
|
+
min: 1,
|
|
8613
|
+
max: 5,
|
|
8614
|
+
steps: 5,
|
|
8615
|
+
points: [
|
|
8616
|
+
{ value: 1, label: "Rare", description: "Would be surprising; no known precedent" },
|
|
8617
|
+
{ value: 2, label: "Unlikely", description: "Could happen, but not expected" },
|
|
8618
|
+
{ value: 3, label: "Possible", description: "Might happen; roughly even odds" },
|
|
8619
|
+
{ value: 4, label: "Likely", description: "Expected to happen absent intervention" },
|
|
8620
|
+
{ value: 5, label: "Almost certain", description: "Expected to happen; plan for it" }
|
|
8621
|
+
]
|
|
8622
|
+
},
|
|
8115
8623
|
// ── Effort ─────────────────────────────────────────────────────────────────
|
|
8116
8624
|
effort_5: {
|
|
8117
8625
|
id: "effort_5",
|
|
@@ -8184,12 +8692,17 @@ var PROPERTY_SCALE_MAP = {
|
|
|
8184
8692
|
// discovery, validation, sales, product-spec
|
|
8185
8693
|
current_confidence: "confidence_5",
|
|
8186
8694
|
// validation/hypothesis (UPG-579 Option B)
|
|
8187
|
-
likelihood: "confidence_5",
|
|
8188
|
-
// market/risk (UPG-579 Option B)
|
|
8189
8695
|
probability: "confidence_5",
|
|
8190
8696
|
// sales/forecast (UPG-579 Option B)
|
|
8191
8697
|
qualification_score: "confidence_5",
|
|
8192
8698
|
// sales/deal — how confident the qualification is (0.24.0)
|
|
8699
|
+
// ── Likelihood ───────────────────────────────────────────────────────────
|
|
8700
|
+
// (0.35.0) Moved off confidence_5. `likelihood` is how probable the event is,
|
|
8701
|
+
// not how well-evidenced the judgment is; the two were sharing one ladder
|
|
8702
|
+
// because no probability ladder existed. Carries market/risk (`risk`,
|
|
8703
|
+
// `market_trend`) and `security.threat.likelihood` in one line.
|
|
8704
|
+
likelihood: "likelihood_5",
|
|
8705
|
+
// compliance/risk, security/threat, market
|
|
8193
8706
|
// ── Effort ───────────────────────────────────────────────────────────────
|
|
8194
8707
|
effort: "effort_5",
|
|
8195
8708
|
// discovery
|
|
@@ -8219,9 +8732,18 @@ var PROPERTY_SCALE_MAP = {
|
|
|
8219
8732
|
// rarity : VRIO distinctiveness; no canonical named scale fits.
|
|
8220
8733
|
// strength : deprecated (validation/evidence; superseded by weight).
|
|
8221
8734
|
};
|
|
8735
|
+
var PROPERTY_SCALE_MAP_BY_ENTITY = {
|
|
8736
|
+
risk: {
|
|
8737
|
+
// Severity of consequences, not magnitude of benefit. See above.
|
|
8738
|
+
impact: "severity_5",
|
|
8739
|
+
// The DEPRECATED spelling of `likelihood`, held on the same ladder as its
|
|
8740
|
+
// replacement for the length of the deprecation window. See above.
|
|
8741
|
+
probability: "likelihood_5"
|
|
8742
|
+
}
|
|
8743
|
+
};
|
|
8222
8744
|
var DEFAULT_SCALE_ID = "scale_5";
|
|
8223
|
-
function getPropertyDefaultScale(
|
|
8224
|
-
return PROPERTY_SCALE_MAP[propertyName] ?? DEFAULT_SCALE_ID;
|
|
8745
|
+
function getPropertyDefaultScale(entityType, propertyName) {
|
|
8746
|
+
return PROPERTY_SCALE_MAP_BY_ENTITY[entityType]?.[propertyName] ?? PROPERTY_SCALE_MAP[propertyName] ?? DEFAULT_SCALE_ID;
|
|
8225
8747
|
}
|
|
8226
8748
|
function getPropertiesForScale(scaleId) {
|
|
8227
8749
|
return Object.entries(PROPERTY_SCALE_MAP).filter(([, id]) => id === scaleId).map(([property]) => property);
|
|
@@ -8312,6 +8834,29 @@ var UPG_ENUM_SCALES = {
|
|
|
8312
8834
|
{ value: "guideline", label: "Guideline", description: "Recommendation: encouraged and expected in most cases, but not enforced." }
|
|
8313
8835
|
]
|
|
8314
8836
|
},
|
|
8837
|
+
// ── EngagementPosture ────────────────────────────────────────────────
|
|
8838
|
+
/**
|
|
8839
|
+
* (0.35.0) The third axis of stakeholder canon, beside the power/interest
|
|
8840
|
+
* grid `influence` / `interest` carry. Those two say how much weight a
|
|
8841
|
+
* stakeholder has and how much they care; neither says which WAY they lean,
|
|
8842
|
+
* and "who blocks this?" is the question a stakeholder map is drawn to answer.
|
|
8843
|
+
*
|
|
8844
|
+
* `tone_direction: 'high-is-good'` reading champion → blocker: the first
|
|
8845
|
+
* value is the desirable end, matching HealthStatus's convention.
|
|
8846
|
+
*/
|
|
8847
|
+
EngagementPosture: {
|
|
8848
|
+
id: "EngagementPosture",
|
|
8849
|
+
label: "Engagement posture",
|
|
8850
|
+
description: "Which way a stakeholder leans on the thing at hand: actively for it, against it, or neither.",
|
|
8851
|
+
tone_direction: "high-is-good",
|
|
8852
|
+
values: [
|
|
8853
|
+
{ value: "champion", label: "Champion", description: "Actively advocates for it and spends their own capital doing so." },
|
|
8854
|
+
{ value: "supporter", label: "Supporter", description: "In favour and will say so when asked, but does not drive it." },
|
|
8855
|
+
{ value: "neutral", label: "Neutral", description: "No position taken; neither helps nor hinders." },
|
|
8856
|
+
{ value: "skeptic", label: "Skeptic", description: "Unconvinced and voices objections, but is not blocking." },
|
|
8857
|
+
{ value: "blocker", label: "Blocker", description: "Actively opposes, and holds enough leverage to stop it." }
|
|
8858
|
+
]
|
|
8859
|
+
},
|
|
8315
8860
|
// ── ProxyConfidence ─────────────────────────────────────────────────
|
|
8316
8861
|
ProxyConfidence: {
|
|
8317
8862
|
id: "ProxyConfidence",
|
|
@@ -8824,6 +9369,62 @@ function getDeprecatedTypes() {
|
|
|
8824
9369
|
return deprecated;
|
|
8825
9370
|
}
|
|
8826
9371
|
var UPG_PROPERTY_MIGRATIONS = {
|
|
9372
|
+
// ── v0.35.0: risk.probability → risk.likelihood, STAGED ─────────────────────
|
|
9373
|
+
//
|
|
9374
|
+
// Deliberately EMPTY, and the emptiness is the design rather than an omission.
|
|
9375
|
+
// The rename is staged: `RiskProperties.probability` is kept, marked
|
|
9376
|
+
// `@deprecated since="0.35.0" removeIn="1.0.0"`, and still read. 0.35.0 changes
|
|
9377
|
+
// what writers emit (`likelihood`) and what readers prefer; it changes no
|
|
9378
|
+
// stored bytes, so there is nothing for a load-time rule to do. A graph
|
|
9379
|
+
// written before 0.35.0 carries `probability` and no `likelihood` and reads
|
|
9380
|
+
// correctly — which is what staging BUYS, and a rule here would spend it.
|
|
9381
|
+
//
|
|
9382
|
+
// Three reasons there is no executable rule, in order of weight:
|
|
9383
|
+
//
|
|
9384
|
+
// 1. A staged deprecation that rewrites data is not staged. The point of the
|
|
9385
|
+
// `epic.estimate` → `effort` pattern this follows is that both names live
|
|
9386
|
+
// through the window and the consumer chooses; migrating the key on load
|
|
9387
|
+
// would end the window on the first read and break any reader still
|
|
9388
|
+
// asking for the old name.
|
|
9389
|
+
// 2. The executable step belongs at the REMOVAL version, not here. That is
|
|
9390
|
+
// the two-step the `removeIn="0.5.0"` properties followed: declared
|
|
9391
|
+
// deprecated with a deadline at 0.4.0, dropped by `drop_props` rules in
|
|
9392
|
+
// the '0.5.0' block below. `probability` is scheduled for the same
|
|
9393
|
+
// treatment at 1.0.0.
|
|
9394
|
+
// 3. No kind fits, and inventing one would be the larger change. Checked
|
|
9395
|
+
// against the APPLIER, not just the type union:
|
|
9396
|
+
// - `drop_props` deletes, which is the 1.0.0 step, not this one.
|
|
9397
|
+
// - `lift_property_to_top_level` targets `UPGBaseNode`, and `likelihood`
|
|
9398
|
+
// is a property, not a top-level field.
|
|
9399
|
+
// - `rename_top_level` renames a top-level field, same mismatch.
|
|
9400
|
+
// - `remap_property_value` is the near miss and still misses twice: it
|
|
9401
|
+
// `break`s unless `typeof oldValue === 'string'` (these values are
|
|
9402
|
+
// `UPGAssessment` objects, or bare numbers in older graphs), and its
|
|
9403
|
+
// `to_property` split OVERWRITES the source with `reset_value`, which
|
|
9404
|
+
// is precisely the staging this release is buying.
|
|
9405
|
+
// - `reshape_value_to_assessment` wraps a bare number on a named scale;
|
|
9406
|
+
// it does not move a key.
|
|
9407
|
+
// The edge-side `kind: 'rename'` (UPG_EDGE_MIGRATIONS, e.g. 0.8.0's
|
|
9408
|
+
// `theme_groups_feature` -> `roadmap_theme_groups_feature`) renames an
|
|
9409
|
+
// EDGE TYPE across instances and has no property-key analogue. Adding one
|
|
9410
|
+
// is new spec surface: a ratified decision, not a side effect of one
|
|
9411
|
+
// property rename.
|
|
9412
|
+
//
|
|
9413
|
+
// The scale moves ride along and need no rule either. `PROPERTY_SCALE_MAP`
|
|
9414
|
+
// resolves `likelihood` to `likelihood_5` (which carries `threat.likelihood`
|
|
9415
|
+
// off `confidence_5` in the same line, since the map is keyed by name) and
|
|
9416
|
+
// `PROPERTY_SCALE_MAP_BY_ENTITY` resolves `risk.impact` to `severity_5`. Both
|
|
9417
|
+
// resolve at READ time, and an assessment only overrides them by PINNING a
|
|
9418
|
+
// `scale_id` on the stored value. Measured across all 36 `.upg` files in the
|
|
9419
|
+
// repo: zero assessments pin a `scale_id` on `likelihood`, `probability` or
|
|
9420
|
+
// `impact`, so there is nothing stamped to re-stamp. One that does pin a
|
|
9421
|
+
// `scale_id` keeps it, by the existing contract.
|
|
9422
|
+
//
|
|
9423
|
+
// What lands at 1.0.0, written down now so it is not rediscovered:
|
|
9424
|
+
// { kind: 'drop_props', type: 'risk', drop_props: ['probability'],
|
|
9425
|
+
// reason: '1.0.0: risk.probability was @deprecated since 0.35.0 with
|
|
9426
|
+
// removeIn="1.0.0"; superseded by `likelihood` on likelihood_5.' }
|
|
9427
|
+
"0.35.0": [],
|
|
8827
9428
|
"0.32.0": [
|
|
8828
9429
|
// C3 — archive becomes a base-node axis. WorkspaceProperties.archived /
|
|
8829
9430
|
// archived_at shipped the orthogonal-archive idea for exactly one type;
|
|
@@ -14228,10 +14829,23 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
14228
14829
|
// RiskProperties: Risk.
|
|
14229
14830
|
risk: {
|
|
14230
14831
|
risk_type: { type: "string", enum: ["technical", "business", "legal", "security", "operational", "program"], description: "Domain the risk belongs to. The single kind axis for `risk`: there is no second classification vocabulary. `program` added in v0.26.0 so the retired `risk_item` (Program Management) type has a home on the canonical `risk` after consolidation, rather than a parallel `risk_domain` field free to drift from this one." },
|
|
14832
|
+
likelihood: {
|
|
14833
|
+
type: "assessment",
|
|
14834
|
+
scale_id: "likelihood_5",
|
|
14835
|
+
description: "How likely this risk is to materialise. Rated on `likelihood_5` (Rare \u2192 Almost certain).",
|
|
14836
|
+
notes: 'Canonical name since 0.35.0, superseding `probability` below. Three reasons, in order of weight. (1) `probability` was one name for two incompatible types (`UPGAssessment` here, a bare `number` on `forecast.probability`), and `PROPERTY_SCALE_MAP` is keyed by name alone, so a sales percentage and a risk judgment resolved to the same ladder. (2) `likelihood` is already the spec\'s own word: the RISK_ITEM lifecycle prose says "Likelihood and impact have been evaluated", and `threat.likelihood` has been a `UPGAssessment` all along: one name, one type, one ladder. (3) ISO 31000 says likelihood. The ladder moved with the name: `likelihood_5` is new in 0.35.0 because no probability ladder existed and `confidence_5` is epistemic. It says how sure the assessor is, not how likely the event is.',
|
|
14837
|
+
properties: {
|
|
14838
|
+
value: { type: "number", description: "The numeric value, used for computation." },
|
|
14839
|
+
label: { type: "string", description: "The qualitative label (what the assessor meant)." },
|
|
14840
|
+
scale_id: { type: "string", description: "Which assessment scale this was rated on (optional)." },
|
|
14841
|
+
normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
|
|
14842
|
+
},
|
|
14843
|
+
required: ["value", "label"]
|
|
14844
|
+
},
|
|
14231
14845
|
probability: {
|
|
14232
14846
|
type: "assessment",
|
|
14233
|
-
scale_id: "
|
|
14234
|
-
description:
|
|
14847
|
+
scale_id: "likelihood_5",
|
|
14848
|
+
description: 'How likely this risk is to materialise (1 = unlikely, 5 = near certain). @deprecated since="0.35.0" removeIn="1.0.0". Use `likelihood`, which is the spec\'s own word for this in the risk lifecycle and on `threat`. Both names resolve to the same `likelihood_5` ladder for the length of the deprecation window (`PROPERTY_SCALE_MAP_BY_ENTITY.risk.probability`, Captain-ratified 2026-08-22): a deprecated field that renders on a DIFFERENT ladder from its replacement would make one stored 4 read "Confident" under the old name and "Likely" under the new one, which is the data changing meaning at the rename, which is the exact thing staging exists to prevent. `forecast.probability` is untouched and stays on `confidence_5`. STAGED, not renamed. The field is KEPT and still read: 0.35.0 changes what writers emit and what readers prefer, and changes NO stored bytes. A graph written before 0.35.0 carries `probability` and no `likelihood`, and reads correctly, which is the whole point of staging it. Writers: emit `likelihood`. Readers: prefer `likelihood`, fall back to `probability`. The fallback is a CONTRACT on consumers, not executable spec machinery, exactly as it is for `epic.estimate` \u2192 `effort`. `removeIn="1.0.0"` is a deadline, not a wish: at 1.0.0 this field is dropped by a `drop_props` rule in `UPG_PROPERTY_MIGRATIONS`, the same two-step the `removeIn="0.5.0"` properties followed (declared at 0.4.0, dropped by the 0.5.0 rules). Until then there is deliberately no executable rule: see the `\'0.35.0\'` block in `grammar/migrations.ts`.',
|
|
14235
14849
|
properties: {
|
|
14236
14850
|
value: { type: "number", description: "The numeric value, used for computation." },
|
|
14237
14851
|
label: { type: "string", description: "The qualitative label (what the assessor meant)." },
|
|
@@ -14242,8 +14856,9 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
14242
14856
|
},
|
|
14243
14857
|
impact: {
|
|
14244
14858
|
type: "assessment",
|
|
14245
|
-
scale_id: "
|
|
14246
|
-
description: "Severity of consequences if the risk materialises (
|
|
14859
|
+
scale_id: "severity_5",
|
|
14860
|
+
description: "Severity of consequences if the risk materialises. Rated on `severity_5` (Mild inconvenience \u2192 Blocker), NOT the benefit-framed `impact_5`.",
|
|
14861
|
+
notes: "The ladder is set by `PROPERTY_SCALE_MAP_BY_ENTITY.risk.impact` (0.35.0), the per-entity override layer. `impact` legitimately means magnitude of BENEFIT on discovery and market entities, where high is good; on a risk it means severity of harm, where high is bad. Sharing `impact_5` rendered a catastrophic risk green.",
|
|
14247
14862
|
properties: {
|
|
14248
14863
|
value: { type: "number", description: "The numeric value, used for computation." },
|
|
14249
14864
|
label: { type: "string", description: "The qualitative label (what the assessor meant)." },
|
|
@@ -14252,7 +14867,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
14252
14867
|
},
|
|
14253
14868
|
required: ["value", "label"]
|
|
14254
14869
|
},
|
|
14255
|
-
mitigation: { type: "string", description: "Planned or implemented mitigation strategy" }
|
|
14870
|
+
mitigation: { type: "string", description: "Planned or implemented mitigation strategy, as prose.", notes: "Prose only. A structured list of mitigating ACTIONS is a set of edges: `risk_mitigated_by_node` (0.35.0), pointing at the decisions, features and experiments that actually do the mitigating; a string array of them is unqueryable by construction. Likewise, what the risk puts at stake is `risk_threatens_node`, not a scope-list property." }
|
|
14256
14871
|
},
|
|
14257
14872
|
// RiskRegisterProperties: Risk register.
|
|
14258
14873
|
risk_register: {
|
|
@@ -14506,7 +15121,9 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
14506
15121
|
normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
|
|
14507
15122
|
},
|
|
14508
15123
|
required: ["value", "label"]
|
|
14509
|
-
}
|
|
15124
|
+
},
|
|
15125
|
+
engagement_posture: { type: "string", enum: ["champion", "supporter", "neutral", "skeptic", "blocker"], description: "Which way this stakeholder leans: actively for, actively against, or neither. The third axis of the stakeholder model (0.35.0), beside the power/interest grid `influence` and `interest` describe.", notes: "`influence` and `interest` are magnitudes and carry no direction: a high-influence, high-interest stakeholder can be the strongest champion or the one who kills it, and the grid renders them identically. This closed enum is what makes \"who blocks this?\" a query rather than a reading exercise. Pairs with `UPG_ENUM_SCALES.EngagementPosture`. Posture is about the person's stance; it is NOT delivery health, which belongs to work items, and NOT the relationship's operational state." },
|
|
15126
|
+
engagement_cadence: { type: "string", enum: ["continuous", "hourly", "daily", "weekly", "monthly", "quarterly", "yearly", "on_demand", "other"], description: "How often this stakeholder is engaged. Uses the shared `Cadence` scale.", notes: 'Typed as `Cadence` rather than free text on purpose: v0.4.0 introduced that enum precisely to retire strings like `"2x/week"`, and a cadence that cannot be compared across stakeholders cannot answer "who have we not spoken to this quarter?". WHERE you meet them (the channel) is an app-level concern and is deliberately not modelled here.' }
|
|
14510
15127
|
},
|
|
14511
15128
|
// StatusReportProperties: Status report.
|
|
14512
15129
|
status_report: {
|
|
@@ -14582,7 +15199,11 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
14582
15199
|
composition_mode: { type: "string", enum: ["exclusive", "additive", "chained"], description: "How the occupants relate: one wins (`exclusive`), all coexist (`additive`), or each wraps the next (`chained`). Declaring `chained` exempts the surface from `contended-surface-without-arbitration`; leaving this unset does not." },
|
|
14583
15200
|
arbitration_rule: { type: "string", description: 'Who wins when more occupants want the surface than `capacity` allows, and why. Absence is meaningful: on a contested surface it means nobody decided. @example "Highest priority wins; ties break to the most recently updated."', notes: "An empty or absent rule on a contested surface is exactly what `contended-surface-without-arbitration` detects. Do not fill it in with a placeholder to silence the check: the check exists to find the unrecorded decision, and a placeholder hides it without settling anything. The field is overloaded across composition modes, which is worth knowing before writing one. On an `exclusive` surface it records DISPLACEMENT (who is not rendered). On an `additive` surface everyone fits, so what it records is ORDER. Only the displacement reading is what the contention detector reads." },
|
|
14584
15201
|
arbitration_state: { type: "string", enum: ["enforced_documented", "enforced_undocumented", "safe_by_coincidence", "none", "no_contention_by_design"], description: "Whether the arbitration answer is enforced, written down, both, neither, or not owed at all. Leaving the field unset means unassessed, and always did.", notes: 'The separations are the whole point of the field, because absence alone conflates four situations with four different remediations: "enforced in code but never transcribed" is ten minutes of typing, "never decided" is a design meeting, "safe only because nothing has collided yet" is the dangerous one, and "nothing to decide, the occupants never compete" is a chained surface, which owes no arbitration rule at all.' },
|
|
14585
|
-
extensibility: { type: "string", enum: ["closed", "plugin_registerable", "user_configurable"], description: "Who may add occupants to the surface." },
|
|
15202
|
+
extensibility: { type: "string", enum: ["closed", "plugin_registerable", "user_configurable"], description: "Who may add occupants to the surface. @deprecated since 0.36.0, removeIn 1.0.0. Use `extension_mechanism` / `extension_audience` / `extension_scope` / `extension_point`: this single enum collapses four independent facts (mechanism, audience, scope, and the named API entry point) into one value, which forces false `closed` readings on surfaces that are overridable through a mechanism the enum has no room to name. STAGED, not renamed: the field is kept and still read, no stored bytes change. There is no `PROPERTY_SCALE_MAP_BY_ENTITY` entry for this one: unlike `risk.probability` \u2192 `likelihood_5`, this is a structural split with no scale to remap onto, so no machine migration ships; re-modelling a surface's real mechanism/audience/scope is a judgement call, same as promoting `objective.timeframe` to a `planning_cycle` node." },
|
|
15203
|
+
extension_mechanism: { type: "string", enum: ["none", "component_wrap", "component_replace", "list_resolve", "register", "config_flag", "render_callback"], description: "The mechanism by which this surface is customized: e.g. `component_wrap` for a `renderDefault`-style override, `list_resolve` for a filter/append resolver, `register` for named-collection registration. Independent of `extension_audience` and `extension_scope`. `none` means not customizable by this mechanism, not unassessed.", notes: "A surface can be `component_wrap`-able by a schema author globally and `register`-able by a plugin author per type at the same time, which is exactly the shape `extensibility` could not hold: it named only a mechanism-or-audience, never both." },
|
|
15204
|
+
extension_audience: { type: "string[]", enum: ["config_author", "schema_author", "plugin_author", "end_user"], description: "WHO may customize the surface through the declared `extension_mechanism`: the studio config author, the schema author, a plugin author, or the end user. Multi-valued because the same surface is often overridable by more than one audience through different entry points (e.g. globally by a config author, per-type by a schema author)." },
|
|
15205
|
+
extension_scope: { type: "string[]", enum: ["global", "per_type", "per_field", "per_instance"], description: "AT WHAT SCOPE the customization applies: the whole surface, one type, one field, or one instance. Multi-valued: a form field surface can be overridable globally and per type at once.", notes: 'This is the fact `extensibility: \'closed\'` got wrong most often: "has no registration list of its own" (no per-instance entry, e.g. `form.components.field` has none) does not mean "cannot be customized" (it is still overridable globally and per type via a schema-level `components` declaration).' },
|
|
15206
|
+
extension_point: { type: "string", description: 'The named API entry point through which the customization happens. @example "form.components.field", "document.actions", "studio.tools"', notes: "A durable, checkable link from the model to the code, in the same spirit as recording a DOM selector: it makes the customization fact verifiable against the real API rather than asserted from memory." },
|
|
14586
15207
|
mutates_content: { type: "boolean", description: "Whether occupying this surface can change the underlying content, as opposed to only selecting or revealing it. The selector-versus-mutator distinction: a gutter that toggles a value is a mutator, a gutter that jumps the cursor is not." },
|
|
14587
15208
|
dimensional_constraint: { type: "string", description: 'The hard spatial budget the surface imposes on its occupants, in whatever unit the design system speaks. @example "292px wide", "25px per field", "two grid columns"' }
|
|
14588
15209
|
},
|
|
@@ -14764,7 +15385,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
14764
15385
|
category: { type: "string", description: 'Attack or threat scenario. @example "injection", "misconfiguration", "social engineering", "supply chain"' },
|
|
14765
15386
|
likelihood: {
|
|
14766
15387
|
type: "assessment",
|
|
14767
|
-
scale_id: "
|
|
15388
|
+
scale_id: "likelihood_5",
|
|
14768
15389
|
description: "Likelihood (1 = theoretical, 5 = actively exploited). @example value 5 for a known, exploitable, commonly targeted pattern",
|
|
14769
15390
|
properties: {
|
|
14770
15391
|
value: { type: "number", description: "The numeric value, used for computation." },
|
|
@@ -21749,10 +22370,16 @@ function validateUPGDocument(doc) {
|
|
|
21749
22370
|
}
|
|
21750
22371
|
const d = doc;
|
|
21751
22372
|
if (!d.upg_version || typeof d.upg_version !== "string") {
|
|
21752
|
-
errors.push({
|
|
22373
|
+
errors.push({
|
|
22374
|
+
path: "$upg.spec_version",
|
|
22375
|
+
message: "spec_version is required and must be a string"
|
|
22376
|
+
});
|
|
21753
22377
|
}
|
|
21754
22378
|
if (!d.exported_at || typeof d.exported_at !== "string") {
|
|
21755
|
-
errors.push({
|
|
22379
|
+
errors.push({
|
|
22380
|
+
path: "$upg.provenance.exported_at",
|
|
22381
|
+
message: "provenance.exported_at is required and must be an ISO 8601 string"
|
|
22382
|
+
});
|
|
21756
22383
|
}
|
|
21757
22384
|
if (!d.source || typeof d.source !== "object") {
|
|
21758
22385
|
errors.push({ path: "$.source", message: "source is required and must be an object" });
|
|
@@ -33520,7 +34147,7 @@ function checkHeaderSealText(text) {
|
|
|
33520
34147
|
}
|
|
33521
34148
|
|
|
33522
34149
|
// src/index.ts
|
|
33523
|
-
var UPG_VERSION = "0.
|
|
34150
|
+
var UPG_VERSION = "0.36.0";
|
|
33524
34151
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
33525
34152
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
33526
34153
|
var UPG_TYPES = getTypes();
|
|
@@ -33623,6 +34250,7 @@ var UPG_PHASE_COUNT = (() => {
|
|
|
33623
34250
|
PROPERTY_MODIFIERS,
|
|
33624
34251
|
PROPERTY_MODIFIER_SEMANTICS,
|
|
33625
34252
|
PROPERTY_SCALE_MAP,
|
|
34253
|
+
PROPERTY_SCALE_MAP_BY_ENTITY,
|
|
33626
34254
|
QUALIFIABLE_EDGE_TYPES,
|
|
33627
34255
|
RECORD_ENTITY_TYPES,
|
|
33628
34256
|
REFLECT_MODES,
|
|
@@ -33659,6 +34287,7 @@ var UPG_PHASE_COUNT = (() => {
|
|
|
33659
34287
|
UPG_EDGE_PAIR_MAP,
|
|
33660
34288
|
UPG_EDGE_TYPES,
|
|
33661
34289
|
UPG_ENTITY_COUNT,
|
|
34290
|
+
UPG_ENTITY_DESCRIPTIONS,
|
|
33662
34291
|
UPG_ENTITY_FILTER_SPECS,
|
|
33663
34292
|
UPG_ENTITY_META,
|
|
33664
34293
|
UPG_ENTITY_META_BY_ID,
|
|
@@ -33760,6 +34389,7 @@ var UPG_PHASE_COUNT = (() => {
|
|
|
33760
34389
|
getDomainIdForType,
|
|
33761
34390
|
getDomainsInRing,
|
|
33762
34391
|
getEdgePropertySchema,
|
|
34392
|
+
getEntityDescription,
|
|
33763
34393
|
getEntityModifierSummary,
|
|
33764
34394
|
getEnumScale,
|
|
33765
34395
|
getEnumValueMeta,
|