@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 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, and the features (with optional epic + user story tiers) they deliver.",
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
- fallback_anchors: ["product", "release"],
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: [opt("roadmap"), opt("release")],
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
- natural_depth: 5
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.18";
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,