@unified-product-graph/core 0.11.3 → 0.11.5

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,29 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.11.5] - 2026-06-15
11
+
12
+ **`get_tree` gains a `commercial` pattern: the business-model spine as a one-call tree.** The monetisation axis was the one populated, tree-shaped region with no pattern — *"show me the money model: streams, costs, tiers, and the metrics that measure them"* meant a hand-authored `query`. `commercial` roots at `business_model` and walks its revenue streams, cost structure, and unit economics; a stream into its pricing tiers, the metrics that measure it, and the pricing strategy that prices it; and metrics decompose into their components (the MRR waterfall). A pricing tier reached from both its stream and its pricing strategy renders once, then as a shared reference — the same multi-parent (G5) path `delivery` relies on. It is a curated spanning tree over the containment subset of the multi-hub `business_gtm_growth` region (the GTM/value flow stays excluded — a tree would misrepresent it); company-grain financials (CAC, LTV, runway) hang off the product, not a single stream, and remain an okr/strategy concern. Declarative child-map record, no engine work, all-optional (a stream without a metric is not a structural hole). 12 patterns now; no new tools (131). A new pattern on a young tool = a patch.
13
+
14
+ ### Added
15
+ - **`commercial` tree pattern** (a.k.a. business / money model). Anchor `business_model`, fallback `product`, region `business_gtm_growth`, natural depth 3, all-optional gap policy. Child map: `business_model -> {revenue_stream, cost_structure, unit_economics}`; `revenue_stream -> {pricing_tier, metric, pricing_strategy}`; `cost_structure -> {metric}`; `pricing_strategy -> {pricing_tier}`; `metric -> {metric}` (the self-nesting decomposition waterfall, cycle-terminated by the assembler's shared-reference guard). Surfaced on `list_tree_patterns` / `get_tree_pattern` introspection and the `/upg-show-tree` skill.
16
+
17
+ ---
18
+
19
+ ## [0.11.4] - 2026-06-14
20
+
21
+ **`get_tree` now linearises a DAG into a tree deterministically: no double-counted node, children in canonical order.** Field-testing the `journey` pattern on a fully-wired forest surfaced two structural defects, both Tier-1 (truth, not presentation — two competent agents must get the identical tree). A step reachable both directly (`user_journey -> journey_step`) and through its phase (`-> journey_phase -> journey_step`) rendered twice: once in full, once as a hollow `shared` reference — the exact mirror of the 0.9.17 silent-drop fix (G5), now silent *duplication* (J1). And children came back in storage order, ignoring the `*_order` scalars they carry, so every client had to re-sort (J2). Both are fixed by two declarative slot fields on the pattern child-map; the assembler does the linearisation once, server-side. No new tools (131); a behaviour fix on a young tool = a patch.
22
+
23
+ ### Added
24
+ - **`order_by` on a tree pattern child slot.** Names the node scalar `get_tree` sorts the slot's children by (ascending, nodes lacking it last). Wired on the `journey` pattern: `journey_phase -> phase_order`, `journey_step -> step_order`, `journey_action -> action_order`, `screen_state -> state_order` (the UPG-663 sequence convention). Surfaced on `get_tree_pattern` introspection rows. Ordering is a property of the data, not the viewer — so the server returns children pre-sorted rather than leaving every client to re-sort (J2).
25
+ - **`prefer_via` on a tree pattern child slot.** Names the sibling type whose path is the canonical spine when a slot reaches the same node redundantly by two declared paths. The `journey` pattern's direct `user_journey -> journey_step` slot declares `prefer_via: journey_phase`: a step also reachable through a phase renders under the phase only. A step in no phase still renders directly (the direct path is the fallback, never a silent drop). Surfaced on `get_tree_pattern` rows (J1).
26
+
27
+ ### Changed
28
+ - **`get_tree` returns children in canonical order across every pattern.** Children are grouped by their declared slot position, then sorted by the slot's `order_by` scalar (stable within ties). Previously edge/storage order. A pattern with no `order_by` slots is unaffected beyond the now-deterministic slot grouping.
29
+ - **`get_tree` collapses a redundant DAG path instead of double-counting it.** A node reachable from one parent by two declared paths renders once, under the `prefer_via` spine. Genuine multi-parent sharing (a node under two *different* parents) is unchanged — it still renders once in full and as a `shared` reference elsewhere (the 0.9.17 G5 behaviour). Verified on the saturated journey forest: every redundant direct-plus-phase step now renders once; legitimate cross-parent shares preserved.
30
+
31
+ ---
32
+
10
33
  ## [0.11.3] - 2026-06-14
11
34
 
12
35
  **A reclassification now supersedes the prior value instead of leaving a stale edge.** 0.11.0 recorded a competitor's move in the reclassification history but kept the old same-axis classify edge, so after a move the competitor was classified as BOTH the old and the new value — `get_portfolio_tree` double-counted it (Data's brief, confirmed live on 0.11.1). This release retires the prior edge as part of the move, keyed off a new axis cardinality field so a genuinely multi-select axis is never collapsed. Property add + behaviour fix = a patch.
