@unified-product-graph/core 0.9.9 → 0.9.10
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 +19 -0
- package/dist/index.cjs +44 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -2
- package/dist/index.d.ts +19 -2
- package/dist/index.js +43 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.9.10] - 2026-06-10
|
|
11
|
+
|
|
12
|
+
**Tooling DX + a born-valid product (batch 6, part 1) + a release-test gate.** Polish from a real multi-graph portfolio session: parity and discoverability fixes, one model correction, and — the headline — the release train now runs the unit suites, after they were found to have been silently red across a release.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **Product `properties.stage` is canonical.** The stale `UPG_PROPERTY_MIGRATIONS['0.2.13']` lift of a product node's `properties.stage` to top-level `status` is retired: it contradicted the UPG-654/661 convention the whole runtime uses, so a freshly-created product read as `property_drift` against its own validator. A new product is now born valid. Legacy values are still normalised on read by `coerceProductStage`. Decision: `2026-06-10-product-stage-properties-canonical.md`.
|
|
16
|
+
- **`get_nodes` resolves cross-product ids.** A qualified `{product_id}/{node_id}` (the form `list_registry` / `export_edges` / cross-edges return) now reads that product's graph (read-only for non-active products), so a connective pass fetches node content across the portfolio without a `switch_product` sweep. Results carry a `product_id`.
|
|
17
|
+
- **`create_edge` / `batch_create_edges` accept the `p_` product-header** where the in-graph product node is expected, resolving it to the `type:"product"` node (older graphs carry a distinct `n_…` product node).
|
|
18
|
+
- **`did_you_mean` for a mistyped edge type** now falls back to the closest catalogue key by name (edit distance) when the endpoints don't resolve to a canonical pair — so a typo always gets a concrete suggestion.
|
|
19
|
+
- **`batch_create_cross_product_edges` reaches enum parity** with `create_cross_product_edge` (both now derive their type list from one spec source, so the batch tool can no longer lag a release behind — it had been missing `rolls_up_to`).
|
|
20
|
+
- **`batch_register_instance` echoes** `aliased` / `alias_updated` per result, so a bulk sanction pass is visible without a second `portfolio_validate`.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **Release-test gate.** The publish train now runs the train packages' unit suites (`gate:tests`) as a hard pre-flight; a red test aborts the train. This closes the gap that let a prior release ship with several silently-red suites — including import adapters that emitted edge types a rename had retired (now corrected), stale compiled test copies, and fixtures referencing retired edges.
|
|
24
|
+
|
|
25
|
+
No new tools (118) and no entity / domain / edge-catalogue count change.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
10
29
|
## [0.9.9] - 2026-06-10
|
|
11
30
|
|
|
12
31
|
**Structural-integrity forks: validation, AI-prompt, and journey models corrected; duplicate edges collapsed.** A spec-model cut that fixes several long-standing structural mismodels and retires shadow edges. Every renamed or retired edge ships a dual-read migration (`UPG_EDGE_MIGRATIONS['0.9.9']`), so stored graphs upgrade automatically on load. No new tools (118) and no entity-count change; the within-product edge catalogue settles at 975 after the duplicate collapse.
|
package/dist/index.cjs
CHANGED
|
@@ -182,6 +182,7 @@ __export(index_exports, {
|
|
|
182
182
|
resolveEntityType: () => resolveEntityType,
|
|
183
183
|
resolveLabel: () => resolveLabel,
|
|
184
184
|
resolveSlugCollision: () => resolveSlugCollision,
|
|
185
|
+
ringOrderedDomainIds: () => ringOrderedDomainIds,
|
|
185
186
|
rotateSlug: () => rotateSlug,
|
|
186
187
|
serializeCanonical: () => serializeCanonical,
|
|
187
188
|
validateProductStageStrict: () => validateProductStageStrict,
|
|
@@ -7666,22 +7667,18 @@ var UPG_PROPERTY_MIGRATIONS = {
|
|
|
7666
7667
|
// graphs typically carry only one of the two; the conflict path is
|
|
7667
7668
|
// theoretical but documented for predictability.
|
|
7668
7669
|
"0.2.13": [
|
|
7669
|
-
// 1.
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
// UPGProductStage values and need no remap when surfaced inline.
|
|
7682
|
-
},
|
|
7683
|
-
reason: 'lifecycle phase belongs in top-level `status` per UPGBaseNode contract, not in `properties`. Pre-canonical "idea" alias mapped to canonical `concept` (UPGProductStage[0]).'
|
|
7684
|
-
},
|
|
7670
|
+
// 1. (RETIRED in 0.9.10, batch-6 #33) The product `properties.stage` →
|
|
7671
|
+
// top-level `status` lift is superseded by UPG-654/661, which
|
|
7672
|
+
// re-canonicalised a product node's lifecycle stage to
|
|
7673
|
+
// `properties.stage` — a distinct 9-phase `UPGProductStage` axis,
|
|
7674
|
+
// separate from the generic top-level `status` other entities use. The
|
|
7675
|
+
// entire runtime (create_product, get_graph_digest, get_product_context,
|
|
7676
|
+
// completeness, header-sync) reads and writes `properties.stage`, so
|
|
7677
|
+
// lifting it to `status` made a freshly-created product read as
|
|
7678
|
+
// `property_drift` against its own validator (a tool emitting drift its
|
|
7679
|
+
// validator immediately flags). Legacy "idea"-style values are still
|
|
7680
|
+
// normalised on READ by `coerceProductStage`, so retiring the lift loses
|
|
7681
|
+
// no cleanup. Decision: 2026-06-10-product-stage-properties-canonical.md.
|
|
7685
7682
|
// 2. Rename top-level lifecycle_status → status (with value remap).
|
|
7686
7683
|
{
|
|
7687
7684
|
kind: "rename_top_level",
|
|
@@ -20474,19 +20471,23 @@ var UPG_DOMAIN_RINGS = [
|
|
|
20474
20471
|
id: "define",
|
|
20475
20472
|
label: "Define",
|
|
20476
20473
|
description: "What are we building?",
|
|
20477
|
-
|
|
20474
|
+
// `business_model` lives in the `define` ring (foundational "what are we
|
|
20475
|
+
// building & how it sustains"), not in `grow`. This is an intentional
|
|
20476
|
+
// ring-vs-region divergence: UPG_REGIONS still groups business_model under
|
|
20477
|
+
// `business_gtm_growth` (region membership is a separately-tracked decision).
|
|
20478
|
+
domain_ids: ["strategy", "product_spec", "business_model", "ux_design", "design_system", "brand", "legal"]
|
|
20478
20479
|
},
|
|
20479
20480
|
{
|
|
20480
20481
|
id: "build",
|
|
20481
20482
|
label: "Build",
|
|
20482
20483
|
description: "How do we construct it?",
|
|
20483
|
-
domain_ids: ["engineering", "
|
|
20484
|
+
domain_ids: ["engineering", "ai", "automation", "data_analytics", "testing", "devops", "security", "accessibility"]
|
|
20484
20485
|
},
|
|
20485
20486
|
{
|
|
20486
20487
|
id: "grow",
|
|
20487
20488
|
label: "Grow",
|
|
20488
20489
|
description: "How do we make money?",
|
|
20489
|
-
domain_ids: ["
|
|
20490
|
+
domain_ids: ["pricing", "go_to_market", "sales", "marketing", "growth"]
|
|
20490
20491
|
},
|
|
20491
20492
|
{
|
|
20492
20493
|
id: "operate",
|
|
@@ -20498,9 +20499,27 @@ var UPG_DOMAIN_RINGS = [
|
|
|
20498
20499
|
id: "extend",
|
|
20499
20500
|
label: "Extend",
|
|
20500
20501
|
description: "How do we scale?",
|
|
20501
|
-
domain_ids: ["team_org", "program_mgmt", "
|
|
20502
|
+
domain_ids: ["team_org", "program_mgmt", "ecosystem", "localisation", "compliance"]
|
|
20502
20503
|
}
|
|
20503
20504
|
];
|
|
20505
|
+
function assertRingsPartitionDomains() {
|
|
20506
|
+
const ringDomainIds = UPG_DOMAIN_RINGS.flatMap((r) => [...r.domain_ids]);
|
|
20507
|
+
const canonical = new Set(UPG_DOMAINS.map((d) => d.id));
|
|
20508
|
+
const ringSet = new Set(ringDomainIds);
|
|
20509
|
+
const duplicates = ringDomainIds.filter((id, i) => ringDomainIds.indexOf(id) !== i);
|
|
20510
|
+
const unknown = [...ringSet].filter((id) => !canonical.has(id));
|
|
20511
|
+
const unassigned = [...canonical].filter((id) => !ringSet.has(id));
|
|
20512
|
+
if (duplicates.length || unknown.length || unassigned.length) {
|
|
20513
|
+
const parts = [];
|
|
20514
|
+
if (unassigned.length) parts.push(`domains in no ring: [${unassigned.join(", ")}]`);
|
|
20515
|
+
if (unknown.length) parts.push(`ring domains not in UPG_DOMAINS: [${unknown.join(", ")}]`);
|
|
20516
|
+
if (duplicates.length) parts.push(`domains in multiple rings: [${[...new Set(duplicates)].join(", ")}]`);
|
|
20517
|
+
throw new Error(
|
|
20518
|
+
`UPG_DOMAIN_RINGS must partition UPG_DOMAINS exactly: ${parts.join("; ")}. Slot every domain into exactly one ring in domain-rings.ts.`
|
|
20519
|
+
);
|
|
20520
|
+
}
|
|
20521
|
+
}
|
|
20522
|
+
assertRingsPartitionDomains();
|
|
20504
20523
|
function getRingForDomain(domainId) {
|
|
20505
20524
|
return UPG_DOMAIN_RINGS.find((ring) => ring.domain_ids.includes(domainId));
|
|
20506
20525
|
}
|
|
@@ -20508,6 +20527,9 @@ function getDomainsInRing(ringId) {
|
|
|
20508
20527
|
const ring = UPG_DOMAIN_RINGS.find((r) => r.id === ringId);
|
|
20509
20528
|
return ring ? [...ring.domain_ids] : [];
|
|
20510
20529
|
}
|
|
20530
|
+
function ringOrderedDomainIds() {
|
|
20531
|
+
return UPG_DOMAIN_RINGS.flatMap((ring) => [...ring.domain_ids]);
|
|
20532
|
+
}
|
|
20511
20533
|
|
|
20512
20534
|
// src/intelligence/domain-guides.ts
|
|
20513
20535
|
var USER_GUIDE = {
|
|
@@ -27174,7 +27196,7 @@ function isCanonical(text) {
|
|
|
27174
27196
|
}
|
|
27175
27197
|
|
|
27176
27198
|
// src/index.ts
|
|
27177
|
-
var UPG_VERSION = "0.9.
|
|
27199
|
+
var UPG_VERSION = "0.9.10";
|
|
27178
27200
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
27179
27201
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
27180
27202
|
var UPG_TYPES = getTypes();
|
|
@@ -27401,6 +27423,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
27401
27423
|
resolveEntityType,
|
|
27402
27424
|
resolveLabel,
|
|
27403
27425
|
resolveSlugCollision,
|
|
27426
|
+
ringOrderedDomainIds,
|
|
27404
27427
|
rotateSlug,
|
|
27405
27428
|
serializeCanonical,
|
|
27406
27429
|
validateProductStageStrict,
|