@unified-product-graph/core 0.9.10 → 0.9.12

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 CHANGED
@@ -7,12 +7,50 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.9.12] - 2026-06-10
11
+
12
+ **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.
13
+
14
+ ### Added
15
+ - **`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.
16
+ - **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`.
17
+ - **`data_flow.orientation`** (`inbound`, `outbound`, `internal`): a separate axis from `direction` (cardinality), so a flow can be one-way and inbound at once.
18
+ - **A `remap_property_value` migration kind** that remaps a properties-bag enum value in place, optionally splitting it into a sibling property.
19
+
20
+ ### Changed
21
+ - **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`.
22
+
23
+ ### Migrations (`UPG_PROPERTY_MIGRATIONS['0.9.12']`, dual-read on load)
24
+ - `data_flow.direction` values `inbound` / `outbound` / `internal` move to the new `orientation` property; `direction` resets to `unidirectional`.
25
+ - `integration_pattern.pattern_type`: `"data sync"` to `data_sync`, `backend_client` to `client_library`.
26
+ - `service.service_type`: `backend` to `api`.
27
+ - `api_contract.protocol`: `HTTP` to `REST`, `HTTP/SSE` to `SSE`.
28
+
29
+ Edge-catalogue count 975 to 979; region count 10 to 11; domain count 36 to 37. No new tools (119).
30
+
31
+ ---
32
+
33
+ ## [0.9.11] - 2026-06-10
34
+
35
+ **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.
36
+
37
+ ### Added
38
+ - **`list_status_values(entity_type)`** (tools 118 to 119, on both the local and cloud servers): the valid `status` values a type can hold, as a focused pre-flight lookup so an author no longer learns the set only from a rejected write. Returns lifecycle phases as `{ status, label, terminal }` plus `initial_status` / `terminal_statuses`, or `lifecycle_free: true` with empty `values`. Sourced from `UPG_LIFECYCLES` (exactly what the write validator checks): the low-token sibling of `get_lifecycle`.
39
+ - **Anti-pattern version tracking.** `UPGCuratedAntiPattern` gains an optional `since` field, and `get_spec_version` now reports `anti_patterns: { total, versioned }`. A consumer can see which validators are newer than the version a graph was authored under, so a spec upgrade no longer silently flips a clean graph invalid with no heads-up.
40
+
41
+ ### Changed
42
+ - **A drafted hypothesis is auto-promoted to `active`** when it gains a `hypothesis_requires_experiment_plan` edge (via `create_edge` / `batch_create_edges`). Pairing a hypothesis with a plan is what activates it; this stops the documented structural-spine recipe (hypotheses paired with plans) from self-tripping the `untested-hypothesis-pile-up` anti-pattern. Best-effort: a promotion failure never fails the edge.
43
+
44
+ No entity, domain, or edge-catalogue count change (tool count 118 to 119).
45
+
46
+ ---
47
+
10
48
  ## [0.9.10] - 2026-06-10
11
49
 
12
50
  **Tooling DX + a born-valid product (batch 6, part 1) + a release-test gate.** Polish from a real multi-graph portfolio session: parity and discoverability fixes, one model correction, and — the headline — the release train now runs the unit suites, after they were found to have been silently red across a release.
13
51
 
14
52
  ### Changed
15
- - **Product `properties.stage` is canonical.** The stale `UPG_PROPERTY_MIGRATIONS['0.2.13']` lift of a product node's `properties.stage` to top-level `status` is retired: it contradicted the UPG-654/661 convention the whole runtime uses, so a freshly-created product read as `property_drift` against its own validator. A new product is now born valid. Legacy values are still normalised on read by `coerceProductStage`. Decision: `2026-06-10-product-stage-properties-canonical.md`.
53
+ - **Product `properties.stage` is canonical.** The stale `UPG_PROPERTY_MIGRATIONS['0.2.13']` lift of a product node's `properties.stage` to top-level `status` is retired: it contradicted the current convention the whole runtime uses (a product node's lifecycle stage lives in `properties.stage`), so a freshly-created product read as `property_drift` against its own validator. A new product is now born valid. Legacy values are still normalised on read by `coerceProductStage`. Decision: `2026-06-10-product-stage-properties-canonical.md`.
16
54
  - **`get_nodes` resolves cross-product ids.** A qualified `{product_id}/{node_id}` (the form `list_registry` / `export_edges` / cross-edges return) now reads that product's graph (read-only for non-active products), so a connective pass fetches node content across the portfolio without a `switch_product` sweep. Results carry a `product_id`.
17
55
  - **`create_edge` / `batch_create_edges` accept the `p_` product-header** where the in-graph product node is expected, resolving it to the `type:"product"` node (older graphs carry a distinct `n_…` product node).
18
56
  - **`did_you_mean` for a mistyped edge type** now falls back to the closest catalogue key by name (edit distance) when the endpoints don't resolve to a canonical pair — so a typo always gets a concrete suggestion.
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,14 @@ 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" }
3124
3141
  };
3125
3142
  var UPG_POLYMORPHIC_EDGE_KEYS = [
3126
3143
  // Universal semantic verbs
@@ -3187,7 +3204,14 @@ var UPG_CROSS_EDGE_TYPES = [
3187
3204
  "instance_of",
3188
3205
  "area_serves_persona",
3189
3206
  "area_targets_market_segment",
3190
- "rolls_up_to"
3207
+ "rolls_up_to",
3208
+ "product_implements_specification",
3209
+ "product_exposes_specification",
3210
+ "feature_conforms_to_specification",
3211
+ "api_contract_speaks_specification",
3212
+ "product_exposes_primitive",
3213
+ "feature_manipulates_primitive",
3214
+ "primitive_stored_as_data_type"
3191
3215
  ];
3192
3216
  var REGISTRY_PRODUCT_ID = "registry";
3193
3217
 
@@ -3251,6 +3275,13 @@ function resolveEntityType(rawType) {
3251
3275
 
3252
3276
  // src/grammar/hierarchy.ts
3253
3277
  var UPG_VALID_CHILDREN = {
3278
+ // ── Foundations (0.9.12): registry canonicals that self-nest. A
3279
+ // specification extends a specification (a dialect of a parent spec); a
3280
+ // primitive composes primitives (a block contains spans). These back the
3281
+ // hierarchy-classified specification_extends_specification /
3282
+ // primitive_composes_primitive catalog edges.
3283
+ specification: ["specification"],
3284
+ primitive: ["primitive"],
3254
3285
  // ── Product (root) ─────────────────────────────────────────────────────────
3255
3286
  product: [
3256
3287
  // Strategy
@@ -6171,9 +6202,42 @@ var FRAMEWORK_EXERCISE_LIFECYCLE = {
6171
6202
  }
6172
6203
  ]
6173
6204
  };
6205
+ var SPECIFICATION_LIFECYCLE = {
6206
+ entity_type: "specification",
6207
+ initial_phase: "draft",
6208
+ terminal_phases: ["superseded"],
6209
+ phases: [
6210
+ {
6211
+ id: "draft",
6212
+ label: "Draft",
6213
+ description: "Being authored. Not yet adopted as the canonical specification.",
6214
+ transitions_to: ["active", "superseded"]
6215
+ },
6216
+ {
6217
+ id: "active",
6218
+ label: "Active",
6219
+ description: "In force. The current authoritative version products implement and conform to.",
6220
+ transitions_to: ["deprecated", "superseded"]
6221
+ },
6222
+ {
6223
+ id: "deprecated",
6224
+ label: "Deprecated",
6225
+ description: "Still valid but no longer recommended; a successor exists or is imminent.",
6226
+ transitions_to: ["superseded"]
6227
+ },
6228
+ {
6229
+ id: "superseded",
6230
+ label: "Superseded",
6231
+ description: "Replaced by a newer specification. Retained for historical reference.",
6232
+ transitions_to: []
6233
+ }
6234
+ ]
6235
+ };
6174
6236
  var UPG_LIFECYCLES = [
6175
6237
  // Product (root)
6176
6238
  PRODUCT_LIFECYCLE,
6239
+ // Foundations (0.9.12)
6240
+ SPECIFICATION_LIFECYCLE,
6177
6241
  // Discovery & Validation
6178
6242
  RESEARCH_STUDY_LIFECYCLE,
6179
6243
  NEED_LIFECYCLE,
@@ -6281,6 +6345,9 @@ function getLifecycleForType(entityType) {
6281
6345
  return UPG_LIFECYCLES.find((l) => l.entity_type === entityType);
6282
6346
  }
6283
6347
  var UPG_LIFECYCLE_FREE_TYPES = /* @__PURE__ */ new Set([
6348
+ // ── Foundations (1 of 2): a primitive either exists or is deprecated; no
6349
+ // phase ladder (like metric). `specification` carries a lifecycle. ──
6350
+ "primitive",
6284
6351
  // ── User (5): personas and JTBD atoms are reference data ──────────────────
6285
6352
  "persona",
6286
6353
  "job",
@@ -7352,6 +7419,50 @@ function getDeprecatedTypes() {
7352
7419
  return deprecated;
7353
7420
  }
7354
7421
  var UPG_PROPERTY_MIGRATIONS = {
7422
+ // ── v0.9.12: technical-domain enum widening + data_flow orientation split ──
7423
+ //
7424
+ // Real authoring outgrew four engineering-domain enums. 0.9.12 widens them
7425
+ // (see properties/domains/engineering.ts) and adds a data_flow `orientation`
7426
+ // axis distinct from `direction` (cardinality). These rules dual-read the
7427
+ // legacy values so graphs authored before 0.9.12 stay valid on load:
7428
+ // - data_flow.direction inbound/outbound/internal -> properties.orientation,
7429
+ // and direction resets to unidirectional (all three were one-way).
7430
+ // - integration_pattern.pattern_type "data sync" -> data_sync,
7431
+ // backend_client -> client_library.
7432
+ // - service.service_type backend -> api (use the new `cli` for CLI tools).
7433
+ // - api_contract.protocol HTTP -> REST, HTTP/SSE -> the new SSE value.
7434
+ "0.9.12": [
7435
+ {
7436
+ kind: "remap_property_value",
7437
+ type: "data_flow",
7438
+ property: "direction",
7439
+ value_map: { inbound: "inbound", outbound: "outbound", internal: "internal" },
7440
+ to_property: "orientation",
7441
+ reset_value: "unidirectional",
7442
+ 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)."
7443
+ },
7444
+ {
7445
+ kind: "remap_property_value",
7446
+ type: "integration_pattern",
7447
+ property: "pattern_type",
7448
+ value_map: { "data sync": "data_sync", backend_client: "client_library" },
7449
+ 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.'
7450
+ },
7451
+ {
7452
+ kind: "remap_property_value",
7453
+ type: "service",
7454
+ property: "service_type",
7455
+ value_map: { backend: "api" },
7456
+ reason: "service.service_type `backend` is too generic to canonise; normalised to `api` in 0.9.12 (use `cli` for command-line tools)."
7457
+ },
7458
+ {
7459
+ kind: "remap_property_value",
7460
+ type: "api_contract",
7461
+ property: "protocol",
7462
+ value_map: { HTTP: "REST", "HTTP/SSE": "SSE" },
7463
+ reason: "api_contract.protocol normalised in 0.9.12: HTTP maps to REST and HTTP/SSE maps to the new SSE value."
7464
+ }
7465
+ ],
7355
7466
  // ── v0.8.0: UPG-574 deprecated-property removal pass ───────────────────
7356
7467
  //
7357
7468
  // The seven properties tagged `@deprecated since v0.4.0` that survived the
@@ -7845,6 +7956,25 @@ function migrateNodeProperties(node, fromVersion, toVersion) {
7845
7956
  }
7846
7957
  break;
7847
7958
  }
7959
+ case "remap_property_value": {
7960
+ const props = workingNode.properties;
7961
+ if (!props || !(m.property in props)) break;
7962
+ const oldValue = props[m.property];
7963
+ if (typeof oldValue !== "string" || !(oldValue in m.value_map)) break;
7964
+ const mapped = m.value_map[oldValue];
7965
+ const nextProps = { ...props };
7966
+ if (m.to_property) {
7967
+ nextProps[m.to_property] = mapped;
7968
+ nextProps[m.property] = m.reset_value ?? mapped;
7969
+ changes.push({ kind: "remapped_property_value", property: m.property, to_property: m.to_property, value_changed: true });
7970
+ } else {
7971
+ nextProps[m.property] = mapped;
7972
+ changes.push({ kind: "remapped_property_value", property: m.property, value_changed: mapped !== oldValue });
7973
+ }
7974
+ workingNode.properties = nextProps;
7975
+ mutated = true;
7976
+ break;
7977
+ }
7848
7978
  }
7849
7979
  }
7850
7980
  }
@@ -9298,7 +9428,7 @@ var UPG_PROPERTY_SCHEMA = {
9298
9428
  // ApiContractProperties: API contract.
9299
9429
  api_contract: {
9300
9430
  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" },
9431
+ 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
9432
  version: { type: "string", description: "API version" },
9303
9433
  owner: { type: "string", description: "Maintaining person or team." }
9304
9434
  },
@@ -9779,7 +9909,8 @@ var UPG_PROPERTY_SCHEMA = {
9779
9909
  data_flow: {
9780
9910
  trigger: { type: "string", description: "What triggers the flow" },
9781
9911
  data_type: { type: "string", description: "Type of data transferred" },
9782
- direction: { type: "string", enum: ["unidirectional", "bidirectional"], description: "Direction" },
9912
+ direction: { type: "string", enum: ["unidirectional", "bidirectional"], description: "Direction (cardinality of the link)." },
9913
+ 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
9914
  protocol: { type: "string", enum: ["rest", "graphql", "grpc", "event", "webhook", "file"], description: "Communication protocol" }
9784
9915
  },
9785
9916
  // DataLineageProperties: DataLineage entity.
@@ -10535,7 +10666,7 @@ var UPG_PROPERTY_SCHEMA = {
10535
10666
  },
10536
10667
  // IntegrationPatternProperties: Integration pattern between systems.
10537
10668
  integration_pattern: {
10538
- pattern_type: { type: "string", enum: ["api", "event", "file", "database", "webhook"], description: "Type" },
10669
+ 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
10670
  protocol: { type: "string", description: "Communication protocol" }
10540
10671
  },
10541
10672
  // InteractionSpecProperties: Interaction specification.
@@ -11240,6 +11371,12 @@ var UPG_PROPERTY_SCHEMA = {
11240
11371
  tier_order: { type: "number", description: "Display ordering (1 = first tier shown)" },
11241
11372
  is_highlighted: { type: "boolean", description: "Highlighted as recommended / most popular" }
11242
11373
  },
11374
+ // PrimitiveProperties: A foundational compositional unit a specification defines: the noun products
11375
+ primitive: {
11376
+ primitive_kind: { type: "string", enum: ["data_type", "object", "block", "unit"], description: "The shape of the thing products pass around." },
11377
+ 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." },
11378
+ since: { type: "string", description: "Year or version the primitive was introduced." }
11379
+ },
11243
11380
  // PrivacyPolicyProperties: Privacy policy.
11244
11381
  privacy_policy: {
11245
11382
  version: { type: "string", description: "Version identifier of the policy" },
@@ -11610,7 +11747,7 @@ var UPG_PROPERTY_SCHEMA = {
11610
11747
  },
11611
11748
  // ServiceProperties: Service or microservice.
11612
11749
  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." },
11750
+ 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
11751
  tech_stack: { type: "string[]", description: 'Technologies used (e.g. ["TypeScript", "Postgres", "Redis"])' },
11615
11752
  owner: { type: "string", description: "Owning person or team. Backstage marks this required; strongly recommended." },
11616
11753
  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 +11869,18 @@ var UPG_PROPERTY_SCHEMA = {
11732
11869
  },
11733
11870
  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
11871
  },
11872
+ // SpecificationProperties: A governed specification: a query language, protocol, data format, encoding,
11873
+ specification: {
11874
+ 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." },
11875
+ language_flavor: { type: "string", enum: ["query", "programming", "markup", "styling", "schema", "template"], description: "Set only when `kind` is `language`: the kind of language." },
11876
+ 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." },
11877
+ steward: { type: "string", description: "The governing body or organisation (may later become an `organization` ref)." },
11878
+ openness: { type: "string", enum: ["open", "proprietary"], description: "Whether the specification itself is open or proprietary." },
11879
+ spec_url: { type: "string", description: "URL of the published specification." },
11880
+ current_version: { type: "string", description: "Latest published version string." },
11881
+ since: { type: "string", description: "Year or version the specification was introduced." },
11882
+ conformance: { type: "string", description: "How conformance is tested (test suite, certification program). Optional." }
11883
+ },
11735
11884
  // StakeholderProperties: Stakeholder entity.
11736
11885
  stakeholder: {
11737
11886
  stakeholder_type: { type: "string", enum: ["internal", "external", "investor", "regulator"], description: "Relationship of the stakeholder to the organisation" },
@@ -19717,6 +19866,30 @@ var OPERATIONS_QUALITY_PLAYBOOK = {
19717
19866
  )
19718
19867
  ]
19719
19868
  };
19869
+ var FOUNDATIONS_PLAYBOOK = {
19870
+ id: "playbook:foundations",
19871
+ name: "Foundations",
19872
+ version: "0.9.12",
19873
+ description: "Register the specifications your products implement and expose, and the primitives those specs define, as canonical entities instead of scattered per-product features.",
19874
+ region: "foundations",
19875
+ is_canonical: true,
19876
+ related_framework_ids: [],
19877
+ target_anchor_entity: "specification",
19878
+ creation_sequence: [
19879
+ seqStep(
19880
+ 1,
19881
+ "Specifications",
19882
+ ["specification"],
19883
+ "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."
19884
+ ),
19885
+ seqStep(
19886
+ 2,
19887
+ "Primitives",
19888
+ ["primitive"],
19889
+ "Define the compositional units those specs define (a block, a reference, a query value) as primitives, each linked to its specification."
19890
+ )
19891
+ ]
19892
+ };
19720
19893
  var UPG_PLAYBOOKS = [
19721
19894
  // Region 1 - strategy_outcomes
19722
19895
  STRATEGY_OUTCOMES_PLAYBOOK,
@@ -19739,7 +19912,9 @@ var UPG_PLAYBOOKS = [
19739
19912
  // Region 9 - analytics_data
19740
19913
  ANALYTICS_DATA_PLAYBOOK,
19741
19914
  // Region 10 - operations_quality
19742
- OPERATIONS_QUALITY_PLAYBOOK
19915
+ OPERATIONS_QUALITY_PLAYBOOK,
19916
+ // Region 11 - foundations
19917
+ FOUNDATIONS_PLAYBOOK
19743
19918
  ];
19744
19919
 
19745
19920
  // src/step-sequence.ts
@@ -20481,7 +20656,7 @@ var UPG_DOMAIN_RINGS = [
20481
20656
  id: "build",
20482
20657
  label: "Build",
20483
20658
  description: "How do we construct it?",
20484
- domain_ids: ["engineering", "ai", "automation", "data_analytics", "testing", "devops", "security", "accessibility"]
20659
+ domain_ids: ["engineering", "foundations", "ai", "automation", "data_analytics", "testing", "devops", "security", "accessibility"]
20485
20660
  },
20486
20661
  {
20487
20662
  id: "grow",
@@ -21396,10 +21571,30 @@ var WORKSPACE_GUIDE = {
21396
21571
  { description: "Workspace content is transient by default: promote discoveries to the graph, do not leave them in the workspace" }
21397
21572
  ]
21398
21573
  };
21574
+ var FOUNDATIONS_GUIDE = {
21575
+ domain_id: "foundations",
21576
+ anchor_entity: "specification",
21577
+ creation_sequence: ["specification", "primitive"],
21578
+ patterns: [
21579
+ {
21580
+ name: "Specification defines primitives",
21581
+ description: "A specification governs one or more primitives; each primitive is defined_by the spec and may compose other primitives",
21582
+ entity_types: ["specification", "primitive"],
21583
+ edge_chain: ["primitive_defined_by_specification", "primitive_composes_primitive"]
21584
+ }
21585
+ ],
21586
+ required_bridges: [],
21587
+ anti_patterns: [
21588
+ { description: "A specification scattered as duplicate features across products instead of one canonical with instance_of links" },
21589
+ { description: "Modelling a governed spec as a product: it has no single owner, P&L, or buyer; it is the rulebook products point at" }
21590
+ ]
21591
+ };
21399
21592
  var UPG_DOMAIN_GUIDES = [
21400
21593
  // Nucleus
21401
21594
  PORTFOLIO_GUIDE,
21402
21595
  WORKSPACE_GUIDE,
21596
+ // Foundations (0.9.12)
21597
+ FOUNDATIONS_GUIDE,
21403
21598
  // Ring 1: Understand
21404
21599
  USER_GUIDE,
21405
21600
  USER_RESEARCH_GUIDE,
@@ -24093,6 +24288,7 @@ var UPG_ANTI_PATTERNS = [
24093
24288
  // RelationshipCheck (edge presence/absence) and are promoted here.
24094
24289
  {
24095
24290
  id: "insights-without-evidence",
24291
+ since: "0.9.7",
24096
24292
  name: "Insights without evidence",
24097
24293
  description: "The graph has insight entities but none are backed by a primary-evidence link: no observation yields them (`observation_yields_insight`), no survey response evidences them (`survey_response_evidences_insight`), and no quote is attached (`insight_evidenced_by_quote`). An insight with no evidence behind it is an opinion wearing a research label.",
24098
24294
  structured_condition: {
@@ -24136,6 +24332,7 @@ var UPG_ANTI_PATTERNS = [
24136
24332
  },
24137
24333
  {
24138
24334
  id: "feature-requests-without-provenance",
24335
+ since: "0.9.7",
24139
24336
  name: "Feature requests without provenance",
24140
24337
  description: "The graph has feature_request entities but none trace back to a source: no feedback program collects them (`feedback_program_collects_feature_request`), no customer feedback becomes one (`customer_feedback_becomes_feature_request`), and none originate from a behavioural segment (`feature_request_from_behavioral_segment`). A request with no provenance cannot be weighed against who asked or how many.",
24141
24338
  structured_condition: {
@@ -26690,6 +26887,45 @@ var UPG_REGIONS = [
26690
26887
  crosses_into: "users_needs"
26691
26888
  }
26692
26889
  ]
26890
+ },
26891
+ {
26892
+ id: "foundations",
26893
+ label: "Foundations",
26894
+ order: 11,
26895
+ shape: "polymorphic-target",
26896
+ mental_model: "Shared specs and primitives that many products implement, expose, and conform to.",
26897
+ operators: [
26898
+ "platform architect",
26899
+ "standards author",
26900
+ "staff engineer",
26901
+ "API designer"
26902
+ ],
26903
+ composes_atomic_domains: [
26904
+ "foundations"
26905
+ ],
26906
+ entities: [
26907
+ {
26908
+ type: "specification",
26909
+ role: "anchor"
26910
+ },
26911
+ {
26912
+ type: "primitive",
26913
+ role: "container"
26914
+ }
26915
+ ],
26916
+ anchor: {
26917
+ type: "specification",
26918
+ 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.",
26919
+ outbound_cross_edge_count: 0,
26920
+ inbound_cross_edge_count: 6
26921
+ },
26922
+ intra_edges: [
26923
+ "specification_extends_specification",
26924
+ "specification_competes_with_specification",
26925
+ "primitive_defined_by_specification",
26926
+ "primitive_composes_primitive"
26927
+ ],
26928
+ boundary_edges: []
26693
26929
  }
26694
26930
  ];
26695
26931
  var UPG_REGION_MAP = Object.fromEntries(UPG_REGIONS.map((r) => [r.id, r]));
@@ -27196,7 +27432,7 @@ function isCanonical(text) {
27196
27432
  }
27197
27433
 
27198
27434
  // src/index.ts
27199
- var UPG_VERSION = "0.9.10";
27435
+ var UPG_VERSION = "0.9.12";
27200
27436
  var UPG_FORMAT_VERSION = "0.4.0";
27201
27437
  var MARKDOWN_FORMAT_VERSION = "0.1";
27202
27438
  var UPG_TYPES = getTypes();
@@ -27269,6 +27505,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
27269
27505
  DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK,
27270
27506
  ENGINEERING_PLATFORM_PLAYBOOK,
27271
27507
  EXPERIENCE_DESIGN_BRAND_PLAYBOOK,
27508
+ FOUNDATIONS_PLAYBOOK,
27272
27509
  LEGACY_PRODUCT_STAGES,
27273
27510
  MARKDOWN_FORMAT_VERSION,
27274
27511
  MARKET_COMPETITIVE_PLAYBOOK,