package/dist/index.cjs CHANGED
@@ -20152,8 +20152,8 @@ function buildTypeAliases() {
20152
20152
  var UPG_TYPE_ALIASES = buildTypeAliases();
20153
20153
 
20154
20154
  // src/presentation/tree-patterns.ts
20155
- var req = (type) => ({ type, required: true });
20156
- var opt = (type) => ({ type });
20155
+ var req = (type, order_by) => order_by ? { type, required: true, order_by } : { type, required: true };
20156
+ var opt = (type, order_by) => order_by ? { type, order_by } : { type };
20157
20157
  var UPG_TREE_PATTERNS = [
20158
20158
  {
20159
20159
  id: "ost",
@@ -20339,11 +20339,18 @@ var UPG_TREE_PATTERNS = [
20339
20339
  anchor_type: "user_journey",
20340
20340
  fallback_anchors: ["user_flow"],
20341
20341
  child_map: {
20342
- user_journey: [opt("journey_phase"), opt("journey_step")],
20343
- journey_phase: [opt("journey_step")],
20344
- journey_step: [opt("journey_action"), opt("screen")],
20342
+ // The phase is the grouping layer: a step reachable through a phase
20343
+ // renders under the phase, not twice (J1). `prefer_via` collapses the
20344
+ // redundant direct path; a step in NO phase still renders here. Children
20345
+ // are returned in `*_order` sequence (J2), not storage order.
20346
+ user_journey: [
20347
+ opt("journey_phase", "phase_order"),
20348
+ { type: "journey_step", prefer_via: "journey_phase", order_by: "step_order" }
20349
+ ],
20350
+ journey_phase: [opt("journey_step", "step_order")],
20351
+ journey_step: [opt("journey_action", "action_order"), opt("screen")],
20345
20352
  user_flow: [opt("screen")],
20346
- screen: [opt("screen_state")]
20353
+ screen: [opt("screen_state", "state_order")]
20347
20354
  },
20348
20355
  natural_depth: 3
20349
20356
  },
@@ -20360,6 +20367,43 @@ var UPG_TREE_PATTERNS = [
20360
20367
  design_component: [opt("design_component"), opt("design_token")]
20361
20368
  },
20362
20369
  natural_depth: 3
20370
+ },
20371
+ {
20372
+ id: "commercial",
20373
+ label: "Commercial / money model",
20374
+ description: "The business-model spine: how the product captures value. The business model branches into its revenue streams, cost structure, and unit economics; a stream into its pricing tiers, the metrics that measure it, and the pricing strategy that prices it; and metrics decompose into their components (the MRR waterfall, NRR composition). A pricing tier reached from both its stream and its pricing strategy renders once, then as a shared reference.",
20375
+ // The sustaining money-model captures value as a containment hierarchy, so it
20376
+ // earns a tree even though business_gtm_growth is a multi-hub region: a pattern
20377
+ // is a curated spanning tree over a SUBSET of a region (cf. `architecture` over
20378
+ // the engineering_platform DAG). The GTM/value flow (positioning -> messaging
20379
+ // -> funnel) is deliberately NOT here — a tree would misrepresent it; it wants
20380
+ // sibling flow instruments. Company-grain financial metrics (CAC, LTV, runway)
20381
+ // hang off the product, not a single stream/cost, so they live in the okr/
20382
+ // strategy views, not this money-structure spine.
20383
+ region: "business_gtm_growth",
20384
+ gap_policy: "all-optional",
20385
+ anchor_type: "business_model",
20386
+ // Fallback is the product alone, and product is deliberately NOT a child_map
20387
+ // parent: were business_model listed under product, the product would be a
20388
+ // superset of the money spine and the most-nodes anchor rule would root there
20389
+ // (the same trap delivery hit with product -> roadmap). With product childless,
20390
+ // business_model always out-nodes it and wins; a graph with no business_model
20391
+ // falls back to a bare product root, reported anchor_resolved_from: business_model.
20392
+ fallback_anchors: ["product"],
20393
+ child_map: {
20394
+ business_model: [opt("revenue_stream"), opt("cost_structure"), opt("unit_economics")],
20395
+ revenue_stream: [opt("pricing_tier"), opt("metric"), opt("pricing_strategy")],
20396
+ cost_structure: [opt("metric")],
20397
+ pricing_strategy: [opt("pricing_tier")],
20398
+ // Self-nesting: a metric decomposes into its component metrics (Net New MRR ->
20399
+ // New/Expansion/Contraction/Churned). The assembler's `seen` set terminates
20400
+ // the recursion for free (a metric met again becomes a shared reference).
20401
+ metric: [opt("metric")]
20402
+ },
20403
+ // Default to 3 tiers (business_model -> stream/cost -> tier/metric) for a
20404
+ // readable money-model overview; `depth` extends into the metric -> metric
20405
+ // decomposition waterfall.
20406
+ natural_depth: 3
20363
20407
  }
20364
20408
  ];
20365
20409
  var UPG_TREE_PATTERNS_BY_ID = Object.fromEntries(
@@ -20381,7 +20425,10 @@ function resolveTreePatternEdges(pattern) {
20381
20425
  for (const [parent, children] of Object.entries(pattern.child_map)) {
20382
20426
  for (const c of children) {
20383
20427
  const e = resolvePatternEdge(parent, c.type);
20384
- out.push({ parent, child: c.type, via: e?.via ?? null, kind: e?.kind ?? null, required: !!c.required });
20428
+ const row = { parent, child: c.type, via: e?.via ?? null, kind: e?.kind ?? null, required: !!c.required };
20429
+ if (c.order_by) row.order_by = c.order_by;
20430
+ if (c.prefer_via) row.prefer_via = c.prefer_via;
20431
+ out.push(row);
20385
20432
  }
20386
20433
  }
20387
20434
  return out;
@@ -28768,7 +28815,7 @@ function isCanonical(text) {
28768
28815
  }
28769
28816
 
28770
28817
  // src/index.ts
28771
- var UPG_VERSION = "0.11.3";
28818
+ var UPG_VERSION = "0.11.5";
28772
28819
  var UPG_FORMAT_VERSION = "0.4.0";
28773
28820
  var MARKDOWN_FORMAT_VERSION = "0.1";
28774
28821
  var UPG_TYPES = getTypes();