@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 +23 -0
- package/dist/index.cjs +55 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +55 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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) =>
|
|
20156
|
-
var opt = (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
|
-
|
|
20343
|
-
|
|
20344
|
-
|
|
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
|
-
|
|
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.
|
|
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();
|