@unified-product-graph/core 0.38.0 → 0.40.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 +35 -0
- package/README.md +1 -1
- package/dist/index.cjs +35 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -3
- package/dist/index.d.ts +39 -3
- package/dist/index.js +35 -4
- 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.40.0] - 2026-09-04
|
|
11
|
+
|
|
12
|
+
**Two gates could not see the thing they were built to catch.** Everything here came out of the 0.39.0 editorial pass rather than a feature request: a stale number that survived several releases in plain sight, and a citation check that had quietly stopped detecting one of its own founding defects. Tooling only; no catalog, schema, or entity surface moves.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **`check:count-drift` now sees claims a formatter has wrapped.** Every claim pattern was written with literal spaces, so a line break between two words of a phrase defeated the match. A "61 cross-product edge types" claim — wrong by ten — sat in a Prettier-wrapped caption through several releases while the identical claim one line long was caught every time. Each top-level literal space is now compiled to `\s+` at load, leaving escapes and character classes alone, so patterns stay readable and line numbers stay accurate. Wrapped Markdown had the same exposure.
|
|
17
|
+
- **`check:editorial` can catch its founding defect again.** The edge-shape test drew its type names from the 324 live types and none of the 38 deprecated ones, at *both* ends of the test — so a citation orphaned by a type rename, which names the old (now deprecated) type by construction, was structurally invisible. `task_implements_story_statement`, one of the three fabrications this check was built for, had stopped being detected when the 0.34.0 tail condition landed, because `story_statement` was deprecated at 0.7.0. Two further miscitations were live in published docs and were found by reading rather than by gate. The deprecated names are now in the list; the false-positive fences the tail condition bought are unchanged and pinned by the selftest.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **`batch_create_nodes` echoes what the caller wrote.** Successful responses now carry each created node's properties (omitted when empty) and, when refs were used, the alias `ref_map`. Both existed only as re-reads before: keying 163 tokens by their CSS variable meant re-reading the file, and `ref_map` was reported on dry-run and on failure but not on success — the call described its ref resolutions when it refused and when it pretended, and went quiet when it did the work.
|
|
22
|
+
|
|
23
|
+
## [0.39.0] - 2026-09-04
|
|
24
|
+
|
|
25
|
+
**A write said "done" while the disk disagreed, and a design system could not point at itself.** Six findings from a second Sanity field brief, written while populating a Studio design layer through roughly two thousand MCP calls across seven graph PRs. One is a correctness bug in the workflow this project's own README recommends; the rest are the design layer's missing vocabulary. Additive throughout.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **Write tools now return only after the file is written.** `store` saves on a 300ms fire-and-forget debounce, so a successful write returned while the file still held the pre-write state: the documented `batch_*` → read-back → `batch_*` loop raced its own writes, and three field scripts read stale state and worked around it with sleeps. Every mutating tool now awaits the flush at the single dispatcher chokepoint, so the guarantee is structural rather than resting on forty handlers each remembering. A flush failure downgrades the response to an error instead of reporting success over an unwritten file.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **`design_token_derives_from_design_token`** (causal, cross-product eligible). The alias tier: primitives (`--gray-100`) that semantic tokens alias (`--background-high: light-dark(var(--gray-100), …)`) that components consume. Measured estate: 163 primitives and 45 aliases whose chain lived in prose. Causal because the alias's value is computed from the primitive's. Makes "which primitives does this component depend on" and "which primitives are dead" traversals.
|
|
34
|
+
- **`feature_area_groups_design_component`** (hierarchy). Component catalogs group by area; 133 components carried an `area:` tag instead. `groups`, not `contains`: the area does not own the component, so this is the non-owning overlay `journey_phase_spans_journey_step` already models.
|
|
35
|
+
- **`validate_graph` reports `property_enum_drift`.** A declared enum was documentation: nothing checked a stored value against it, so an estate wrote `shadow` / `size` / `opacity` into `design_token.category` and saw zero drift, unable to tell whether the enum was strict, extensible, or decorative. Reported, never refused, the `undeclared_property_drift` posture.
|
|
36
|
+
- **`portfolio_validate` reports `registry_file_path_drift`, and `update_product` accepts `file_path`.** A registry entry can point at a path the graph no longer occupies; discovery keeps working because `workspace.json` is authoritative, so nothing surfaced it and nothing repaired it. The report names where the graph actually is; the repair corrects the registry without moving anything, and refuses a path that resolves to nothing.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **`design_component_composes_design_component` is cross-product eligible.** A product graph's shadow component could not point at the design-system primitive it wraps, and eleven wrappers recorded the relationship in descriptions. Widened rather than minting a `_wraps_` twin: a wrap is composition read across a graph boundary, and a second verb for one relationship is a shadow pair.
|
|
41
|
+
- **`design_token.category` speaks the DTCG `$type` vocabulary** alongside its original five values, with the non-one-to-one mapping stated on the property: a `radius` token is a DTCG `dimension`, `motion` is usually a `duration` or `cubicBezier`, `typography` is a composite. Both spellings are accepted, so neither an estate that speaks DTCG nor one that speaks UPG's original five has to translate at the boundary.
|
|
42
|
+
|
|
43
|
+
Catalog: 1092 → 1094 edges (456 hierarchy · 425 cross-domain · 95 causal · 118 semantic); cross-product 69 → 71. No entity types added or removed.
|
|
44
|
+
|
|
10
45
|
## [0.38.0] - 2026-09-03
|
|
11
46
|
|
|
12
47
|
**Cloud-agent hardening: a field brief from a five-repo Cursor Cloud environment found the server fabricating phantom graphs, unverifiable at build time, and unhelpable at setup time.** Seven findings, six accepted (the seventh, filesystem-coupled `code_url` checks, deferred on doctrine); every fix verified live over stdio against the built binaries. Zero catalog/schema surface; minor rather than patch because two behaviors change deliberately.
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
The TypeScript core for the **Unified Product Graph (UPG)**, an open specification that gives product tools a shared vocabulary for entities, edges, and frameworks.
|
|
9
9
|
|
|
10
|
-
324 entity types across 37 domains.
|
|
10
|
+
324 entity types across 37 domains. 1094 typed edges. 53 canonical framework definitions, with more added as each is reviewed.
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
package/dist/index.cjs
CHANGED
|
@@ -1789,6 +1789,15 @@ var UPG_EDGE_CATALOG = {
|
|
|
1789
1789
|
product_categorises_by_roadmap_theme: { forward_verb: "categorises_by", reverse_verb: "categorises", classification: "hierarchy", source_type: "product", target_type: "roadmap_theme" },
|
|
1790
1790
|
feature_area_contains_feature: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "feature_area", target_type: "feature" },
|
|
1791
1791
|
feature_area_contains_feature_area: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "feature_area", target_type: "feature_area" },
|
|
1792
|
+
// (0.39.0, B4) Component catalogs group by area -- a storybook sidebar that
|
|
1793
|
+
// follows the source tree, a visual-coverage report -- and the graph had no
|
|
1794
|
+
// edge for it, so a measured estate tagged 133 components `area:<group>`
|
|
1795
|
+
// instead. `groups`, not `contains`: the area does not OWN the component
|
|
1796
|
+
// (a design_system or product does); it is a grouping view over components
|
|
1797
|
+
// owned elsewhere, the same non-owning relationship `journey_phase_spans_journey_step`
|
|
1798
|
+
// records. Multi-parent GRAMMAR with single per-instance parentage, the
|
|
1799
|
+
// `quote` precedent.
|
|
1800
|
+
feature_area_groups_design_component: { forward_verb: "groups", reverse_verb: "grouped_in", classification: "hierarchy", source_type: "feature_area", target_type: "design_component" },
|
|
1792
1801
|
outcome_delivered_by_feature: { forward_verb: "delivered_by", reverse_verb: "delivers", classification: "cross-domain", source_type: "outcome", target_type: "feature" },
|
|
1793
1802
|
outcome_delivered_via_feature_area: { forward_verb: "delivered_via", reverse_verb: "delivers_for", classification: "cross-domain", source_type: "outcome", target_type: "feature_area" },
|
|
1794
1803
|
feature_decomposed_into_epic: { forward_verb: "decomposed_into", reverse_verb: "implements", classification: "hierarchy", source_type: "feature", target_type: "epic" },
|
|
@@ -2108,10 +2117,32 @@ var UPG_EDGE_CATALOG = {
|
|
|
2108
2117
|
// decision records design choices about the system; not contained by it.
|
|
2109
2118
|
design_system_decided_via_decision: { forward_verb: "decided_via", reverse_verb: "decided_for", classification: "semantic", source_type: "design_system", target_type: "decision" },
|
|
2110
2119
|
design_component_styled_by_design_token: { forward_verb: "styled_by", reverse_verb: "styles", classification: "hierarchy", source_type: "design_component", target_type: "design_token" },
|
|
2120
|
+
// (0.39.0, B2) The alias tier. A modern token system is two or three tiers --
|
|
2121
|
+
// primitives (`--gray-100`) that semantic tokens alias
|
|
2122
|
+
// (`--background-high: light-dark(var(--gray-100), var(--gray-900))`) that
|
|
2123
|
+
// components consume -- and the spec could hold the ends but not the middle,
|
|
2124
|
+
// so a measured estate (163 DTCG primitives, 45 semantic aliases) kept its
|
|
2125
|
+
// alias chain in descriptions. `causal`, not `semantic`: the alias's VALUE is
|
|
2126
|
+
// computed from the primitive's, so the dependency is real and directional.
|
|
2127
|
+
// `cross_product_eligible` because the primitive tier is routinely its own
|
|
2128
|
+
// published package (its own graph) while the semantic tier lives with the
|
|
2129
|
+
// component library. Makes "which primitives does this component ultimately
|
|
2130
|
+
// depend on" and "which primitives are dead" traversals rather than scripts.
|
|
2131
|
+
design_token_derives_from_design_token: { forward_verb: "derives_from", reverse_verb: "derived_into", classification: "causal", source_type: "design_token", target_type: "design_token", cross_product_eligible: true },
|
|
2111
2132
|
design_component_follows_design_pattern: { forward_verb: "follows", reverse_verb: "followed_by", classification: "hierarchy", source_type: "design_component", target_type: "design_pattern" },
|
|
2112
2133
|
design_component_governed_by_design_guideline: { forward_verb: "governed_by", reverse_verb: "governs", classification: "hierarchy", source_type: "design_component", target_type: "design_guideline" },
|
|
2113
2134
|
design_component_specified_by_interaction_spec: { forward_verb: "specified_by", reverse_verb: "specifies", classification: "hierarchy", source_type: "design_component", target_type: "interaction_spec" },
|
|
2114
|
-
|
|
2135
|
+
// (0.39.0, B1) `cross_product_eligible` because a design system and the
|
|
2136
|
+
// product that consumes it are two graphs: a Studio shadow component wrapping
|
|
2137
|
+
// the @sanity/ui primitive it is built on could not be stated, and 11 such
|
|
2138
|
+
// wrappers recorded the relationship in prose instead. Widened rather than
|
|
2139
|
+
// minting `design_component_wraps_design_component`: a wrap IS composition
|
|
2140
|
+
// read across a graph boundary, and a second verb for one relationship is the
|
|
2141
|
+
// shadow-pair shape Pattern D collapses. `design_component` is already
|
|
2142
|
+
// portfolio_shared, and the three sibling component edges
|
|
2143
|
+
// (surface_renders_, screen_renders_, feature_uses_) are already eligible;
|
|
2144
|
+
// this closes the last link between a product graph and its design system.
|
|
2145
|
+
design_component_composes_design_component: { forward_verb: "composes", reverse_verb: "composed_in", classification: "hierarchy", source_type: "design_component", target_type: "design_component", cross_product_eligible: true },
|
|
2115
2146
|
prototype_annotated_with_annotation: { forward_verb: "annotated_with", reverse_verb: "annotates", classification: "hierarchy", source_type: "prototype", target_type: "annotation" },
|
|
2116
2147
|
screen_renders_design_component: { forward_verb: "renders", reverse_verb: "rendered_on", classification: "hierarchy", source_type: "screen", target_type: "design_component", cross_product_eligible: true },
|
|
2117
2148
|
// Marketing surface to product (0.12.7, UPG-696/698). A marketing/landing screen
|
|
@@ -7815,7 +7846,7 @@ var UPG_VALID_CHILDREN = {
|
|
|
7815
7846
|
// parent. Same widening shape as the 0.23.0 epic twins: an existing leaf type
|
|
7816
7847
|
// gains a second, SHALLOWER parent, verbs inherited from the deeper edge.
|
|
7817
7848
|
feature: ["epic", "bug", "task", "user_story"],
|
|
7818
|
-
feature_area: ["feature", "feature_area"],
|
|
7849
|
+
feature_area: ["feature", "feature_area", "design_component"],
|
|
7819
7850
|
// epic owns user_story (the templated promise) and, mirroring feature, may
|
|
7820
7851
|
// also directly contain bug/task — heterogeneous imported tickets that belong
|
|
7821
7852
|
// to one epic rather than the feature as a whole (feedback df99026a).
|
|
@@ -13376,7 +13407,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
13376
13407
|
},
|
|
13377
13408
|
// DesignTokenProperties: Design token.
|
|
13378
13409
|
design_token: {
|
|
13379
|
-
category: { type: "string", enum: ["color", "spacing", "typography", "radius", "motion"], description: "
|
|
13410
|
+
category: { type: "string", enum: ["color", "spacing", "typography", "radius", "motion", "dimension", "fontFamily", "fontWeight", "duration", "cubicBezier", "number", "shadow", "gradient", "border", "transition", "strokeStyle", "opacity"], description: "What kind of value this token holds. Aligned to the Design Tokens Community Group `$type` vocabulary so a DTCG export maps across without translation; the five original UPG values are kept, `color` and `spacing` being the ones DTCG spells the same and `typography`, `radius`, `motion` being groupings a design system names in its own terms.", notes: "DTCG mapping for the values that are not one-to-one: a `radius` token is a DTCG `dimension`, a `motion` token is usually a `duration` or a `cubicBezier`, and a `typography` token is a composite over `fontFamily` / `fontWeight` / `dimension`. Both spellings are accepted: an estate that speaks DTCG writes the `$type` name, one that speaks UPG's original five keeps writing them, and neither has to translate at the boundary. Extended at 0.39.0 from a measured estate (163 DTCG primitives) whose `shadow`, `dimension` and `opacity` tokens had no category to land on. The enum was documented but unenforced then; `validate_graph`'s `property_enum_drift` class (same release) is what makes it real, which is why the vocabulary had to be right before the check arrived." },
|
|
13380
13411
|
value: { type: "string", description: "Resolved value" },
|
|
13381
13412
|
css_variable: { type: "string", description: "CSS custom property name" }
|
|
13382
13413
|
},
|
|
@@ -34158,7 +34189,7 @@ function checkHeaderSealText(text) {
|
|
|
34158
34189
|
}
|
|
34159
34190
|
|
|
34160
34191
|
// src/index.ts
|
|
34161
|
-
var UPG_VERSION = "0.
|
|
34192
|
+
var UPG_VERSION = "0.40.0";
|
|
34162
34193
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
34163
34194
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
34164
34195
|
var UPG_TYPES = getTypes();
|