@unified-product-graph/core 0.9.16 → 0.9.17
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 +17 -0
- package/dist/index.cjs +72 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -9
- package/dist/index.d.ts +30 -9
- package/dist/index.js +72 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.9.17] - 2026-06-11
|
|
11
|
+
|
|
12
|
+
**CLI-next: the CLI catches up to the spec, the tree learns its frameworks, and ordering finally means something.** The `upg` CLI had frozen at the 0.8.x command set while the MCP tool surface grew to 121; its `list`/`tree` ordered lexically and re-rendered shared subtrees into noise. This release closes all of it, and folds in the `get_tree` pattern-definition fixes from a post-ship report on a real 304-node graph.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **Meaning-aware node ordering** (`compareNodesWithinType`, SDK). The intra-type sort ladder: explicit order field (`<type>_order` / order / sequence / ...) -> semantic version (title or `version`) -> lifecycle/status phase position -> `created_at` -> numeric-aware locale. A release series now reads `v5.0.0, v5.1.0, ... v5.9.0, v5.10.0, ... v5.30.0, v6.0.0` instead of the lexical `v5.1.0, v5.10.0, ... v5.2.0`. `list`/`tree` and `get_tree` share it.
|
|
16
|
+
- **DAG-honest tree assembly.** A UPG graph is a DAG; a multi-parent node now renders its subtree ONCE and appears as a `shared` reference under its other parents, in both `get_tree` (`stats.shared_refs`) and the CLI `tree` (`↗ shown above`). Nothing is silently dropped, nothing re-explodes, cycles terminate. `assembleTree` moved to the SDK so the local server, cloud server, and CLI share one implementation.
|
|
17
|
+
- **A `delivery` tree pattern** for the roadmap/release region; `UPG_TREE_PATTERNS` is now 8.
|
|
18
|
+
- **CLI tool-parity surface**: the full 0.9.x MCP surface as commands. New groups `spec` (offline catalogue browser, 36 nouns), `query`, `registry`, `portfolio`, `area`, `migrate`; `tree --pattern <id>` (framework-aware trees via the shared assembler); and singletons `move`, `disconnect`, `dedupe`, `clone`, `context`, `log`, `prioritise`, `sync status`, `product update`, `batch`. Six MCP tools are deliberately not mirrored (recorded by ADR): the agent-cognitive `plan`/`reflect`/`trace`, the remote-mutating `apply_pull_changeset`/`push_to_cloud`, and the skill-dev `skill_audit`.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **`get_tree` pattern definitions corrected (G1-G7)** against the real graph: child slots gain a `required` flag so gaps flag only genuinely-missing required children (no more ~50 false `epic` gaps); the `strategy` pattern reaches the polymorphically-parented bets and auto-flags a bet with no initiative; `okr` extends to the `metric` leaf and `ost` to `hypothesis`/`experiment_plan`; `validation` accepts `experiment` or `experiment_run`; anchor selection picks the candidate that surfaces the most of the pattern, not the first non-empty.
|
|
22
|
+
|
|
23
|
+
No entity, domain, region, playbook, framework, or local-tool count change (entities 315, edges 980, local tools 121, regions 11, frameworks 46). The work is in the CLI, the SDK, and the tree presentation layer.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
10
27
|
## [0.9.16] - 2026-06-11
|
|
11
28
|
|
|
12
29
|
**get_tree reaches the cloud, the show-tree skill calls it, and the three "area" taxonomies get a documented cross-walk.** 0.9.15 shipped `get_tree` local-only and left the show-tree skill on its interim hardcoded chains; this release finishes the loop and closes the deferred A4 introspection gap.
|
package/dist/index.cjs
CHANGED
|
@@ -18408,13 +18408,13 @@ function validateFrameworkScores(nodes, edges, warnings) {
|
|
|
18408
18408
|
const divisors = divisorInputs(framework);
|
|
18409
18409
|
const path = `$.edges[${i}].properties`;
|
|
18410
18410
|
const scoreObj = score;
|
|
18411
|
-
for (const
|
|
18412
|
-
const value = scoreObj[
|
|
18411
|
+
for (const req2 of requirements) {
|
|
18412
|
+
const value = scoreObj[req2.property];
|
|
18413
18413
|
if (value === void 0 || value === null) continue;
|
|
18414
|
-
const finding = checkScoreValue(
|
|
18414
|
+
const finding = checkScoreValue(req2, value, divisors.has(req2.property), framework.id);
|
|
18415
18415
|
if (finding) {
|
|
18416
18416
|
warnings.push({
|
|
18417
|
-
path: `${path}.${
|
|
18417
|
+
path: `${path}.${req2.property}`,
|
|
18418
18418
|
message: finding,
|
|
18419
18419
|
rule: "framework-score"
|
|
18420
18420
|
});
|
|
@@ -18434,58 +18434,58 @@ function divisorInputs(framework) {
|
|
|
18434
18434
|
return out;
|
|
18435
18435
|
}
|
|
18436
18436
|
var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
|
|
18437
|
-
function checkScoreValue(
|
|
18438
|
-
switch (
|
|
18437
|
+
function checkScoreValue(req2, value, isDivisor, frameworkId) {
|
|
18438
|
+
switch (req2.type) {
|
|
18439
18439
|
case "enum": {
|
|
18440
|
-
const allowed =
|
|
18440
|
+
const allowed = req2.enum_values ?? [];
|
|
18441
18441
|
if (typeof value !== "string") {
|
|
18442
|
-
return `${frameworkId} score "${
|
|
18442
|
+
return `${frameworkId} score "${req2.property}" must be one of ${allowed.join(", ")}; got ${describeKind(value)}.`;
|
|
18443
18443
|
}
|
|
18444
18444
|
if (allowed.length > 0 && !allowed.includes(value)) {
|
|
18445
|
-
return `${frameworkId} score "${
|
|
18445
|
+
return `${frameworkId} score "${req2.property}" has value "${value}" outside its allowed set: ${allowed.join(", ")}.`;
|
|
18446
18446
|
}
|
|
18447
18447
|
return null;
|
|
18448
18448
|
}
|
|
18449
18449
|
case "assessment": {
|
|
18450
18450
|
const num = numericScore(value);
|
|
18451
18451
|
if (num === null) {
|
|
18452
|
-
return `${frameworkId} score "${
|
|
18452
|
+
return `${frameworkId} score "${req2.property}" must be a number; got ${describeKind(value)}.`;
|
|
18453
18453
|
}
|
|
18454
|
-
const scale =
|
|
18454
|
+
const scale = req2.scale_id ? getScale(req2.scale_id) : void 0;
|
|
18455
18455
|
if (scale) {
|
|
18456
18456
|
if (num < scale.min || num > scale.max) {
|
|
18457
|
-
return `${frameworkId} score "${
|
|
18457
|
+
return `${frameworkId} score "${req2.property}" is ${num}, outside the ${req2.scale_id} scale range [${scale.min}, ${scale.max}].`;
|
|
18458
18458
|
}
|
|
18459
18459
|
} else if (num <= 0) {
|
|
18460
|
-
return `${frameworkId} score "${
|
|
18460
|
+
return `${frameworkId} score "${req2.property}" must be greater than 0; got ${num}.`;
|
|
18461
18461
|
}
|
|
18462
18462
|
if (isDivisor && num === 0) {
|
|
18463
|
-
return `${frameworkId} score "${
|
|
18463
|
+
return `${frameworkId} score "${req2.property}" must not be 0 (it is a divisor in the framework's formula).`;
|
|
18464
18464
|
}
|
|
18465
18465
|
return null;
|
|
18466
18466
|
}
|
|
18467
18467
|
case "number": {
|
|
18468
18468
|
const num = numericScore(value);
|
|
18469
18469
|
if (num === null) {
|
|
18470
|
-
return `${frameworkId} score "${
|
|
18470
|
+
return `${frameworkId} score "${req2.property}" must be a number; got ${describeKind(value)}.`;
|
|
18471
18471
|
}
|
|
18472
18472
|
if (isDivisor && num === 0) {
|
|
18473
|
-
return `${frameworkId} score "${
|
|
18473
|
+
return `${frameworkId} score "${req2.property}" must not be 0 (it is a divisor in the framework's formula).`;
|
|
18474
18474
|
}
|
|
18475
18475
|
if (num < 0) {
|
|
18476
|
-
return `${frameworkId} score "${
|
|
18476
|
+
return `${frameworkId} score "${req2.property}" must not be negative; got ${num}.`;
|
|
18477
18477
|
}
|
|
18478
18478
|
return null;
|
|
18479
18479
|
}
|
|
18480
18480
|
case "boolean": {
|
|
18481
18481
|
if (typeof value !== "boolean") {
|
|
18482
|
-
return `${frameworkId} score "${
|
|
18482
|
+
return `${frameworkId} score "${req2.property}" must be a boolean; got ${describeKind(value)}.`;
|
|
18483
18483
|
}
|
|
18484
18484
|
return null;
|
|
18485
18485
|
}
|
|
18486
18486
|
case "string": {
|
|
18487
18487
|
if (typeof value !== "string") {
|
|
18488
|
-
return `${frameworkId} score "${
|
|
18488
|
+
return `${frameworkId} score "${req2.property}" must be a string; got ${describeKind(value)}.`;
|
|
18489
18489
|
}
|
|
18490
18490
|
return null;
|
|
18491
18491
|
}
|
|
@@ -19871,33 +19871,38 @@ function buildTypeAliases() {
|
|
|
19871
19871
|
var UPG_TYPE_ALIASES = buildTypeAliases();
|
|
19872
19872
|
|
|
19873
19873
|
// src/presentation/tree-patterns.ts
|
|
19874
|
+
var req = (type) => ({ type, required: true });
|
|
19875
|
+
var opt = (type) => ({ type });
|
|
19874
19876
|
var UPG_TREE_PATTERNS = [
|
|
19875
19877
|
{
|
|
19876
19878
|
id: "ost",
|
|
19877
19879
|
label: "Opportunity Solution Tree",
|
|
19878
|
-
description: "A desired outcome branching into the opportunities under it
|
|
19880
|
+
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).",
|
|
19879
19881
|
framework_id: "opportunity-solution-tree",
|
|
19880
19882
|
anchor_type: "outcome",
|
|
19881
19883
|
fallback_anchors: ["desired_outcome", "opportunity"],
|
|
19882
19884
|
child_map: {
|
|
19883
|
-
outcome: ["opportunity"],
|
|
19884
|
-
desired_outcome: ["opportunity"],
|
|
19885
|
-
opportunity: ["solution"]
|
|
19885
|
+
outcome: [req("opportunity")],
|
|
19886
|
+
desired_outcome: [req("opportunity")],
|
|
19887
|
+
opportunity: [req("solution")],
|
|
19888
|
+
solution: [opt("hypothesis")],
|
|
19889
|
+
hypothesis: [opt("experiment_plan")]
|
|
19886
19890
|
},
|
|
19887
|
-
natural_depth:
|
|
19891
|
+
natural_depth: 5
|
|
19888
19892
|
},
|
|
19889
19893
|
{
|
|
19890
19894
|
id: "okr",
|
|
19891
19895
|
label: "Objectives and Key Results",
|
|
19892
|
-
description: "Strategic themes containing objectives, each measured by its key results (John Doerr).",
|
|
19896
|
+
description: "Strategic themes containing objectives, each measured by its key results and the metric that quantifies them (John Doerr).",
|
|
19893
19897
|
framework_id: "okr-framework",
|
|
19894
19898
|
anchor_type: "strategic_theme",
|
|
19895
19899
|
fallback_anchors: ["objective"],
|
|
19896
19900
|
child_map: {
|
|
19897
|
-
strategic_theme: ["objective"],
|
|
19898
|
-
objective: ["key_result"]
|
|
19901
|
+
strategic_theme: [req("objective")],
|
|
19902
|
+
objective: [req("key_result")],
|
|
19903
|
+
key_result: [opt("metric")]
|
|
19899
19904
|
},
|
|
19900
|
-
natural_depth:
|
|
19905
|
+
natural_depth: 4
|
|
19901
19906
|
},
|
|
19902
19907
|
{
|
|
19903
19908
|
id: "user",
|
|
@@ -19906,54 +19911,55 @@ var UPG_TREE_PATTERNS = [
|
|
|
19906
19911
|
anchor_type: "persona",
|
|
19907
19912
|
fallback_anchors: ["job"],
|
|
19908
19913
|
child_map: {
|
|
19909
|
-
persona: ["job", "need", "desired_outcome"],
|
|
19910
|
-
job: ["need", "desired_outcome"]
|
|
19914
|
+
persona: [req("job"), opt("need"), opt("desired_outcome")],
|
|
19915
|
+
job: [opt("need"), opt("desired_outcome")]
|
|
19911
19916
|
},
|
|
19912
19917
|
natural_depth: 3
|
|
19913
19918
|
},
|
|
19914
19919
|
{
|
|
19915
19920
|
id: "product",
|
|
19916
19921
|
label: "Product breakdown",
|
|
19917
|
-
description: "The product organised into feature areas, then features,
|
|
19922
|
+
description: "The product organised into feature areas, then features, and the optional epic + user story tiers beneath them.",
|
|
19918
19923
|
anchor_type: "product",
|
|
19919
19924
|
fallback_anchors: ["feature_area", "feature"],
|
|
19920
19925
|
child_map: {
|
|
19921
|
-
product: ["feature_area"],
|
|
19922
|
-
feature_area: ["feature"],
|
|
19923
|
-
feature: ["epic"],
|
|
19924
|
-
epic: ["user_story"]
|
|
19926
|
+
product: [opt("feature_area")],
|
|
19927
|
+
feature_area: [req("feature")],
|
|
19928
|
+
feature: [opt("epic")],
|
|
19929
|
+
epic: [opt("user_story")]
|
|
19925
19930
|
},
|
|
19926
19931
|
natural_depth: 5
|
|
19927
19932
|
},
|
|
19928
19933
|
{
|
|
19929
19934
|
id: "validation",
|
|
19930
19935
|
label: "Validation chain",
|
|
19931
|
-
description: "A hypothesis through its experiment plan, experiment
|
|
19936
|
+
description: "A hypothesis through its experiment plan, the experiment (or its runs), and the learning it produced.",
|
|
19932
19937
|
framework_id: "build-measure-learn",
|
|
19933
19938
|
anchor_type: "hypothesis",
|
|
19934
19939
|
fallback_anchors: ["experiment_plan", "experiment"],
|
|
19935
19940
|
child_map: {
|
|
19936
|
-
hypothesis: ["experiment_plan"],
|
|
19937
|
-
experiment_plan: ["experiment"],
|
|
19938
|
-
experiment: ["experiment_run"],
|
|
19939
|
-
experiment_run: ["learning"]
|
|
19941
|
+
hypothesis: [req("experiment_plan")],
|
|
19942
|
+
experiment_plan: [req("experiment"), req("experiment_run")],
|
|
19943
|
+
experiment: [opt("experiment_run"), opt("learning")],
|
|
19944
|
+
experiment_run: [opt("learning")]
|
|
19940
19945
|
},
|
|
19941
19946
|
natural_depth: 5
|
|
19942
19947
|
},
|
|
19943
19948
|
{
|
|
19944
19949
|
id: "strategy",
|
|
19945
19950
|
label: "Strategy cascade",
|
|
19946
|
-
description: "Vision and mission into the strategic themes (bets), the initiatives that pursue them, and the outcomes they drive. Themes
|
|
19951
|
+
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.",
|
|
19947
19952
|
anchor_type: "vision",
|
|
19948
19953
|
fallback_anchors: ["product", "strategic_theme"],
|
|
19949
19954
|
child_map: {
|
|
19950
|
-
vision: ["mission", "strategic_theme"],
|
|
19951
|
-
|
|
19952
|
-
|
|
19953
|
-
|
|
19954
|
-
|
|
19955
|
+
vision: [opt("mission"), opt("strategic_theme")],
|
|
19956
|
+
mission: [opt("strategic_pillar")],
|
|
19957
|
+
strategic_pillar: [opt("strategic_theme")],
|
|
19958
|
+
product: [opt("strategic_theme")],
|
|
19959
|
+
strategic_theme: [req("initiative")],
|
|
19960
|
+
initiative: [opt("outcome")]
|
|
19955
19961
|
},
|
|
19956
|
-
natural_depth:
|
|
19962
|
+
natural_depth: 5
|
|
19957
19963
|
},
|
|
19958
19964
|
{
|
|
19959
19965
|
id: "feature_areas",
|
|
@@ -19962,9 +19968,26 @@ var UPG_TREE_PATTERNS = [
|
|
|
19962
19968
|
anchor_type: "feature_area",
|
|
19963
19969
|
fallback_anchors: ["feature"],
|
|
19964
19970
|
child_map: {
|
|
19965
|
-
feature_area: ["feature"]
|
|
19971
|
+
feature_area: [opt("feature")]
|
|
19966
19972
|
},
|
|
19967
19973
|
natural_depth: 2
|
|
19974
|
+
},
|
|
19975
|
+
{
|
|
19976
|
+
id: "delivery",
|
|
19977
|
+
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.",
|
|
19979
|
+
anchor_type: "roadmap",
|
|
19980
|
+
fallback_anchors: ["product", "release"],
|
|
19981
|
+
child_map: {
|
|
19982
|
+
product: [opt("roadmap"), opt("release")],
|
|
19983
|
+
roadmap: [opt("roadmap_theme"), opt("roadmap_item"), opt("release")],
|
|
19984
|
+
roadmap_theme: [opt("feature")],
|
|
19985
|
+
roadmap_item: [opt("feature")],
|
|
19986
|
+
release: [opt("feature")],
|
|
19987
|
+
feature: [opt("epic")],
|
|
19988
|
+
epic: [opt("user_story")]
|
|
19989
|
+
},
|
|
19990
|
+
natural_depth: 5
|
|
19968
19991
|
}
|
|
19969
19992
|
];
|
|
19970
19993
|
var UPG_TREE_PATTERNS_BY_ID = Object.fromEntries(
|
|
@@ -28277,7 +28300,7 @@ function isCanonical(text) {
|
|
|
28277
28300
|
}
|
|
28278
28301
|
|
|
28279
28302
|
// src/index.ts
|
|
28280
|
-
var UPG_VERSION = "0.9.
|
|
28303
|
+
var UPG_VERSION = "0.9.17";
|
|
28281
28304
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
28282
28305
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
28283
28306
|
var UPG_TYPES = getTypes();
|