@unified-product-graph/core 0.9.11 → 0.9.13
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 +39 -0
- package/dist/index.cjs +289 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +177 -18
- package/dist/index.d.ts +177 -18
- package/dist/index.js +288 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.9.13] - 2026-06-11
|
|
11
|
+
|
|
12
|
+
**Foundations follow-ups: a registry-edge authoring path, specification stewardship, and three portfolio-scoped anti-patterns.** The deferred half of the foundations work (0.9.12) lands: canonical entities can now relate to one another in the registry, a specification can name its steward, and the registry surfaces the ways a foundations tier goes wrong across a portfolio.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **`create_registry_edge`** (tools 119 to 120, local only): the authoring path for canonical-internal edges between registry entities, activating the reserved `registry.edges`. A registry `specification` can now be `governed_by` a registry `organization`, a `primitive` `defined_by` a `specification`, a `specification` extend another, all without touching a product graph. Validates that both endpoints exist in the registry, the type is a real catalogue edge, and the catalogue source and target types match the pair. Idempotent.
|
|
16
|
+
- **`specification_governed_by_organization`** catalogue edge (edge count 979 to 980): a registry specification's stewardship, an organization as its governing body. Organizations are registry-hostable canonicals (`define_canonical_entity` already accepts the type), so the steward is a first-class entity, not a free-text field.
|
|
17
|
+
- **Three portfolio-scoped anti-patterns** (`scope: 'portfolio'`, evaluated by `portfolio_validate`, surfaced in a new `portfolio_anti_patterns` block): `specification-without-implementer` (a registry specification no product, feature, or api_contract implements or conforms to), `primitive-scattered-without-canonical` (the same primitive title appears as a product-local node in two or more products with no registry canonical unifying them), and `product-reimplements-specification` (two or more products independently implement the same registry specification). These read the registry plus cross-product edges, context a single graph cannot express.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **`UPGCuratedAntiPattern` gains an optional `scope`** (`'graph'` default, or `'portfolio'`), and `structured_condition` is now optional. The single-graph evaluator skips portfolio-scoped patterns, so a portfolio pattern can never flip one product graph invalid; their detectors live in `portfolio_validate` instead. Anti-pattern count 15 to 18.
|
|
21
|
+
|
|
22
|
+
No entity, domain, region, or playbook count change (entities 315, domains 37, regions 11, playbooks 13).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## [0.9.12] - 2026-06-10
|
|
27
|
+
|
|
28
|
+
**Foundations: a first-class `specification` and `primitive` entity, plus a technical-domain enum widening.** Two governance moats that product orgs usually mismodel as products or scatter across features now have a home, and four engineering enums that real authoring outgrew are widened.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- **`specification` and `primitive` entity types** (entity count 313 to 315) in a new **`foundations`** domain and region. A `specification` is a governed spec a product implements, exposes, or conforms to (a query language, protocol, data format, encoding); a `primitive` is the compositional unit a specification defines (a block, a reference, a query value). Both are registry-hostable canonicals (`define_canonical_entity` accepts them), so a spec scattered across N products becomes one canonical that product instances link to via `instance_of`. `specification` carries a lifecycle (draft, active, deprecated, superseded); `primitive` is lifecycle-free. Both ship at `proposed` maturity, with a canonical playbook and a domain guide.
|
|
32
|
+
- **11 foundations edges.** Four registry-internal catalogue edges: `specification_extends_specification` (a dialect or superset), `specification_competes_with_specification`, `primitive_defined_by_specification`, `primitive_composes_primitive`. Seven portfolio cross-edges from a product-graph entity to a registry canonical (the `instance_of` shape): `product_implements_specification`, `product_exposes_specification`, `feature_conforms_to_specification`, `api_contract_speaks_specification`, `product_exposes_primitive`, `feature_manipulates_primitive`, `primitive_stored_as_data_type`.
|
|
33
|
+
- **`data_flow.orientation`** (`inbound`, `outbound`, `internal`): a separate axis from `direction` (cardinality), so a flow can be one-way and inbound at once.
|
|
34
|
+
- **A `remap_property_value` migration kind** that remaps a properties-bag enum value in place, optionally splitting it into a sibling property.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
- **Four engineering enums widened** to fit real authoring: `service.service_type` gains `cli`; `api_contract.protocol` gains `SSE`; `integration_pattern.pattern_type` gains `adapter`, `client_library`, `host_embedding`, `pipes_and_filters`, `data_sync`.
|
|
38
|
+
|
|
39
|
+
### Migrations (`UPG_PROPERTY_MIGRATIONS['0.9.12']`, dual-read on load)
|
|
40
|
+
- `data_flow.direction` values `inbound` / `outbound` / `internal` move to the new `orientation` property; `direction` resets to `unidirectional`.
|
|
41
|
+
- `integration_pattern.pattern_type`: `"data sync"` to `data_sync`, `backend_client` to `client_library`.
|
|
42
|
+
- `service.service_type`: `backend` to `api`.
|
|
43
|
+
- `api_contract.protocol`: `HTTP` to `REST`, `HTTP/SSE` to `SSE`.
|
|
44
|
+
|
|
45
|
+
Edge-catalogue count 975 to 979; region count 10 to 11; domain count 36 to 37. No new tools (119).
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
10
49
|
## [0.9.11] - 2026-06-10
|
|
11
50
|
|
|
12
51
|
**Tooling DX (batch 6, part 2): status pre-flight + anti-pattern visibility.** Two discoverability fixes from the same real portfolio session, plus a self-trip fix.
|
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ __export(index_exports, {
|
|
|
28
28
|
DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK: () => DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK,
|
|
29
29
|
ENGINEERING_PLATFORM_PLAYBOOK: () => ENGINEERING_PLATFORM_PLAYBOOK,
|
|
30
30
|
EXPERIENCE_DESIGN_BRAND_PLAYBOOK: () => EXPERIENCE_DESIGN_BRAND_PLAYBOOK,
|
|
31
|
+
FOUNDATIONS_PLAYBOOK: () => FOUNDATIONS_PLAYBOOK,
|
|
31
32
|
LEGACY_PRODUCT_STAGES: () => LEGACY_PRODUCT_STAGES,
|
|
32
33
|
MARKDOWN_FORMAT_VERSION: () => MARKDOWN_FORMAT_VERSION,
|
|
33
34
|
MARKET_COMPETITIVE_PLAYBOOK: () => MARKET_COMPETITIVE_PLAYBOOK,
|
|
@@ -683,6 +684,12 @@ var UPG_DOMAINS = [
|
|
|
683
684
|
label: "Workspace",
|
|
684
685
|
description: "Spatial thinking spaces for arranging entities, debating decisions, and committing to the graph. Workspaces are transient canvases that sit alongside all other domains, letting you compose and explore relationships before they become permanent graph structure. A framework exercise is a structured workspace: one run of a framework (MoSCoW, RICE, Kano, \u2026) applied to a chosen set of entities, with each entity's result recorded on the exercise-to-entity edge rather than the entity itself.",
|
|
685
686
|
types: ["workspace", "framework_exercise"]
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
id: "foundations",
|
|
690
|
+
label: "Foundations",
|
|
691
|
+
description: "The shared specifications and foundational primitives a product organisation stewards or implements everywhere. Specifications are governed specs (query languages, protocols, data formats, encodings) like GROQ or Portable Text; primitives are the compositional units those specs define (a block, a reference, a query value). Both are registry-hostable canonicals that products implement, expose, or conform to. Distinct from a product feature: a specification has no single owner, no P&L, and no buyer; it is the rulebook many products point at.",
|
|
692
|
+
types: ["specification", "primitive"]
|
|
686
693
|
}
|
|
687
694
|
];
|
|
688
695
|
var UPG_ENTITY_TO_DOMAIN = Object.freeze(
|
|
@@ -702,6 +709,9 @@ function getDomainIdForType(entityType) {
|
|
|
702
709
|
|
|
703
710
|
// src/registry/entity-meta.ts
|
|
704
711
|
var UPG_ENTITY_META = [
|
|
712
|
+
// Foundations (0.9.12): shared specifications + the primitives they define.
|
|
713
|
+
{ name: "specification", type_id: "ent_352", maturity: "proposed", since: "0.9.12" },
|
|
714
|
+
{ name: "primitive", type_id: "ent_353", maturity: "proposed", since: "0.9.12" },
|
|
705
715
|
// ── Strategic ──
|
|
706
716
|
{ name: "product", type_id: "ent_001", maturity: "stable", since: "0.1.0" },
|
|
707
717
|
{ name: "outcome", type_id: "ent_002", maturity: "stable", since: "0.1.0" },
|
|
@@ -3120,7 +3130,16 @@ var UPG_EDGE_CATALOG = {
|
|
|
3120
3130
|
data_quality_rule_governs_data_source: { forward_verb: "governs", reverse_verb: "governed_by", classification: "semantic", source_type: "data_quality_rule", target_type: "data_source" },
|
|
3121
3131
|
// press_release → launch (marketing → go_to_market). A press release had 0
|
|
3122
3132
|
// outbound; it could not name the launch it announces.
|
|
3123
|
-
press_release_announces_launch: { forward_verb: "announces", reverse_verb: "announced_by", classification: "cross-domain", source_type: "press_release", target_type: "launch" }
|
|
3133
|
+
press_release_announces_launch: { forward_verb: "announces", reverse_verb: "announced_by", classification: "cross-domain", source_type: "press_release", target_type: "launch" },
|
|
3134
|
+
// ── Foundations (0.9.12): registry-internal relationships between canonical
|
|
3135
|
+
// specifications and primitives. Product-to-foundation links (implements,
|
|
3136
|
+
// exposes, conforms_to) are portfolio cross-edges, not catalog edges.
|
|
3137
|
+
specification_extends_specification: { forward_verb: "extends", reverse_verb: "extended_by", classification: "hierarchy", source_type: "specification", target_type: "specification" },
|
|
3138
|
+
specification_competes_with_specification: { forward_verb: "competes_with", reverse_verb: "competes_with", classification: "semantic", source_type: "specification", target_type: "specification" },
|
|
3139
|
+
primitive_defined_by_specification: { forward_verb: "defined_by", reverse_verb: "defines", classification: "semantic", source_type: "primitive", target_type: "specification" },
|
|
3140
|
+
primitive_composes_primitive: { forward_verb: "composes", reverse_verb: "composed_by", classification: "hierarchy", source_type: "primitive", target_type: "primitive" },
|
|
3141
|
+
// Stewardship: a registry specification is governed by a (registry-hostable) organization (0.9.13).
|
|
3142
|
+
specification_governed_by_organization: { forward_verb: "governed_by", reverse_verb: "governs", classification: "semantic", source_type: "specification", target_type: "organization" }
|
|
3124
3143
|
};
|
|
3125
3144
|
var UPG_POLYMORPHIC_EDGE_KEYS = [
|
|
3126
3145
|
// Universal semantic verbs
|
|
@@ -3187,7 +3206,14 @@ var UPG_CROSS_EDGE_TYPES = [
|
|
|
3187
3206
|
"instance_of",
|
|
3188
3207
|
"area_serves_persona",
|
|
3189
3208
|
"area_targets_market_segment",
|
|
3190
|
-
"rolls_up_to"
|
|
3209
|
+
"rolls_up_to",
|
|
3210
|
+
"product_implements_specification",
|
|
3211
|
+
"product_exposes_specification",
|
|
3212
|
+
"feature_conforms_to_specification",
|
|
3213
|
+
"api_contract_speaks_specification",
|
|
3214
|
+
"product_exposes_primitive",
|
|
3215
|
+
"feature_manipulates_primitive",
|
|
3216
|
+
"primitive_stored_as_data_type"
|
|
3191
3217
|
];
|
|
3192
3218
|
var REGISTRY_PRODUCT_ID = "registry";
|
|
3193
3219
|
|
|
@@ -3251,6 +3277,13 @@ function resolveEntityType(rawType) {
|
|
|
3251
3277
|
|
|
3252
3278
|
// src/grammar/hierarchy.ts
|
|
3253
3279
|
var UPG_VALID_CHILDREN = {
|
|
3280
|
+
// ── Foundations (0.9.12): registry canonicals that self-nest. A
|
|
3281
|
+
// specification extends a specification (a dialect of a parent spec); a
|
|
3282
|
+
// primitive composes primitives (a block contains spans). These back the
|
|
3283
|
+
// hierarchy-classified specification_extends_specification /
|
|
3284
|
+
// primitive_composes_primitive catalog edges.
|
|
3285
|
+
specification: ["specification"],
|
|
3286
|
+
primitive: ["primitive"],
|
|
3254
3287
|
// ── Product (root) ─────────────────────────────────────────────────────────
|
|
3255
3288
|
product: [
|
|
3256
3289
|
// Strategy
|
|
@@ -6171,9 +6204,42 @@ var FRAMEWORK_EXERCISE_LIFECYCLE = {
|
|
|
6171
6204
|
}
|
|
6172
6205
|
]
|
|
6173
6206
|
};
|
|
6207
|
+
var SPECIFICATION_LIFECYCLE = {
|
|
6208
|
+
entity_type: "specification",
|
|
6209
|
+
initial_phase: "draft",
|
|
6210
|
+
terminal_phases: ["superseded"],
|
|
6211
|
+
phases: [
|
|
6212
|
+
{
|
|
6213
|
+
id: "draft",
|
|
6214
|
+
label: "Draft",
|
|
6215
|
+
description: "Being authored. Not yet adopted as the canonical specification.",
|
|
6216
|
+
transitions_to: ["active", "superseded"]
|
|
6217
|
+
},
|
|
6218
|
+
{
|
|
6219
|
+
id: "active",
|
|
6220
|
+
label: "Active",
|
|
6221
|
+
description: "In force. The current authoritative version products implement and conform to.",
|
|
6222
|
+
transitions_to: ["deprecated", "superseded"]
|
|
6223
|
+
},
|
|
6224
|
+
{
|
|
6225
|
+
id: "deprecated",
|
|
6226
|
+
label: "Deprecated",
|
|
6227
|
+
description: "Still valid but no longer recommended; a successor exists or is imminent.",
|
|
6228
|
+
transitions_to: ["superseded"]
|
|
6229
|
+
},
|
|
6230
|
+
{
|
|
6231
|
+
id: "superseded",
|
|
6232
|
+
label: "Superseded",
|
|
6233
|
+
description: "Replaced by a newer specification. Retained for historical reference.",
|
|
6234
|
+
transitions_to: []
|
|
6235
|
+
}
|
|
6236
|
+
]
|
|
6237
|
+
};
|
|
6174
6238
|
var UPG_LIFECYCLES = [
|
|
6175
6239
|
// Product (root)
|
|
6176
6240
|
PRODUCT_LIFECYCLE,
|
|
6241
|
+
// Foundations (0.9.12)
|
|
6242
|
+
SPECIFICATION_LIFECYCLE,
|
|
6177
6243
|
// Discovery & Validation
|
|
6178
6244
|
RESEARCH_STUDY_LIFECYCLE,
|
|
6179
6245
|
NEED_LIFECYCLE,
|
|
@@ -6281,6 +6347,9 @@ function getLifecycleForType(entityType) {
|
|
|
6281
6347
|
return UPG_LIFECYCLES.find((l) => l.entity_type === entityType);
|
|
6282
6348
|
}
|
|
6283
6349
|
var UPG_LIFECYCLE_FREE_TYPES = /* @__PURE__ */ new Set([
|
|
6350
|
+
// ── Foundations (1 of 2): a primitive either exists or is deprecated; no
|
|
6351
|
+
// phase ladder (like metric). `specification` carries a lifecycle. ──
|
|
6352
|
+
"primitive",
|
|
6284
6353
|
// ── User (5): personas and JTBD atoms are reference data ──────────────────
|
|
6285
6354
|
"persona",
|
|
6286
6355
|
"job",
|
|
@@ -7352,6 +7421,50 @@ function getDeprecatedTypes() {
|
|
|
7352
7421
|
return deprecated;
|
|
7353
7422
|
}
|
|
7354
7423
|
var UPG_PROPERTY_MIGRATIONS = {
|
|
7424
|
+
// ── v0.9.12: technical-domain enum widening + data_flow orientation split ──
|
|
7425
|
+
//
|
|
7426
|
+
// Real authoring outgrew four engineering-domain enums. 0.9.12 widens them
|
|
7427
|
+
// (see properties/domains/engineering.ts) and adds a data_flow `orientation`
|
|
7428
|
+
// axis distinct from `direction` (cardinality). These rules dual-read the
|
|
7429
|
+
// legacy values so graphs authored before 0.9.12 stay valid on load:
|
|
7430
|
+
// - data_flow.direction inbound/outbound/internal -> properties.orientation,
|
|
7431
|
+
// and direction resets to unidirectional (all three were one-way).
|
|
7432
|
+
// - integration_pattern.pattern_type "data sync" -> data_sync,
|
|
7433
|
+
// backend_client -> client_library.
|
|
7434
|
+
// - service.service_type backend -> api (use the new `cli` for CLI tools).
|
|
7435
|
+
// - api_contract.protocol HTTP -> REST, HTTP/SSE -> the new SSE value.
|
|
7436
|
+
"0.9.12": [
|
|
7437
|
+
{
|
|
7438
|
+
kind: "remap_property_value",
|
|
7439
|
+
type: "data_flow",
|
|
7440
|
+
property: "direction",
|
|
7441
|
+
value_map: { inbound: "inbound", outbound: "outbound", internal: "internal" },
|
|
7442
|
+
to_property: "orientation",
|
|
7443
|
+
reset_value: "unidirectional",
|
|
7444
|
+
reason: "data_flow.direction conflated cardinality with orientation; the orientation values move to the new `orientation` property and direction resets to `unidirectional` (0.9.12)."
|
|
7445
|
+
},
|
|
7446
|
+
{
|
|
7447
|
+
kind: "remap_property_value",
|
|
7448
|
+
type: "integration_pattern",
|
|
7449
|
+
property: "pattern_type",
|
|
7450
|
+
value_map: { "data sync": "data_sync", backend_client: "client_library" },
|
|
7451
|
+
reason: 'integration_pattern.pattern_type widened in 0.9.12; legacy "data sync" snake-cases to data_sync and backend_client collapses to client_library.'
|
|
7452
|
+
},
|
|
7453
|
+
{
|
|
7454
|
+
kind: "remap_property_value",
|
|
7455
|
+
type: "service",
|
|
7456
|
+
property: "service_type",
|
|
7457
|
+
value_map: { backend: "api" },
|
|
7458
|
+
reason: "service.service_type `backend` is too generic to canonise; normalised to `api` in 0.9.12 (use `cli` for command-line tools)."
|
|
7459
|
+
},
|
|
7460
|
+
{
|
|
7461
|
+
kind: "remap_property_value",
|
|
7462
|
+
type: "api_contract",
|
|
7463
|
+
property: "protocol",
|
|
7464
|
+
value_map: { HTTP: "REST", "HTTP/SSE": "SSE" },
|
|
7465
|
+
reason: "api_contract.protocol normalised in 0.9.12: HTTP maps to REST and HTTP/SSE maps to the new SSE value."
|
|
7466
|
+
}
|
|
7467
|
+
],
|
|
7355
7468
|
// ── v0.8.0: UPG-574 deprecated-property removal pass ───────────────────
|
|
7356
7469
|
//
|
|
7357
7470
|
// The seven properties tagged `@deprecated since v0.4.0` that survived the
|
|
@@ -7845,6 +7958,25 @@ function migrateNodeProperties(node, fromVersion, toVersion) {
|
|
|
7845
7958
|
}
|
|
7846
7959
|
break;
|
|
7847
7960
|
}
|
|
7961
|
+
case "remap_property_value": {
|
|
7962
|
+
const props = workingNode.properties;
|
|
7963
|
+
if (!props || !(m.property in props)) break;
|
|
7964
|
+
const oldValue = props[m.property];
|
|
7965
|
+
if (typeof oldValue !== "string" || !(oldValue in m.value_map)) break;
|
|
7966
|
+
const mapped = m.value_map[oldValue];
|
|
7967
|
+
const nextProps = { ...props };
|
|
7968
|
+
if (m.to_property) {
|
|
7969
|
+
nextProps[m.to_property] = mapped;
|
|
7970
|
+
nextProps[m.property] = m.reset_value ?? mapped;
|
|
7971
|
+
changes.push({ kind: "remapped_property_value", property: m.property, to_property: m.to_property, value_changed: true });
|
|
7972
|
+
} else {
|
|
7973
|
+
nextProps[m.property] = mapped;
|
|
7974
|
+
changes.push({ kind: "remapped_property_value", property: m.property, value_changed: mapped !== oldValue });
|
|
7975
|
+
}
|
|
7976
|
+
workingNode.properties = nextProps;
|
|
7977
|
+
mutated = true;
|
|
7978
|
+
break;
|
|
7979
|
+
}
|
|
7848
7980
|
}
|
|
7849
7981
|
}
|
|
7850
7982
|
}
|
|
@@ -9298,7 +9430,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
9298
9430
|
// ApiContractProperties: API contract.
|
|
9299
9431
|
api_contract: {
|
|
9300
9432
|
spec_url: { type: "string", description: "URL of the specification document" },
|
|
9301
|
-
protocol: { type: "string", enum: ["REST", "GraphQL", "gRPC", "AsyncAPI", "SOAP", "WebSocket", "MQTT", "other"], description: "Communication protocol" },
|
|
9433
|
+
protocol: { type: "string", enum: ["REST", "GraphQL", "gRPC", "AsyncAPI", "SOAP", "WebSocket", "MQTT", "SSE", "other"], description: "Communication protocol. `SSE` (server-sent events) added in 0.9.12." },
|
|
9302
9434
|
version: { type: "string", description: "API version" },
|
|
9303
9435
|
owner: { type: "string", description: "Maintaining person or team." }
|
|
9304
9436
|
},
|
|
@@ -9779,7 +9911,8 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
9779
9911
|
data_flow: {
|
|
9780
9912
|
trigger: { type: "string", description: "What triggers the flow" },
|
|
9781
9913
|
data_type: { type: "string", description: "Type of data transferred" },
|
|
9782
|
-
direction: { type: "string", enum: ["unidirectional", "bidirectional"], description: "Direction" },
|
|
9914
|
+
direction: { type: "string", enum: ["unidirectional", "bidirectional"], description: "Direction (cardinality of the link)." },
|
|
9915
|
+
orientation: { type: "string", enum: ["inbound", "outbound", "internal"], description: "Flow orientation relative to the system or component, a separate axis from `direction` (which is cardinality). A flow can be `unidirectional` AND `inbound`. Added in 0.9.12 to split the orientation axis that authors were conflating into `direction` (inbound/outbound/internal values)." },
|
|
9783
9916
|
protocol: { type: "string", enum: ["rest", "graphql", "grpc", "event", "webhook", "file"], description: "Communication protocol" }
|
|
9784
9917
|
},
|
|
9785
9918
|
// DataLineageProperties: DataLineage entity.
|
|
@@ -10535,7 +10668,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
10535
10668
|
},
|
|
10536
10669
|
// IntegrationPatternProperties: Integration pattern between systems.
|
|
10537
10670
|
integration_pattern: {
|
|
10538
|
-
pattern_type: { type: "string", enum: ["api", "event", "file", "database", "webhook"], description: "Type" },
|
|
10671
|
+
pattern_type: { type: "string", enum: ["api", "event", "file", "database", "webhook", "adapter", "client_library", "host_embedding", "pipes_and_filters", "data_sync"], description: "Type. `adapter`, `client_library`, `host_embedding`, `pipes_and_filters`, `data_sync` added in 0.9.12." },
|
|
10539
10672
|
protocol: { type: "string", description: "Communication protocol" }
|
|
10540
10673
|
},
|
|
10541
10674
|
// InteractionSpecProperties: Interaction specification.
|
|
@@ -11240,6 +11373,12 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
11240
11373
|
tier_order: { type: "number", description: "Display ordering (1 = first tier shown)" },
|
|
11241
11374
|
is_highlighted: { type: "boolean", description: "Highlighted as recommended / most popular" }
|
|
11242
11375
|
},
|
|
11376
|
+
// PrimitiveProperties: A foundational compositional unit a specification defines: the noun products
|
|
11377
|
+
primitive: {
|
|
11378
|
+
primitive_kind: { type: "string", enum: ["data_type", "object", "block", "unit"], description: "The shape of the thing products pass around." },
|
|
11379
|
+
defined_by: { type: "string", description: "The `specification/<slug>` this primitive comes from; nullable for spec-less internal primitives. Mirrors the `primitive_defined_by_specification` edge for quick lookup." },
|
|
11380
|
+
since: { type: "string", description: "Year or version the primitive was introduced." }
|
|
11381
|
+
},
|
|
11243
11382
|
// PrivacyPolicyProperties: Privacy policy.
|
|
11244
11383
|
privacy_policy: {
|
|
11245
11384
|
version: { type: "string", description: "Version identifier of the policy" },
|
|
@@ -11610,7 +11749,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
11610
11749
|
},
|
|
11611
11750
|
// ServiceProperties: Service or microservice.
|
|
11612
11751
|
service: {
|
|
11613
|
-
service_type: { type: "string", enum: ["web", "api", "worker", "db", "queue", "library", "mobile", "docs", "lambda"], description: "Functional classification. Expanded from Backstage's component type vocabulary." },
|
|
11752
|
+
service_type: { type: "string", enum: ["web", "api", "worker", "db", "queue", "library", "mobile", "docs", "lambda", "cli"], description: "Functional classification. Expanded from Backstage's component type vocabulary. `cli` added in 0.9.12." },
|
|
11614
11753
|
tech_stack: { type: "string[]", description: 'Technologies used (e.g. ["TypeScript", "Postgres", "Redis"])' },
|
|
11615
11754
|
owner: { type: "string", description: "Owning person or team. Backstage marks this required; strongly recommended." },
|
|
11616
11755
|
lifecycle: { type: "string", enum: ["experimental", "production", "deprecated"], description: 'Service maturity. Answers "how mature is it?". `experimental` = early-stage. `production` = battle-tested. `deprecated` = being phased out.' },
|
|
@@ -11732,6 +11871,18 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
11732
11871
|
},
|
|
11733
11872
|
rice_score: { type: "number", description: "Computed: (reach \xD7 impact \xD7 confidence) / effort. @deprecated 0.9.0:computed by the `rice-scoring` framework on its application edge, not stored on the entity. Removed in 0.9.1." }
|
|
11734
11873
|
},
|
|
11874
|
+
// SpecificationProperties: A governed specification: a query language, protocol, data format, encoding,
|
|
11875
|
+
specification: {
|
|
11876
|
+
kind: { type: "string", enum: ["language", "protocol", "data_format", "encoding", "interface_contract", "object_model"], description: "What the artifact fundamentally is, independent of how it is governed." },
|
|
11877
|
+
language_flavor: { type: "string", enum: ["query", "programming", "markup", "styling", "schema", "template"], description: "Set only when `kind` is `language`: the kind of language." },
|
|
11878
|
+
governance: { type: "string", enum: ["open_spec_stewarded", "open_standard_consortium", "proprietary_open", "internal_primitive", "de_facto"], description: "How the specification is governed (its ratification status, and whether it counts as a `standard`). `open_standard_consortium` is a formal standard (W3C, IETF, ISO); `de_facto` and `internal_primitive` never became one." },
|
|
11879
|
+
steward: { type: "string", description: "The governing body or organisation (may later become an `organization` ref)." },
|
|
11880
|
+
openness: { type: "string", enum: ["open", "proprietary"], description: "Whether the specification itself is open or proprietary." },
|
|
11881
|
+
spec_url: { type: "string", description: "URL of the published specification." },
|
|
11882
|
+
current_version: { type: "string", description: "Latest published version string." },
|
|
11883
|
+
since: { type: "string", description: "Year or version the specification was introduced." },
|
|
11884
|
+
conformance: { type: "string", description: "How conformance is tested (test suite, certification program). Optional." }
|
|
11885
|
+
},
|
|
11735
11886
|
// StakeholderProperties: Stakeholder entity.
|
|
11736
11887
|
stakeholder: {
|
|
11737
11888
|
stakeholder_type: { type: "string", enum: ["internal", "external", "investor", "regulator"], description: "Relationship of the stakeholder to the organisation" },
|
|
@@ -19717,6 +19868,30 @@ var OPERATIONS_QUALITY_PLAYBOOK = {
|
|
|
19717
19868
|
)
|
|
19718
19869
|
]
|
|
19719
19870
|
};
|
|
19871
|
+
var FOUNDATIONS_PLAYBOOK = {
|
|
19872
|
+
id: "playbook:foundations",
|
|
19873
|
+
name: "Foundations",
|
|
19874
|
+
version: "0.9.12",
|
|
19875
|
+
description: "Register the specifications your products implement and expose, and the primitives those specs define, as canonical entities instead of scattered per-product features.",
|
|
19876
|
+
region: "foundations",
|
|
19877
|
+
is_canonical: true,
|
|
19878
|
+
related_framework_ids: [],
|
|
19879
|
+
target_anchor_entity: "specification",
|
|
19880
|
+
creation_sequence: [
|
|
19881
|
+
seqStep(
|
|
19882
|
+
1,
|
|
19883
|
+
"Specifications",
|
|
19884
|
+
["specification"],
|
|
19885
|
+
"Define each governed spec your products implement, expose, or conform to (a query language, protocol, or format) as one canonical specification, not a per-product feature."
|
|
19886
|
+
),
|
|
19887
|
+
seqStep(
|
|
19888
|
+
2,
|
|
19889
|
+
"Primitives",
|
|
19890
|
+
["primitive"],
|
|
19891
|
+
"Define the compositional units those specs define (a block, a reference, a query value) as primitives, each linked to its specification."
|
|
19892
|
+
)
|
|
19893
|
+
]
|
|
19894
|
+
};
|
|
19720
19895
|
var UPG_PLAYBOOKS = [
|
|
19721
19896
|
// Region 1 - strategy_outcomes
|
|
19722
19897
|
STRATEGY_OUTCOMES_PLAYBOOK,
|
|
@@ -19739,7 +19914,9 @@ var UPG_PLAYBOOKS = [
|
|
|
19739
19914
|
// Region 9 - analytics_data
|
|
19740
19915
|
ANALYTICS_DATA_PLAYBOOK,
|
|
19741
19916
|
// Region 10 - operations_quality
|
|
19742
|
-
OPERATIONS_QUALITY_PLAYBOOK
|
|
19917
|
+
OPERATIONS_QUALITY_PLAYBOOK,
|
|
19918
|
+
// Region 11 - foundations
|
|
19919
|
+
FOUNDATIONS_PLAYBOOK
|
|
19743
19920
|
];
|
|
19744
19921
|
|
|
19745
19922
|
// src/step-sequence.ts
|
|
@@ -20481,7 +20658,7 @@ var UPG_DOMAIN_RINGS = [
|
|
|
20481
20658
|
id: "build",
|
|
20482
20659
|
label: "Build",
|
|
20483
20660
|
description: "How do we construct it?",
|
|
20484
|
-
domain_ids: ["engineering", "ai", "automation", "data_analytics", "testing", "devops", "security", "accessibility"]
|
|
20661
|
+
domain_ids: ["engineering", "foundations", "ai", "automation", "data_analytics", "testing", "devops", "security", "accessibility"]
|
|
20485
20662
|
},
|
|
20486
20663
|
{
|
|
20487
20664
|
id: "grow",
|
|
@@ -21396,10 +21573,30 @@ var WORKSPACE_GUIDE = {
|
|
|
21396
21573
|
{ description: "Workspace content is transient by default: promote discoveries to the graph, do not leave them in the workspace" }
|
|
21397
21574
|
]
|
|
21398
21575
|
};
|
|
21576
|
+
var FOUNDATIONS_GUIDE = {
|
|
21577
|
+
domain_id: "foundations",
|
|
21578
|
+
anchor_entity: "specification",
|
|
21579
|
+
creation_sequence: ["specification", "primitive"],
|
|
21580
|
+
patterns: [
|
|
21581
|
+
{
|
|
21582
|
+
name: "Specification defines primitives",
|
|
21583
|
+
description: "A specification governs one or more primitives; each primitive is defined_by the spec and may compose other primitives",
|
|
21584
|
+
entity_types: ["specification", "primitive"],
|
|
21585
|
+
edge_chain: ["primitive_defined_by_specification", "primitive_composes_primitive"]
|
|
21586
|
+
}
|
|
21587
|
+
],
|
|
21588
|
+
required_bridges: [],
|
|
21589
|
+
anti_patterns: [
|
|
21590
|
+
{ description: "A specification scattered as duplicate features across products instead of one canonical with instance_of links" },
|
|
21591
|
+
{ description: "Modelling a governed spec as a product: it has no single owner, P&L, or buyer; it is the rulebook products point at" }
|
|
21592
|
+
]
|
|
21593
|
+
};
|
|
21399
21594
|
var UPG_DOMAIN_GUIDES = [
|
|
21400
21595
|
// Nucleus
|
|
21401
21596
|
PORTFOLIO_GUIDE,
|
|
21402
21597
|
WORKSPACE_GUIDE,
|
|
21598
|
+
// Foundations (0.9.12)
|
|
21599
|
+
FOUNDATIONS_GUIDE,
|
|
21403
21600
|
// Ring 1: Understand
|
|
21404
21601
|
USER_GUIDE,
|
|
21405
21602
|
USER_RESEARCH_GUIDE,
|
|
@@ -24178,6 +24375,47 @@ var UPG_ANTI_PATTERNS = [
|
|
|
24178
24375
|
stages: ["beta", "launch", "growth", "mature"],
|
|
24179
24376
|
severity: "medium",
|
|
24180
24377
|
source: { kind: "practitioner", attribution: "Marty Cagan, Inspired (product discovery)" }
|
|
24378
|
+
},
|
|
24379
|
+
// ── Foundations layer (0.9.13): portfolio-scoped, registry-aware ──────────
|
|
24380
|
+
// These read the shared registry + cross-product edges, so they are evaluated
|
|
24381
|
+
// by portfolio_validate, not the single-graph evaluator. They carry no
|
|
24382
|
+
// structured_condition (the detector is cross-product, not a single-graph
|
|
24383
|
+
// IntelligenceCondition).
|
|
24384
|
+
{
|
|
24385
|
+
id: "specification-without-implementer",
|
|
24386
|
+
since: "0.9.13",
|
|
24387
|
+
scope: "portfolio",
|
|
24388
|
+
name: "Specification without implementer",
|
|
24389
|
+
description: "A specification in the shared registry has no product, feature, or api_contract implementing or conforming to it anywhere in the portfolio. A specification nobody implements is a document, not a contract: it states an intent the portfolio never honours.",
|
|
24390
|
+
why_it_matters: "An unimplemented specification carries authority it has not earned. Teams cite it as a standard while no surface actually conforms, so conformance claims cannot be trusted or traced.",
|
|
24391
|
+
remediation: "Link at least one product, feature, or api_contract to the specification via `product_implements_specification`, `product_exposes_specification`, `feature_conforms_to_specification`, or `api_contract_speaks_specification`; or retire the specification from the registry.",
|
|
24392
|
+
stages: ["validation", "build", "beta", "launch", "growth", "mature"],
|
|
24393
|
+
severity: "medium",
|
|
24394
|
+
source: { kind: "fundamental" }
|
|
24395
|
+
},
|
|
24396
|
+
{
|
|
24397
|
+
id: "primitive-scattered-without-canonical",
|
|
24398
|
+
since: "0.9.13",
|
|
24399
|
+
scope: "portfolio",
|
|
24400
|
+
name: "Primitive scattered without a canonical",
|
|
24401
|
+
description: "The same primitive concept appears as a product-local node in two or more products, but no canonical primitive in the shared registry unifies them. Each product redefines the building block on its own terms, so the portfolio carries several drifting copies of one shared idea instead of a single authoritative definition.",
|
|
24402
|
+
why_it_matters: "Scattered primitives drift apart in name, shape, and meaning. Cross-product reasoning breaks because the same concept reads as several unrelated entities, and a change to the shared building block has no single place to land.",
|
|
24403
|
+
remediation: "Define the shared primitive once in the registry with `define_canonical_entity`, then link each product copy via `register_instance` so the building block has one authoritative definition.",
|
|
24404
|
+
stages: ["build", "beta", "launch", "growth", "mature"],
|
|
24405
|
+
severity: "medium",
|
|
24406
|
+
source: { kind: "fundamental" }
|
|
24407
|
+
},
|
|
24408
|
+
{
|
|
24409
|
+
id: "product-reimplements-specification",
|
|
24410
|
+
since: "0.9.13",
|
|
24411
|
+
scope: "portfolio",
|
|
24412
|
+
name: "Specification reimplemented across products",
|
|
24413
|
+
description: "Two or more products independently implement the same registry specification rather than one depending on a shared implementation. Parallel implementations of a single contract multiply the surface that must stay in sync and usually signal a missing shared library or service.",
|
|
24414
|
+
why_it_matters: "Every independent reimplementation of a specification is another place a conformance bug can hide and another copy that drifts from the contract. The cost of a spec change scales with the number of reimplementers.",
|
|
24415
|
+
remediation: "Consolidate onto one implementation that the others depend on (`depends_on_product` / `hosts`), or confirm the duplication is deliberate and record why. Capture the stewarding organization with `create_registry_edge` so the contract has an owner.",
|
|
24416
|
+
stages: ["build", "beta", "launch", "growth", "mature"],
|
|
24417
|
+
severity: "low",
|
|
24418
|
+
source: { kind: "fundamental" }
|
|
24181
24419
|
}
|
|
24182
24420
|
];
|
|
24183
24421
|
function getAntiPatternById(id) {
|
|
@@ -24201,6 +24439,7 @@ function evaluateAntiPatterns(inputs, options) {
|
|
|
24201
24439
|
const idFilter = options?.anti_pattern_ids ? new Set(options.anti_pattern_ids) : void 0;
|
|
24202
24440
|
const fires = [];
|
|
24203
24441
|
for (const ap of UPG_ANTI_PATTERNS) {
|
|
24442
|
+
if (ap.scope === "portfolio" || !ap.structured_condition) continue;
|
|
24204
24443
|
if (severityFilter && ap.severity !== severityFilter) continue;
|
|
24205
24444
|
if (idFilter && !idFilter.has(ap.id)) continue;
|
|
24206
24445
|
if (inputs.productStage && !ap.stages.includes(inputs.productStage)) {
|
|
@@ -24361,6 +24600,7 @@ function buildViolation(ap) {
|
|
|
24361
24600
|
}
|
|
24362
24601
|
function collectTargetEntities(cond) {
|
|
24363
24602
|
const types = /* @__PURE__ */ new Set();
|
|
24603
|
+
if (!cond) return [];
|
|
24364
24604
|
walk(cond);
|
|
24365
24605
|
return [...types].sort();
|
|
24366
24606
|
function walk(c) {
|
|
@@ -26692,6 +26932,45 @@ var UPG_REGIONS = [
|
|
|
26692
26932
|
crosses_into: "users_needs"
|
|
26693
26933
|
}
|
|
26694
26934
|
]
|
|
26935
|
+
},
|
|
26936
|
+
{
|
|
26937
|
+
id: "foundations",
|
|
26938
|
+
label: "Foundations",
|
|
26939
|
+
order: 11,
|
|
26940
|
+
shape: "polymorphic-target",
|
|
26941
|
+
mental_model: "Shared specs and primitives that many products implement, expose, and conform to.",
|
|
26942
|
+
operators: [
|
|
26943
|
+
"platform architect",
|
|
26944
|
+
"standards author",
|
|
26945
|
+
"staff engineer",
|
|
26946
|
+
"API designer"
|
|
26947
|
+
],
|
|
26948
|
+
composes_atomic_domains: [
|
|
26949
|
+
"foundations"
|
|
26950
|
+
],
|
|
26951
|
+
entities: [
|
|
26952
|
+
{
|
|
26953
|
+
type: "specification",
|
|
26954
|
+
role: "anchor"
|
|
26955
|
+
},
|
|
26956
|
+
{
|
|
26957
|
+
type: "primitive",
|
|
26958
|
+
role: "container"
|
|
26959
|
+
}
|
|
26960
|
+
],
|
|
26961
|
+
anchor: {
|
|
26962
|
+
type: "specification",
|
|
26963
|
+
rationale: "The specification is the rulebook many products point at: they implement, expose, and conform to it, and primitives are defined by it. It is the high-inbound canonical of the Foundations region.",
|
|
26964
|
+
outbound_cross_edge_count: 0,
|
|
26965
|
+
inbound_cross_edge_count: 6
|
|
26966
|
+
},
|
|
26967
|
+
intra_edges: [
|
|
26968
|
+
"specification_extends_specification",
|
|
26969
|
+
"specification_competes_with_specification",
|
|
26970
|
+
"primitive_defined_by_specification",
|
|
26971
|
+
"primitive_composes_primitive"
|
|
26972
|
+
],
|
|
26973
|
+
boundary_edges: []
|
|
26695
26974
|
}
|
|
26696
26975
|
];
|
|
26697
26976
|
var UPG_REGION_MAP = Object.fromEntries(UPG_REGIONS.map((r) => [r.id, r]));
|
|
@@ -27198,7 +27477,7 @@ function isCanonical(text) {
|
|
|
27198
27477
|
}
|
|
27199
27478
|
|
|
27200
27479
|
// src/index.ts
|
|
27201
|
-
var UPG_VERSION = "0.9.
|
|
27480
|
+
var UPG_VERSION = "0.9.13";
|
|
27202
27481
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
27203
27482
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
27204
27483
|
var UPG_TYPES = getTypes();
|
|
@@ -27271,6 +27550,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
27271
27550
|
DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK,
|
|
27272
27551
|
ENGINEERING_PLATFORM_PLAYBOOK,
|
|
27273
27552
|
EXPERIENCE_DESIGN_BRAND_PLAYBOOK,
|
|
27553
|
+
FOUNDATIONS_PLAYBOOK,
|
|
27274
27554
|
LEGACY_PRODUCT_STAGES,
|
|
27275
27555
|
MARKDOWN_FORMAT_VERSION,
|
|
27276
27556
|
MARKET_COMPETITIVE_PLAYBOOK,
|