@unified-product-graph/core 0.9.18 → 0.9.20
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 +32 -0
- package/dist/index.cjs +141 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -3
- package/dist/index.d.ts +63 -3
- package/dist/index.js +138 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,38 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.9.20] - 2026-06-11
|
|
11
|
+
|
|
12
|
+
**The tree-pattern catalogue is now introspectable and drift-guarded.** A pattern can no longer cite an edge the grammar lacks, and clients can read the catalogue instead of reverse-engineering it.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **Two introspection tools** (local + cloud, spec-introspection family, local 121 to 123 / cloud 95 to 97):
|
|
16
|
+
- **`list_tree_patterns`** returns every `get_tree` pattern as a summary row: id, label, the region it is the tree view of, anchor, fallback anchors, natural depth, gap policy, and slot count. Paired with `list_regions`, coverage becomes a queryable diff.
|
|
17
|
+
- **`get_tree_pattern(id)`** returns the full declarative record: region, anchor, fallbacks, gap policy, depth, and the child map resolved to concrete edges. Each parent-to-child slot carries the canonical `via` edge and its `kind`, resolved live from the edge catalogue, so a client reads the real wiring rather than reverse-engineering it from behaviour.
|
|
18
|
+
- **Declarative pattern records.** `UPGTreePattern` gains `region` (ties a pattern to its region and that region's `shape`) and `gap_policy` (`required-children-only` or `all-optional`). Helpers `listTreePatternSummaries`, `describeTreePattern`, and `resolveTreePatternEdges` expose the catalogue from core.
|
|
19
|
+
- **A drift-guard test.** Every pattern's every `(parent -> child)` slot must resolve to a canonical edge in the catalogue. A pattern that cites an ungrounded pair fails the build. This makes the whole class of bug structural (it already caught an `aggregate -> read_model` slot in the 0.9.19 architecture sketch that the grammar does not wire).
|
|
20
|
+
|
|
21
|
+
No entity, domain, region, or edge-count change (entities 315, edges 980); local tools 121 to 123, cloud 95 to 97.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## [0.9.19] - 2026-06-11
|
|
26
|
+
|
|
27
|
+
**The tree-pattern catalogue covers every tree-shaped region, and `delivery` finally roots at the roadmap.** `get_tree` now offers 11 patterns (was 8), one per hierarchical region.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- **`delivery` refined.** The 0.9.17 pattern rooted at the product, not the roadmap: its child map listed `product -> roadmap`, which made the product a superset of the roadmap, so the most-nodes anchor rule chose the product even when a roadmap existed. Dropping that slot lets the roadmap win when present; the product is the fallback root (`product -> release`). Added the missing optional children `roadmap_theme -> feature_area`, `release -> changelog`, and `release -> bug`. Default depth is now 3 (a readable overview to the feature tier; `depth` extends into epic and user story).
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- **Three new tree patterns**, all-optional (browse views, no gap noise):
|
|
34
|
+
- **`architecture`** (anchor `service`, fallback `bounded_context`): services and the API contracts, endpoints, schemas, queues, deployments, and dependencies they own, grouped by bounded context, with domain aggregates and their members. A DAG: a schema or queue shared by several services renders once, then as a reference.
|
|
35
|
+
- **`journey`** (anchor `user_journey`, fallback `user_flow`): a journey over time, its phases and steps, the actions within each step, and the screens those steps surface.
|
|
36
|
+
- **`design_system`** (anchor `design_system`, fallback `design_component`): a design system, its components, their nested sub-components, and the design tokens they consume.
|
|
37
|
+
|
|
38
|
+
No entity, domain, region, edge, or tool-count change (entities 315, edges 980, local tools 121). The work is in the presentation layer: `UPG_TREE_PATTERNS` (8 to 11) and the pattern lists carried by both servers, the CLI, and the show-tree skill.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
10
42
|
## [0.9.18] - 2026-06-11
|
|
11
43
|
|
|
12
44
|
**The CLI help stops drifting, and an unknown `tree` filter stops lying.** The CLI rendered `--help` from a hand-maintained table divorced from each command's own option definitions, so adding a flag silently left its help stale (that is how `tree --pattern` shipped undocumented in 0.9.17). Separately, `upg tree <unknown>` quietly rendered the whole graph mislabeled as the filter. Both are now structural.
|
package/dist/index.cjs
CHANGED
|
@@ -113,6 +113,7 @@ __export(index_exports, {
|
|
|
113
113
|
collectSlugsForType: () => collectSlugsForType,
|
|
114
114
|
compareVersions: () => compareVersions,
|
|
115
115
|
computeBodyChecksum: () => computeBodyChecksum,
|
|
116
|
+
describeTreePattern: () => describeTreePattern,
|
|
116
117
|
edgeCarriesProperties: () => edgeCarriesProperties,
|
|
117
118
|
evaluateAntiPatterns: () => evaluateAntiPatterns,
|
|
118
119
|
findInvalidStatusMigrationTargets: () => findInvalidStatusMigrationTargets,
|
|
@@ -180,6 +181,7 @@ __export(index_exports, {
|
|
|
180
181
|
isSubSequenceStep: () => isSubSequenceStep,
|
|
181
182
|
isUPGDocument: () => isUPGDocument,
|
|
182
183
|
listStatusMigrations: () => listStatusMigrations,
|
|
184
|
+
listTreePatternSummaries: () => listTreePatternSummaries,
|
|
183
185
|
migrateEdge: () => migrateEdge,
|
|
184
186
|
migrateNode: () => migrateNode,
|
|
185
187
|
migrateNodeProperties: () => migrateNodeProperties,
|
|
@@ -193,6 +195,7 @@ __export(index_exports, {
|
|
|
193
195
|
resolveEntityType: () => resolveEntityType,
|
|
194
196
|
resolveLabel: () => resolveLabel,
|
|
195
197
|
resolveSlugCollision: () => resolveSlugCollision,
|
|
198
|
+
resolveTreePatternEdges: () => resolveTreePatternEdges,
|
|
196
199
|
ringOrderedDomainIds: () => ringOrderedDomainIds,
|
|
197
200
|
rotateSlug: () => rotateSlug,
|
|
198
201
|
serializeCanonical: () => serializeCanonical,
|
|
@@ -19879,6 +19882,8 @@ var UPG_TREE_PATTERNS = [
|
|
|
19879
19882
|
label: "Opportunity Solution Tree",
|
|
19880
19883
|
description: "A desired outcome branching into the opportunities under it, the solutions that address them, and the hypotheses + experiment plans that validate them (Teresa Torres).",
|
|
19881
19884
|
framework_id: "opportunity-solution-tree",
|
|
19885
|
+
region: "discovery_research_validation",
|
|
19886
|
+
gap_policy: "required-children-only",
|
|
19882
19887
|
anchor_type: "outcome",
|
|
19883
19888
|
fallback_anchors: ["desired_outcome", "opportunity"],
|
|
19884
19889
|
child_map: {
|
|
@@ -19895,6 +19900,8 @@ var UPG_TREE_PATTERNS = [
|
|
|
19895
19900
|
label: "Objectives and Key Results",
|
|
19896
19901
|
description: "Strategic themes containing objectives, each measured by its key results and the metric that quantifies them (John Doerr).",
|
|
19897
19902
|
framework_id: "okr-framework",
|
|
19903
|
+
region: "strategy_outcomes",
|
|
19904
|
+
gap_policy: "required-children-only",
|
|
19898
19905
|
anchor_type: "strategic_theme",
|
|
19899
19906
|
fallback_anchors: ["objective"],
|
|
19900
19907
|
child_map: {
|
|
@@ -19908,6 +19915,8 @@ var UPG_TREE_PATTERNS = [
|
|
|
19908
19915
|
id: "user",
|
|
19909
19916
|
label: "User chain",
|
|
19910
19917
|
description: "A persona and the jobs it pursues, branching into the needs and desired outcomes behind them.",
|
|
19918
|
+
region: "users_needs",
|
|
19919
|
+
gap_policy: "required-children-only",
|
|
19911
19920
|
anchor_type: "persona",
|
|
19912
19921
|
fallback_anchors: ["job"],
|
|
19913
19922
|
child_map: {
|
|
@@ -19920,6 +19929,8 @@ var UPG_TREE_PATTERNS = [
|
|
|
19920
19929
|
id: "product",
|
|
19921
19930
|
label: "Product breakdown",
|
|
19922
19931
|
description: "The product organised into feature areas, then features, and the optional epic + user story tiers beneath them.",
|
|
19932
|
+
region: "product_delivery",
|
|
19933
|
+
gap_policy: "required-children-only",
|
|
19923
19934
|
anchor_type: "product",
|
|
19924
19935
|
fallback_anchors: ["feature_area", "feature"],
|
|
19925
19936
|
child_map: {
|
|
@@ -19935,6 +19946,8 @@ var UPG_TREE_PATTERNS = [
|
|
|
19935
19946
|
label: "Validation chain",
|
|
19936
19947
|
description: "A hypothesis through its experiment plan, the experiment (or its runs), and the learning it produced.",
|
|
19937
19948
|
framework_id: "build-measure-learn",
|
|
19949
|
+
region: "discovery_research_validation",
|
|
19950
|
+
gap_policy: "required-children-only",
|
|
19938
19951
|
anchor_type: "hypothesis",
|
|
19939
19952
|
fallback_anchors: ["experiment_plan", "experiment"],
|
|
19940
19953
|
child_map: {
|
|
@@ -19949,6 +19962,8 @@ var UPG_TREE_PATTERNS = [
|
|
|
19949
19962
|
id: "strategy",
|
|
19950
19963
|
label: "Strategy cascade",
|
|
19951
19964
|
description: "Vision and mission into the strategic themes (bets), the initiatives that pursue them, and the outcomes they drive. Themes are polymorphically parented: they hang off vision, the product, or a strategic pillar, whichever the graph wired.",
|
|
19965
|
+
region: "strategy_outcomes",
|
|
19966
|
+
gap_policy: "required-children-only",
|
|
19952
19967
|
anchor_type: "vision",
|
|
19953
19968
|
fallback_anchors: ["product", "strategic_theme"],
|
|
19954
19969
|
child_map: {
|
|
@@ -19965,6 +19980,8 @@ var UPG_TREE_PATTERNS = [
|
|
|
19965
19980
|
id: "feature_areas",
|
|
19966
19981
|
label: "Feature areas",
|
|
19967
19982
|
description: "Feature areas and the features they contain.",
|
|
19983
|
+
region: "product_delivery",
|
|
19984
|
+
gap_policy: "all-optional",
|
|
19968
19985
|
anchor_type: "feature_area",
|
|
19969
19986
|
fallback_anchors: ["feature"],
|
|
19970
19987
|
child_map: {
|
|
@@ -19975,19 +19992,96 @@ var UPG_TREE_PATTERNS = [
|
|
|
19975
19992
|
{
|
|
19976
19993
|
id: "delivery",
|
|
19977
19994
|
label: "Delivery roadmap",
|
|
19978
|
-
description: "How product work is scheduled and shipped: the roadmap and its themes/items, the releases they schedule,
|
|
19995
|
+
description: "How product work is scheduled and shipped: the roadmap and its themes/items, the releases they schedule, the features (and the changelog + bugs) those releases deliver, and the feature areas a theme spans. Optional epic + user story tiers appear on request.",
|
|
19996
|
+
region: "product_delivery",
|
|
19997
|
+
gap_policy: "all-optional",
|
|
19979
19998
|
anchor_type: "roadmap",
|
|
19980
|
-
|
|
19999
|
+
// Fallback is the product alone: a graph with no roadmap roots at the
|
|
20000
|
+
// product (product -> release -> feature), reported as anchor_resolved_from:
|
|
20001
|
+
// roadmap. `release` is deliberately NOT a fallback root — a 32-release
|
|
20002
|
+
// forest would out-node the product and shadow it under the most-nodes rule.
|
|
20003
|
+
fallback_anchors: ["product"],
|
|
19981
20004
|
child_map: {
|
|
19982
|
-
product
|
|
20005
|
+
// product holds releases directly (the delivery axis without a roadmap),
|
|
20006
|
+
// but NOT the roadmap: listing roadmap here made the product a superset of
|
|
20007
|
+
// the roadmap, so the most-nodes anchor rule rooted delivery at the product
|
|
20008
|
+
// even when a roadmap existed. Dropping it lets the roadmap win when present.
|
|
20009
|
+
product: [opt("release")],
|
|
19983
20010
|
roadmap: [opt("roadmap_theme"), opt("roadmap_item"), opt("release")],
|
|
19984
|
-
roadmap_theme: [opt("feature")],
|
|
20011
|
+
roadmap_theme: [opt("feature"), opt("feature_area")],
|
|
19985
20012
|
roadmap_item: [opt("feature")],
|
|
19986
|
-
release: [opt("feature")],
|
|
20013
|
+
release: [opt("feature"), opt("changelog"), opt("bug")],
|
|
19987
20014
|
feature: [opt("epic")],
|
|
19988
20015
|
epic: [opt("user_story")]
|
|
19989
20016
|
},
|
|
19990
|
-
|
|
20017
|
+
// Default to 3 tiers (roadmap -> theme/item/release -> feature) for a
|
|
20018
|
+
// readable overview on a many-release roadmap; `depth` extends into
|
|
20019
|
+
// epic -> user_story.
|
|
20020
|
+
natural_depth: 3
|
|
20021
|
+
},
|
|
20022
|
+
{
|
|
20023
|
+
id: "architecture",
|
|
20024
|
+
label: "Architecture",
|
|
20025
|
+
description: "The engineering platform: services and the API contracts, endpoints, schemas, queues, deployments, and dependencies they own, grouped by bounded context, with domain aggregates and their members. A DAG (a schema or queue shared by several services renders once, then as a reference).",
|
|
20026
|
+
region: "engineering_platform",
|
|
20027
|
+
gap_policy: "all-optional",
|
|
20028
|
+
anchor_type: "service",
|
|
20029
|
+
fallback_anchors: ["bounded_context"],
|
|
20030
|
+
child_map: {
|
|
20031
|
+
bounded_context: [
|
|
20032
|
+
opt("service"),
|
|
20033
|
+
opt("external_api"),
|
|
20034
|
+
opt("data_flow"),
|
|
20035
|
+
opt("integration_pattern"),
|
|
20036
|
+
opt("code_repository"),
|
|
20037
|
+
opt("api_contract"),
|
|
20038
|
+
opt("aggregate"),
|
|
20039
|
+
opt("domain_event")
|
|
20040
|
+
],
|
|
20041
|
+
service: [
|
|
20042
|
+
opt("api_contract"),
|
|
20043
|
+
opt("api_endpoint"),
|
|
20044
|
+
opt("database_schema"),
|
|
20045
|
+
opt("queue_topic"),
|
|
20046
|
+
opt("deployment"),
|
|
20047
|
+
opt("build_artifact"),
|
|
20048
|
+
opt("library_dependency"),
|
|
20049
|
+
opt("feature_flag")
|
|
20050
|
+
],
|
|
20051
|
+
aggregate: [opt("domain_entity"), opt("value_object"), opt("command"), opt("domain_event")]
|
|
20052
|
+
},
|
|
20053
|
+
natural_depth: 3
|
|
20054
|
+
},
|
|
20055
|
+
{
|
|
20056
|
+
id: "journey",
|
|
20057
|
+
label: "User journey",
|
|
20058
|
+
description: "A user journey over time: its phases and steps, the actions within each step, and the screens those steps surface. Falls back to a user_flow when journeys are not yet mapped.",
|
|
20059
|
+
region: "experience_design_brand",
|
|
20060
|
+
gap_policy: "all-optional",
|
|
20061
|
+
anchor_type: "user_journey",
|
|
20062
|
+
fallback_anchors: ["user_flow"],
|
|
20063
|
+
child_map: {
|
|
20064
|
+
user_journey: [opt("journey_phase"), opt("journey_step")],
|
|
20065
|
+
journey_phase: [opt("journey_step")],
|
|
20066
|
+
journey_step: [opt("journey_action"), opt("screen")],
|
|
20067
|
+
user_flow: [opt("screen")],
|
|
20068
|
+
screen: [opt("screen_state")]
|
|
20069
|
+
},
|
|
20070
|
+
natural_depth: 3
|
|
20071
|
+
},
|
|
20072
|
+
{
|
|
20073
|
+
id: "design_system",
|
|
20074
|
+
label: "Design system",
|
|
20075
|
+
description: "A design system broken into its components, their nested sub-components (atom to molecule to organism), and the design tokens they consume.",
|
|
20076
|
+
region: "experience_design_brand",
|
|
20077
|
+
gap_policy: "all-optional",
|
|
20078
|
+
anchor_type: "design_system",
|
|
20079
|
+
fallback_anchors: ["design_component"],
|
|
20080
|
+
child_map: {
|
|
20081
|
+
design_system: [opt("design_component"), opt("design_token")],
|
|
20082
|
+
design_component: [opt("design_component"), opt("design_token")]
|
|
20083
|
+
},
|
|
20084
|
+
natural_depth: 3
|
|
19991
20085
|
}
|
|
19992
20086
|
];
|
|
19993
20087
|
var UPG_TREE_PATTERNS_BY_ID = Object.fromEntries(
|
|
@@ -19996,6 +20090,43 @@ var UPG_TREE_PATTERNS_BY_ID = Object.fromEntries(
|
|
|
19996
20090
|
function getTreePattern(id) {
|
|
19997
20091
|
return UPG_TREE_PATTERNS_BY_ID[id];
|
|
19998
20092
|
}
|
|
20093
|
+
function resolvePatternEdge(source, target) {
|
|
20094
|
+
for (const [id, def] of Object.entries(UPG_EDGE_CATALOG)) {
|
|
20095
|
+
if (def.source_type === source && def.target_type === target) {
|
|
20096
|
+
return { via: id, kind: def.classification };
|
|
20097
|
+
}
|
|
20098
|
+
}
|
|
20099
|
+
return null;
|
|
20100
|
+
}
|
|
20101
|
+
function resolveTreePatternEdges(pattern) {
|
|
20102
|
+
const out = [];
|
|
20103
|
+
for (const [parent, children] of Object.entries(pattern.child_map)) {
|
|
20104
|
+
for (const c of children) {
|
|
20105
|
+
const e = resolvePatternEdge(parent, c.type);
|
|
20106
|
+
out.push({ parent, child: c.type, via: e?.via ?? null, kind: e?.kind ?? null, required: !!c.required });
|
|
20107
|
+
}
|
|
20108
|
+
}
|
|
20109
|
+
return out;
|
|
20110
|
+
}
|
|
20111
|
+
function describeTreePattern(id) {
|
|
20112
|
+
const p = getTreePattern(id);
|
|
20113
|
+
if (!p) return void 0;
|
|
20114
|
+
return { ...p, fallback_anchors: [...p.fallback_anchors], edges: resolveTreePatternEdges(p) };
|
|
20115
|
+
}
|
|
20116
|
+
function listTreePatternSummaries() {
|
|
20117
|
+
return UPG_TREE_PATTERNS.map((p) => ({
|
|
20118
|
+
id: p.id,
|
|
20119
|
+
label: p.label,
|
|
20120
|
+
description: p.description,
|
|
20121
|
+
framework_id: p.framework_id,
|
|
20122
|
+
region: p.region,
|
|
20123
|
+
anchor_type: p.anchor_type,
|
|
20124
|
+
fallback_anchors: [...p.fallback_anchors],
|
|
20125
|
+
natural_depth: p.natural_depth,
|
|
20126
|
+
gap_policy: p.gap_policy,
|
|
20127
|
+
slot_count: Object.values(p.child_map).reduce((n, cs) => n + cs.length, 0)
|
|
20128
|
+
}));
|
|
20129
|
+
}
|
|
19999
20130
|
|
|
20000
20131
|
// src/presentation/area-taxonomy.ts
|
|
20001
20132
|
var UPG_AREA_TAXONOMY = [
|
|
@@ -28300,7 +28431,7 @@ function isCanonical(text) {
|
|
|
28300
28431
|
}
|
|
28301
28432
|
|
|
28302
28433
|
// src/index.ts
|
|
28303
|
-
var UPG_VERSION = "0.9.
|
|
28434
|
+
var UPG_VERSION = "0.9.20";
|
|
28304
28435
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
28305
28436
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
28306
28437
|
var UPG_TYPES = getTypes();
|
|
@@ -28458,6 +28589,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
28458
28589
|
collectSlugsForType,
|
|
28459
28590
|
compareVersions,
|
|
28460
28591
|
computeBodyChecksum,
|
|
28592
|
+
describeTreePattern,
|
|
28461
28593
|
edgeCarriesProperties,
|
|
28462
28594
|
evaluateAntiPatterns,
|
|
28463
28595
|
findInvalidStatusMigrationTargets,
|
|
@@ -28525,6 +28657,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
28525
28657
|
isSubSequenceStep,
|
|
28526
28658
|
isUPGDocument,
|
|
28527
28659
|
listStatusMigrations,
|
|
28660
|
+
listTreePatternSummaries,
|
|
28528
28661
|
migrateEdge,
|
|
28529
28662
|
migrateNode,
|
|
28530
28663
|
migrateNodeProperties,
|
|
@@ -28538,6 +28671,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
28538
28671
|
resolveEntityType,
|
|
28539
28672
|
resolveLabel,
|
|
28540
28673
|
resolveSlugCollision,
|
|
28674
|
+
resolveTreePatternEdges,
|
|
28541
28675
|
ringOrderedDomainIds,
|
|
28542
28676
|
rotateSlug,
|
|
28543
28677
|
serializeCanonical,
|