@unified-product-graph/core 0.9.5 → 0.9.6
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 +23 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +22 -3
- 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.6] - 2026-06-10
|
|
11
|
+
|
|
12
|
+
**Canonical shared-entity registry + drift detection (canonical-registry initiative, Phases 2–3).** The core of the registry: a portfolio can now define a shared entity ONCE and have every product's local copy link to it as a canonical instance, with drift surfaced automatically. Decision: `2026-06-10-canonical-shared-entity-registry.md`.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **`instance_of` cross-edge** (9th member of `UPG_CROSS_EDGE_TYPES`): a directed *product entity → canonical (registry) entity* relationship — "this product node is an instance of that shared, authoritative node." Endpoints must share a type (a `persona` instance_of a `persona`); the target is addressed as `registry/{node_id}`. Distinct from the symmetric `shares_*` peer edges and coexists with them (canonical-to-instance vs peer-equivalence; neither deprecates the other). Unlocks rollup ("every Developer instance and its per-surface jobs"), cross-instance diff, and drift detection.
|
|
16
|
+
- **`registry` section on the portfolio document** (`UPGPortfolioDocument.registry`, shape `UPGRegistry`): the shared-vocabulary tier that sits above products. Canonical entities are normal `UPGBaseNode`s (no new type, no flag — canonical-ness is conferred by living in the registry). Optional and additive: portfolios without a registry stay valid and byte-identical; an empty registry is omitted on serialise. `REGISTRY_PRODUCT_ID = 'registry'` is the reserved pseudo product-id used to qualify registry references; product creation rejects it.
|
|
17
|
+
- (SDK) `UPGPortfolioStore` registry accessors: `getRegistry` / `ensureRegistry` / `addRegistryNode` / `getRegistryNode` / `listRegistryNodes(type?)` / `removeRegistryNode`. The canonical serialiser round-trips the registry section (write + normalise).
|
|
18
|
+
- (MCP server) three local registry tools: **`define_canonical_entity`** (create a canonical node in the registry), **`register_instance`** (create an `instance_of` edge from a product node to a canonical, enforcing the same-type constraint and the `registry/` target), and **`list_registry`** (list canonical entities, optionally with their instances). Tool surface 110 → 113. Local-only (portfolio.upg is a workspace concept; no cloud analogue).
|
|
19
|
+
- (MCP server) **registry drift detection in `portfolio_validate`** (Phase 3): flags an `instance_of` edge whose target is missing from the registry, whose endpoints disagree on type, or whose instance title diverges from its canonical (off-canon rename). The payoff of the registry — a renamed-off-canon instance is now *detectable*, not silent.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- `create_cross_product_edge` and `batch_create_cross_product_edges` reject `instance_of`, directing callers to `register_instance` (single enforcement path for the registry's same-type + `registry/` target rules). `list_cross_edge_types` now reports nine types.
|
|
23
|
+
|
|
24
|
+
### Notes
|
|
25
|
+
- Phase 4 (`primitive` / `standard` type for foundational shared tech) remains deferred pending a coverage pass against existing `dependency` / `capability`; it must not block the registry core. No entity / domain / edge-catalogue count change (cross-edges are a separate union from `UPG_EDGE_CATALOG`).
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
10
29
|
## [0.9.5] - 2026-06-10
|
|
11
30
|
|
|
12
31
|
**Persona `audience_role` (canonical-registry initiative, Phase 1).** A small, independent property change — the first cut of the canonical shared-entity registry initiative. Decision: `2026-06-10-canonical-shared-entity-registry.md`.
|
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
PRODUCT_DELIVERY_PLAYBOOK: () => PRODUCT_DELIVERY_PLAYBOOK,
|
|
36
36
|
PROPERTY_SCALE_MAP: () => PROPERTY_SCALE_MAP,
|
|
37
37
|
REFLECT_MODES: () => REFLECT_MODES,
|
|
38
|
+
REGISTRY_PRODUCT_ID: () => REGISTRY_PRODUCT_ID,
|
|
38
39
|
STRATEGY_OUTCOMES_PLAYBOOK: () => STRATEGY_OUTCOMES_PLAYBOOK,
|
|
39
40
|
UPG_ACTIVE_TYPES: () => UPG_ACTIVE_TYPES,
|
|
40
41
|
UPG_ANTI_PATTERNS: () => UPG_ANTI_PATTERNS,
|
|
@@ -2968,8 +2969,10 @@ var UPG_CROSS_EDGE_TYPES = [
|
|
|
2968
2969
|
"cannibalises",
|
|
2969
2970
|
"succeeds",
|
|
2970
2971
|
"hosts",
|
|
2971
|
-
"contributes_to"
|
|
2972
|
+
"contributes_to",
|
|
2973
|
+
"instance_of"
|
|
2972
2974
|
];
|
|
2975
|
+
var REGISTRY_PRODUCT_ID = "registry";
|
|
2973
2976
|
|
|
2974
2977
|
// src/registry/entity-type-resolution.ts
|
|
2975
2978
|
var TYPES = getTypes();
|
|
@@ -26105,7 +26108,16 @@ function portfolioBody(doc) {
|
|
|
26105
26108
|
product_areas: [...doc.product_areas ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((a) => orderedObject(a, ["id", "title", "description", "parent_area_id", "strategic_priority", "products"], { forceKeys: ["id", "title"] })),
|
|
26106
26109
|
portfolios: [...doc.portfolios ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((p) => orderedObject(p, ["id", "title", "description", "parent_portfolio_id", "hierarchy_model", "products"], { forceKeys: ["id", "title"] })),
|
|
26107
26110
|
products,
|
|
26108
|
-
cross_edges: sortEdges(doc.cross_edges ?? []).map(canonicalCrossEdge)
|
|
26111
|
+
cross_edges: sortEdges(doc.cross_edges ?? []).map(canonicalCrossEdge),
|
|
26112
|
+
// Registry tier (shared vocabulary). Emitted only when non-empty so existing
|
|
26113
|
+
// portfolio files without a registry stay byte-identical. Canonical entities
|
|
26114
|
+
// are normal nodes, serialised with the same node/edge canonical rules.
|
|
26115
|
+
...doc.registry && doc.registry.nodes.length > 0 ? {
|
|
26116
|
+
registry: {
|
|
26117
|
+
nodes: sortNodes(doc.registry.nodes).map(canonicalNode),
|
|
26118
|
+
...doc.registry.edges && doc.registry.edges.length > 0 ? { edges: sortEdges(doc.registry.edges).map(canonicalEdge) } : {}
|
|
26119
|
+
}
|
|
26120
|
+
} : {}
|
|
26109
26121
|
};
|
|
26110
26122
|
}
|
|
26111
26123
|
function computeBodyChecksum(doc) {
|
|
@@ -26205,6 +26217,13 @@ function normalizeDocument(obj) {
|
|
|
26205
26217
|
})),
|
|
26206
26218
|
cross_edges: raw.cross_edges ?? []
|
|
26207
26219
|
};
|
|
26220
|
+
const rawRegistry = raw.registry;
|
|
26221
|
+
if (rawRegistry && Array.isArray(rawRegistry.nodes)) {
|
|
26222
|
+
out2.registry = {
|
|
26223
|
+
nodes: rawRegistry.nodes.map((n) => repairNodeDrift(n)),
|
|
26224
|
+
...Array.isArray(rawRegistry.edges) ? { edges: rawRegistry.edges } : {}
|
|
26225
|
+
};
|
|
26226
|
+
}
|
|
26208
26227
|
return out2;
|
|
26209
26228
|
}
|
|
26210
26229
|
const out = {
|
|
@@ -26227,7 +26246,7 @@ function isCanonical(text) {
|
|
|
26227
26246
|
}
|
|
26228
26247
|
|
|
26229
26248
|
// src/index.ts
|
|
26230
|
-
var UPG_VERSION = "0.9.
|
|
26249
|
+
var UPG_VERSION = "0.9.6";
|
|
26231
26250
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
26232
26251
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
26233
26252
|
var UPG_TYPES = getTypes();
|
|
@@ -26307,6 +26326,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
26307
26326
|
PRODUCT_DELIVERY_PLAYBOOK,
|
|
26308
26327
|
PROPERTY_SCALE_MAP,
|
|
26309
26328
|
REFLECT_MODES,
|
|
26329
|
+
REGISTRY_PRODUCT_ID,
|
|
26310
26330
|
STRATEGY_OUTCOMES_PLAYBOOK,
|
|
26311
26331
|
UPG_ACTIVE_TYPES,
|
|
26312
26332
|
UPG_ANTI_PATTERNS,
|