@unified-product-graph/core 0.10.8 → 0.11.0
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 +14 -0
- package/dist/index.cjs +15 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -3
- package/dist/index.d.ts +42 -3
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.11.0] - 2026-06-14
|
|
11
|
+
|
|
12
|
+
**Self-documenting competitive history: a competitor's classification change records itself.** The 0.10.x tier classifies competitors against axes and values and carries `confidence` / `assessed_on`, but a re-assessment overwrites the prior value — there was no way to ask *what moved*. This release makes the classification landscape remember its own changes. A minor bump: it widens the `competitor_signal.signal_type` enum (union widening), which spec policy treats as a minor; additive, so old graphs still parse.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **`reclassification` shape on `competitor_signal`.** The `signal_type` enum gains `reclassification`, and the type carries four optional transition properties — `competitor` (the classify-edge source, identifying subject + product), `axis`, `from_value`, `to_value`. A reclassification is one more kind of the dated competitor move the entity already models.
|
|
16
|
+
- **Append-only `signals[]` collection on the portfolio document.** A portfolio-scoped, optional, additive collection holding the classification-history stream. Kept here rather than in `registry.nodes` (so it never pollutes the canonical-vocabulary tier or the landscape/tree reads) and rather than in a product graph (so the auto-emit stays atomic with the portfolio cross-edge write). Round-trips through the canonical serializer; portfolios without it stay byte-identical.
|
|
17
|
+
- **`diff_classification` (new tool, 128 total; local-only).** `diff_classification({ product?, competitor?, since? })` reads the reclassification history and projects each move (`from_value` to `to_value` on an axis) with resolved titles, newest first. "Did Sitecore move from integrated to agentic since last quarter" is one call. Pairs with the 0.10.8 freshness filter (which decides *when* to re-assess); this surfaces *what* changed.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **Classify writes auto-record a move.** When `create_cross_product_edge` / `batch_create_cross_product_edges` create a classify edge that supersedes a sibling classification of the same competitor on the *same axis* (resolved via registry edge or `axis:` tag), the portfolio store appends a `reclassification` signal — completeness by construction, no caller discipline. Record-only and non-destructive: the superseded edge is kept (a `categorical` axis may legitimately carry several values), so current-state reads are untouched. First-time classifications are not moves; unaxed values are skipped (a single-axis move cannot be proven); an identical transition is logged at most once.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
10
24
|
## [0.10.8] - 2026-06-14
|
|
11
25
|
|
|
12
26
|
**Operational tooling for the property layer: coverage audit, write pre-flight, freshness query.** The 218-edge confidence backfill (0.10.6) needed five manual motions the MCP did not expose, mostly `jq` over `portfolio.upg`. This release lands the three near-term ones from Data's tooling-gaps brief. Read-path plus a non-mutating write flag; no schema or data migration.
|
package/dist/index.cjs
CHANGED
|
@@ -9949,9 +9949,13 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
9949
9949
|
// CompetitorSignalProperties: Competitor signal: a dated competitor move mapped onto our portfolio.
|
|
9950
9950
|
competitor_signal: {
|
|
9951
9951
|
observed_at: { type: "string", description: 'ISO date-time the move was observed. @example "2026-06-10"' },
|
|
9952
|
-
signal_type: { type: "string", enum: ["feature_launch", "pricing_change", "acquisition", "partnership", "market_entry"], description: "Kind of move. `feature_launch` = shipped a feature. `pricing_change` = a plan or tier change. `acquisition` / `partnership` / `market_entry` = strategic moves." },
|
|
9952
|
+
signal_type: { type: "string", enum: ["feature_launch", "pricing_change", "acquisition", "partnership", "market_entry", "reclassification"], description: "Kind of move. `feature_launch` = shipped a feature. `pricing_change` = a plan or tier change. `acquisition` / `partnership` / `market_entry` = strategic moves. `reclassification` = the competitor moved between classification cells on an axis (e.g. ai_integrated to ai_agentic); auto-emitted at the classify-write chokepoint and carries `axis` / `from_value` / `to_value` / `competitor`." },
|
|
9953
9953
|
summary: { type: "string", description: "One-line factual summary of the move (what shipped, not marketing copy)." },
|
|
9954
9954
|
impact: { type: "string", enum: ["high", "medium", "low"], description: "Expected impact on our position." },
|
|
9955
|
+
competitor: { type: "string", description: "Reclassification only. The qualified id of the competitor that moved, as the classify cross-edge source (e.g. `p_rival/n_acme`). Identifies both the subject and its owning product, so `diff_classification({ product })` can filter the history stream." },
|
|
9956
|
+
axis: { type: "string", description: "Reclassification only. The `classification_axis` id the move is on (e.g. `ca_ai_maturity`). Mirrors the axis the superseded and new classify edges share." },
|
|
9957
|
+
from_value: { type: "string", description: "Reclassification only. The prior `classification_value` id the competitor was classified as before this move (the superseded cell). Absent for a first-time classification (nothing was superseded)." },
|
|
9958
|
+
to_value: { type: "string", description: "Reclassification only. The new `classification_value` id the competitor is classified as after this move (the cell the new classify edge points at)." },
|
|
9955
9959
|
last_updated: { type: "string", description: 'Provenance: ISO date-time this record was last observed or refreshed. @example "2026-06-13"' },
|
|
9956
9960
|
source: { type: "string", description: 'Provenance: where this was observed. A changelog, pricing, or docs URL, an analyst report, or a research note. @example "https://www.contentful.com/changelog/"' },
|
|
9957
9961
|
confidence: {
|
|
@@ -28603,7 +28607,11 @@ function portfolioBody(doc) {
|
|
|
28603
28607
|
nodes: sortNodes(doc.registry.nodes).map(canonicalNode),
|
|
28604
28608
|
...doc.registry.edges && doc.registry.edges.length > 0 ? { edges: sortEdges(doc.registry.edges).map(canonicalEdge) } : {}
|
|
28605
28609
|
}
|
|
28606
|
-
} : {}
|
|
28610
|
+
} : {},
|
|
28611
|
+
// Append-only classification-history stream (0.11.0). Emitted only when
|
|
28612
|
+
// non-empty so existing portfolio files without it stay byte-identical.
|
|
28613
|
+
// Each entry is a competitor_signal node (serialised with the node rules).
|
|
28614
|
+
...doc.signals && doc.signals.length > 0 ? { signals: sortNodes(doc.signals).map(canonicalNode) } : {}
|
|
28607
28615
|
};
|
|
28608
28616
|
}
|
|
28609
28617
|
function computeBodyChecksum(doc) {
|
|
@@ -28719,6 +28727,10 @@ function normalizeDocument(obj) {
|
|
|
28719
28727
|
...Array.isArray(rawRegistry.edges) ? { edges: rawRegistry.edges } : {}
|
|
28720
28728
|
};
|
|
28721
28729
|
}
|
|
28730
|
+
const rawSignals = raw.signals;
|
|
28731
|
+
if (Array.isArray(rawSignals) && rawSignals.length > 0) {
|
|
28732
|
+
out2.signals = rawSignals.map((n) => repairNodeDrift(n));
|
|
28733
|
+
}
|
|
28722
28734
|
return out2;
|
|
28723
28735
|
}
|
|
28724
28736
|
const out = {
|
|
@@ -28743,7 +28755,7 @@ function isCanonical(text) {
|
|
|
28743
28755
|
}
|
|
28744
28756
|
|
|
28745
28757
|
// src/index.ts
|
|
28746
|
-
var UPG_VERSION = "0.
|
|
28758
|
+
var UPG_VERSION = "0.11.0";
|
|
28747
28759
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
28748
28760
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
28749
28761
|
var UPG_TYPES = getTypes();
|