@unified-product-graph/core 0.9.1 → 0.9.3
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 +35 -0
- package/dist/index.cjs +88 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -15
- package/dist/index.d.ts +60 -15
- package/dist/index.js +88 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,41 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.9.3] - 2026-06-09
|
|
11
|
+
|
|
12
|
+
**Multi-product authoring safety pack (batch-4, part 1).** No core spec changes — a DX + safety release for the MCP server and SDK, surfaced from bringing an entire product org to structural-spine parity through one single-active-product server (~850 nodes / ~1,259 edges across 12 graphs). Every change is additive and backward-compatible. Decision: `2026-06-09-multiproduct-authoring-safety.md`.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- (MCP server + SDK) **`validate_only` dry-run** on `batch_create_nodes` and `batch_create_edges` (batch-4 #15): run the full validation pass (types, status, refs, edge directions/pairs) and report `{ valid, errors, would_create_* }` WITHOUT writing. Errors now ACCUMULATE across the whole batch, so an agent fixes every bad item in one pass instead of losing the batch to the first. Folds in batch-4 #21 (status-vocabulary footguns are caught pre-commit).
|
|
16
|
+
- (MCP server + SDK) **`ref` aliases** for `batch_create_nodes` (batch-4 #16): a node may declare a batch-local `ref`, referenceable from `parent_ref` / `edges[].from_ref` / `to_ref` in place of a positional `$N` — removing the index-counting that was the #1 cause of failed batches. A stray `$`-prefixed token that resolves to neither a valid `$N` nor a declared alias is now rejected explicitly (was silently treated as a node id); failures echo the alias `ref_map`.
|
|
17
|
+
- (MCP server) **`portfolio_validate`** (batch-4 #19): run `validate_graph` across every product in scope in one call — the audit counterpart to `portfolio_digest`. Replaces the `switch_product` + `validate_graph` round-trip per product; reuses the single-product code path verbatim so per-product verdicts can never diverge. Tool surface 108 → 109. Local-only (no cloud analogue).
|
|
18
|
+
- (MCP server) **`expect_product` guard + `active_product` echo** on active-product writes (batch-4 #20): every write to the active product's graph echoes `active_product: { id, title }`, and accepts an optional `expect_product` arg that aborts before writing if the active product isn't the one named — cheap insurance against a forgotten `switch_product` writing into the wrong graph in a multi-product session.
|
|
19
|
+
|
|
20
|
+
### Notes
|
|
21
|
+
- Cross-product WRITE (content propagation into product internals) and the structure-template / `clone_structure` operation (batch-4 #17), pre-commit anti-pattern preview (#18), and coverage profiles (#22) are scoped for the 0.9.4 cut.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## [0.9.2] - 2026-06-09
|
|
26
|
+
|
|
27
|
+
**Journey-model disambiguation (UPG-663).** The four Experience-Design journey types (`user_journey`, `journey_phase`, `journey_step`, `journey_action`) had a coherent concept but broken wiring: there was no deterministic answer to "what are the steps of this journey?". A `journey_phase` is now a temporal band that **spans** steps, not a container that **owns** them, mirroring the marketing precedent `customer_journey_stage_spans_journey_step`. Steps stay owned by `user_journey` (the stable 0.1.0 spine), so each step has exactly one containment parent and the journey renders one canonical step list. Additive plus one edge rename (dual-read via `UPG_EDGE_MIGRATIONS['0.9.2']`). Decision: `2026-06-09-journey-model-disambiguation.md`.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- `journey_action_surfaces_need` and `journey_action_realised_by_feature` edges: `journey_action` previously had zero outbound edges despite carrying `pain_score` / `opportunity_score` "to drive opportunity discovery". Opportunity discovery now routes through `need` (which reaches `opportunity` via `opportunity_addresses_need`), mirroring `journey_step_reveals_need` one level deeper.
|
|
31
|
+
- `JourneyStepProperties.step_order` and `JourneyActionProperties.action_order`: scalar ordering, completing a single `*_order` convention across phase / step / action (matching the existing `phase_order` and `customer_journey_stage.stage_order`). `journey_step_precedes_journey_step` remains the explicit-chain option for branching journeys.
|
|
32
|
+
- `journey-phases-without-canonical-steps` curated anti-pattern (high severity): fires when phases span steps but no journey owns them via `user_journey_contains_journey_step`, so `validate_graph` surfaces a journey with no canonical step spine.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- **`journey_phase_has_step` → `journey_phase_spans_journey_step`** (paired, via `UPG_EDGE_MIGRATIONS['0.9.2']`): the phase-to-step edge becomes non-owning (verb `has_step` to `spans`, classification `hierarchy` to `cross-domain`). `journey_phase` is removed as a containment parent of `journey_step` in the hierarchy; `user_journey` keeps `journey_step` (owned spine) and `journey_phase` (band overlay).
|
|
36
|
+
- `properties/domains/ux-design.ts` JSDoc reconciled to the actual edge names (removed six phantom edges that the spec documented but never shipped).
|
|
37
|
+
- `presentation/labels.ts` alt-label collisions removed: `user_journey` drops `"customer journey"` (collided with `customer_journey_stage`); `journey_step` drops `"touchpoint"` and `"journey phase"` (distinct entity surfaces).
|
|
38
|
+
- `intelligence/domain-guides.ts` UX creation sequence reordered so `journey_step` precedes its child `journey_action`.
|
|
39
|
+
|
|
40
|
+
### Notes
|
|
41
|
+
- `journey_phase` and `journey_action` remain `proposed` (the v0.2 Experience-Design extension over the stable 0.1.0 journey/step spine); not promoted to `stable` in this pass.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
10
45
|
## [0.9.1] - 2026-06-09
|
|
11
46
|
|
|
12
47
|
**Cross-product read layer + an OKR-alignment edge + a workspace-resolution fix.** One core spec addition (the `contributes_to` cross-product edge), the read counterpart to 0.8.16's portfolio write tier (server tooling), and a `switch_product` bug fix. From batch-3 dogfooding (portfolio strategy reasoning across the full multi-product workspace). All additive; existing files stay valid.
|
package/dist/index.cjs
CHANGED
|
@@ -1484,8 +1484,24 @@ var UPG_EDGE_CATALOG = {
|
|
|
1484
1484
|
journey_step_realised_by_feature: { forward_verb: "realised_by", reverse_verb: "realises", classification: "cross-domain", source_type: "journey_step", target_type: "feature" },
|
|
1485
1485
|
opportunity_improves_user_journey: { forward_verb: "improves", reverse_verb: "improved_by", classification: "cross-domain", source_type: "opportunity", target_type: "user_journey" },
|
|
1486
1486
|
user_journey_passes_through_journey_phase: { forward_verb: "passes_through", reverse_verb: "is_phase_of", classification: "hierarchy", source_type: "user_journey", target_type: "journey_phase" },
|
|
1487
|
-
|
|
1487
|
+
// (since v0.9.2, UPG-663) A journey_phase is a temporal BAND over the
|
|
1488
|
+
// journey's single step timeline, not a container that owns steps. Steps
|
|
1489
|
+
// belong to the journey via `user_journey_contains_journey_step` (the stable
|
|
1490
|
+
// 0.1.0 spine); a phase merely SPANS a range of them. Mirrors the marketing
|
|
1491
|
+
// precedent `customer_journey_stage_spans_journey_step`. Renamed from the
|
|
1492
|
+
// owning `journey_phase_has_step` (hierarchy) so a step has exactly one
|
|
1493
|
+
// containment parent. See UPG_EDGE_MIGRATIONS['0.9.2'].
|
|
1494
|
+
journey_phase_spans_journey_step: { forward_verb: "spans", reverse_verb: "spanned_by", classification: "cross-domain", source_type: "journey_phase", target_type: "journey_step" },
|
|
1488
1495
|
journey_step_has_action: { forward_verb: "has_action", reverse_verb: "is_action_in", classification: "hierarchy", source_type: "journey_step", target_type: "journey_action" },
|
|
1496
|
+
// (since v0.9.2, UPG-663) journey_action outbound edges. Fixes the
|
|
1497
|
+
// discovery dead-end (D2). The finest blueprint layer carries pain_score /
|
|
1498
|
+
// opportunity_score "to drive opportunity discovery" but previously had zero
|
|
1499
|
+
// outbound edges. Opportunity discovery routes through `need` (mirroring
|
|
1500
|
+
// `journey_step_reveals_need`), which already reaches `opportunity` via the
|
|
1501
|
+
// user chain; the action does not link an opportunity directly. The feature
|
|
1502
|
+
// edge mirrors `journey_step_realised_by_feature` one level deeper.
|
|
1503
|
+
journey_action_surfaces_need: { forward_verb: "surfaces", reverse_verb: "surfaced_in", classification: "cross-domain", source_type: "journey_action", target_type: "need" },
|
|
1504
|
+
journey_action_realised_by_feature: { forward_verb: "realised_by", reverse_verb: "realises", classification: "cross-domain", source_type: "journey_action", target_type: "feature" },
|
|
1489
1505
|
// 2.5 UI System Domain
|
|
1490
1506
|
product_systematised_in_design_system: { forward_verb: "systematised_in", reverse_verb: "systematises", classification: "hierarchy", source_type: "product", target_type: "design_system" },
|
|
1491
1507
|
product_built_with_design_component: { forward_verb: "built_with", reverse_verb: "built_for", classification: "hierarchy", source_type: "product", target_type: "design_component" },
|
|
@@ -3243,8 +3259,13 @@ var UPG_VALID_CHILDREN = {
|
|
|
3243
3259
|
// parallel to `observation: ['quote']`. Research-synthesis canon.
|
|
3244
3260
|
// Pairs with `insight_evidenced_by_quote` in the edge catalog.
|
|
3245
3261
|
// ── Design hierarchy ────────────────────────────────────────────────────────
|
|
3262
|
+
// (UPG-663, v0.9.2) A user_journey owns its steps (the stable 0.1.0 spine)
|
|
3263
|
+
// and carries phases as a non-owning band overlay. A journey_phase is NO
|
|
3264
|
+
// LONGER a containment parent of journey_step: it SPANS steps via the
|
|
3265
|
+
// `journey_phase_spans_journey_step` edge (mirroring the marketing
|
|
3266
|
+
// `customer_journey_stage_spans_journey_step` precedent), so each step has
|
|
3267
|
+
// exactly one containment parent and the journey has one canonical step list.
|
|
3246
3268
|
user_journey: ["journey_step", "journey_phase"],
|
|
3247
|
-
journey_phase: ["journey_step"],
|
|
3248
3269
|
journey_step: ["journey_action"],
|
|
3249
3270
|
// v0.5.8 (UPG-528 Part 2d): insights own their evidencing quotes;
|
|
3250
3271
|
// parallel to `observation: ['quote']`. Research-synthesis canon
|
|
@@ -7797,6 +7818,20 @@ function getSplitMigrations(fromVersion, toVersion) {
|
|
|
7797
7818
|
return result;
|
|
7798
7819
|
}
|
|
7799
7820
|
var UPG_EDGE_MIGRATIONS = {
|
|
7821
|
+
"0.9.2": [
|
|
7822
|
+
// (since v0.9.2, UPG-663) Journey-model disambiguation. A journey_phase
|
|
7823
|
+
// is a temporal BAND over the journey's single step timeline, not a
|
|
7824
|
+
// container that owns steps. The owning hierarchy edge
|
|
7825
|
+
// `journey_phase_has_step` is renamed to the non-owning
|
|
7826
|
+
// `journey_phase_spans_journey_step` (mirroring the marketing precedent
|
|
7827
|
+
// `customer_journey_stage_spans_journey_step`). Steps stay owned by the
|
|
7828
|
+
// journey via `user_journey_contains_journey_step` (the stable 0.1.0
|
|
7829
|
+
// spine), so each step keeps a single containment parent and the journey
|
|
7830
|
+
// renders one canonical step list. Endpoints are unchanged
|
|
7831
|
+
// (journey_phase → journey_step); only the edge key and its grammar
|
|
7832
|
+
// (verb has_step → spans, classification hierarchy → cross-domain) change.
|
|
7833
|
+
{ kind: "rename", from: "journey_phase_has_step", to: "journey_phase_spans_journey_step", requires_source_type: "journey_phase", requires_target_type: "journey_step", reason: "Journey-model disambiguation (UPG-663): a phase spans steps, it does not own them. The phase to step edge becomes non-owning (verb has_step to spans), so the step keeps a single containment parent (the journey) and the journey has one canonical step list." }
|
|
7834
|
+
],
|
|
7800
7835
|
"0.9.0": [
|
|
7801
7836
|
// (since v0.9.0, UPG-660) theme → roadmap_theme. The four canonical edges that
|
|
7802
7837
|
// touch the roadmap theme are renamed to the roadmap_theme form. Endpoint guards
|
|
@@ -10295,6 +10330,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
10295
10330
|
},
|
|
10296
10331
|
// JourneyActionProperties: Discrete action at a journey step, classified by service layer.
|
|
10297
10332
|
journey_action: {
|
|
10333
|
+
action_order: { type: "number", description: "Display order of this action within its step (0-indexed). The scalar ordering convention shared with `journey_phase.phase_order` and `journey_step.step_order` (UPG-663). Orders the service-blueprint rows within a single moment." },
|
|
10298
10334
|
layer: { type: "string", enum: ["user", "frontstage", "backstage", "support"], description: "Service layer" },
|
|
10299
10335
|
action_description: { type: "string", description: "Plain-language description. Primary content of the action." },
|
|
10300
10336
|
channel: { type: "string", enum: ["in-app", "email", "web", "mobile", "phone", "in-person", "sms", "social", "other"], description: "Channel or surface. Keeps service-blueprint columns consistent across the journey." },
|
|
@@ -10326,7 +10362,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
10326
10362
|
system: { type: "string", description: "Performing system or service" },
|
|
10327
10363
|
notes: { type: "string", description: "Free-text notes, observations, or follow-up questions" }
|
|
10328
10364
|
},
|
|
10329
|
-
// JourneyPhaseProperties: Phase within a user journey.
|
|
10365
|
+
// JourneyPhaseProperties: Phase within a user journey. A temporal BAND over the journey's step
|
|
10330
10366
|
journey_phase: {
|
|
10331
10367
|
phase_order: { type: "number", description: "Display order within the journey (0-indexed)" },
|
|
10332
10368
|
label: { type: "string", description: 'Short human-readable name. @example "Discovery", "Onboarding", "Activation"' },
|
|
@@ -10337,8 +10373,9 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
10337
10373
|
key_questions: { type: "string[]", description: "Open questions the user asks themselves. Fuel for design and content priorities." },
|
|
10338
10374
|
timeframe: { type: "string", description: 'Typical time window. @example "first 30 seconds", "days 1\u20137", "onboarding week"' }
|
|
10339
10375
|
},
|
|
10340
|
-
// JourneyStepProperties: Single step within a user journey.
|
|
10376
|
+
// JourneyStepProperties: Single step within a user journey. A user-moment on the journey's single
|
|
10341
10377
|
journey_step: {
|
|
10378
|
+
step_order: { type: "number", description: "Display order within the journey's step timeline (0-indexed). The scalar ordering convention shared with `journey_phase.phase_order` and `journey_action.action_order` (UPG-663). For branching journeys, the explicit `journey_step_precedes_journey_step` edge captures the chain." },
|
|
10342
10379
|
touchpoint: { type: "string", description: "Interaction touchpoint" },
|
|
10343
10380
|
channel: { type: "string", description: 'Channel (e.g. "web", "email", "in-store")' },
|
|
10344
10381
|
emotion_score: {
|
|
@@ -18261,8 +18298,13 @@ var STANDARD_LABELS = {
|
|
|
18261
18298
|
interview_guide: { alt_labels: ["discussion guide", "interview script", "moderator guide"] },
|
|
18262
18299
|
survey_response: { alt_labels: ["survey answer", "questionnaire response"] },
|
|
18263
18300
|
// Design layer
|
|
18264
|
-
|
|
18265
|
-
|
|
18301
|
+
// (UPG-663) alt_labels must not collide with other entity types' canonical
|
|
18302
|
+
// names or alt_labels, or the string->type resolver is ambiguous.
|
|
18303
|
+
// Dropped 'customer journey' (collides with customer_journey_stage),
|
|
18304
|
+
// 'touchpoint' and 'journey phase' (both distinct entity surfaces: a
|
|
18305
|
+
// touchpoint is the journey_step.touchpoint property, a phase is journey_phase).
|
|
18306
|
+
user_journey: { alt_labels: ["journey map", "experience map", "journey"] },
|
|
18307
|
+
journey_step: { alt_labels: ["journey moment", "journey stage"] },
|
|
18266
18308
|
design_component: { alt_labels: ["component", "ui component", "design element"] },
|
|
18267
18309
|
design_token: { alt_labels: ["token", "style token", "css variable"] },
|
|
18268
18310
|
wireframe: { alt_labels: ["wireflow", "lo-fi mockup", "skeleton"] },
|
|
@@ -20327,7 +20369,11 @@ var PRODUCT_SPEC_GUIDE = {
|
|
|
20327
20369
|
var UX_DESIGN_GUIDE = {
|
|
20328
20370
|
domain_id: "ux_design",
|
|
20329
20371
|
anchor_entity: "user_journey",
|
|
20330
|
-
|
|
20372
|
+
// (UPG-663) creation order follows containment: a journey_action is a child
|
|
20373
|
+
// of a journey_step, so the step must precede the action. The journey is the
|
|
20374
|
+
// anchor; phases are the band overlay; steps are the timeline; actions
|
|
20375
|
+
// decompose a step into service-blueprint rows.
|
|
20376
|
+
creation_sequence: ["user_journey", "journey_phase", "journey_step", "journey_action", "screen", "screen_state", "user_flow", "wireframe", "prototype", "design_question", "design_concept"],
|
|
20331
20377
|
patterns: [
|
|
20332
20378
|
{
|
|
20333
20379
|
name: "Journey to Screen Flow",
|
|
@@ -23624,6 +23670,40 @@ var UPG_ANTI_PATTERNS = [
|
|
|
23624
23670
|
stages: ["concept", "validation", "build", "beta", "launch", "growth", "mature"],
|
|
23625
23671
|
severity: "medium",
|
|
23626
23672
|
source: { kind: "fundamental" }
|
|
23673
|
+
},
|
|
23674
|
+
// ── Experience-design layer ─────────────────────────────────────────────
|
|
23675
|
+
{
|
|
23676
|
+
id: "journey-phases-without-canonical-steps",
|
|
23677
|
+
name: "Journey phases without a step spine",
|
|
23678
|
+
description: 'The graph has journey phases spanning steps (`journey_phase_spans_journey_step`), but no journey owns its steps via `user_journey_contains_journey_step`. A phase is a band over a step timeline, not a container. When the timeline itself is missing there is no canonical answer to "what are the steps of this journey?". The phase overlay points at steps the journey does not own.',
|
|
23679
|
+
structured_condition: {
|
|
23680
|
+
operator: "and",
|
|
23681
|
+
checks: [
|
|
23682
|
+
{
|
|
23683
|
+
check: {
|
|
23684
|
+
type: "relationship",
|
|
23685
|
+
source_type: "journey_phase",
|
|
23686
|
+
edge_type: "journey_phase_spans_journey_step",
|
|
23687
|
+
target_type: "journey_step",
|
|
23688
|
+
comparison: "exists"
|
|
23689
|
+
}
|
|
23690
|
+
},
|
|
23691
|
+
{
|
|
23692
|
+
check: {
|
|
23693
|
+
type: "relationship",
|
|
23694
|
+
source_type: "user_journey",
|
|
23695
|
+
edge_type: "user_journey_contains_journey_step",
|
|
23696
|
+
target_type: "journey_step",
|
|
23697
|
+
comparison: "not_exists"
|
|
23698
|
+
}
|
|
23699
|
+
}
|
|
23700
|
+
]
|
|
23701
|
+
},
|
|
23702
|
+
why_it_matters: "Steps owned by no journey render a different step list per consumer: the phase overlay sees them, a journey-direct walk does not. The journey has no deterministic step spine to traverse, score, or map to screens.",
|
|
23703
|
+
remediation: "Own every step under its journey with `user_journey_contains_journey_step`, then let phases span ranges of that single timeline via `journey_phase_spans_journey_step`. The phase is a non-owning band overlay, not the step container.",
|
|
23704
|
+
stages: ["concept", "validation", "build", "beta", "launch", "growth"],
|
|
23705
|
+
severity: "high",
|
|
23706
|
+
source: { kind: "fundamental" }
|
|
23627
23707
|
}
|
|
23628
23708
|
];
|
|
23629
23709
|
function getAntiPatternById(id) {
|
|
@@ -26146,7 +26226,7 @@ function isCanonical(text) {
|
|
|
26146
26226
|
}
|
|
26147
26227
|
|
|
26148
26228
|
// src/index.ts
|
|
26149
|
-
var UPG_VERSION = "0.9.
|
|
26229
|
+
var UPG_VERSION = "0.9.3";
|
|
26150
26230
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
26151
26231
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
26152
26232
|
var UPG_TYPES = getTypes();
|