@unified-product-graph/core 0.39.0 → 0.41.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/dist/index.d.cts CHANGED
@@ -6387,6 +6387,76 @@ declare const UPG_EDGE_CATALOG: {
6387
6387
  target_type: string;
6388
6388
  deliberate_only: true;
6389
6389
  };
6390
+ project_contains_epic: {
6391
+ forward_verb: string;
6392
+ reverse_verb: string;
6393
+ classification: "hierarchy";
6394
+ source_type: string;
6395
+ target_type: string;
6396
+ };
6397
+ project_contains_feature: {
6398
+ forward_verb: string;
6399
+ reverse_verb: string;
6400
+ classification: "hierarchy";
6401
+ source_type: string;
6402
+ target_type: string;
6403
+ };
6404
+ project_contains_user_story: {
6405
+ forward_verb: string;
6406
+ reverse_verb: string;
6407
+ classification: "hierarchy";
6408
+ source_type: string;
6409
+ target_type: string;
6410
+ };
6411
+ project_contains_task: {
6412
+ forward_verb: string;
6413
+ reverse_verb: string;
6414
+ classification: "hierarchy";
6415
+ source_type: string;
6416
+ target_type: string;
6417
+ };
6418
+ project_contains_bug: {
6419
+ forward_verb: string;
6420
+ reverse_verb: string;
6421
+ classification: "hierarchy";
6422
+ source_type: string;
6423
+ target_type: string;
6424
+ };
6425
+ project_advances_key_result: {
6426
+ forward_verb: string;
6427
+ reverse_verb: string;
6428
+ classification: "cross-domain";
6429
+ source_type: string;
6430
+ target_type: string;
6431
+ };
6432
+ project_drives_outcome: {
6433
+ forward_verb: string;
6434
+ reverse_verb: string;
6435
+ classification: "cross-domain";
6436
+ source_type: string;
6437
+ target_type: string;
6438
+ };
6439
+ project_assumes_assumption: {
6440
+ forward_verb: string;
6441
+ reverse_verb: string;
6442
+ classification: "cross-domain";
6443
+ source_type: string;
6444
+ target_type: string;
6445
+ };
6446
+ constraint_constrains_project: {
6447
+ forward_verb: string;
6448
+ reverse_verb: string;
6449
+ classification: "cross-domain";
6450
+ source_type: string;
6451
+ target_type: string;
6452
+ };
6453
+ strategic_theme_pursues_project: {
6454
+ forward_verb: string;
6455
+ reverse_verb: string;
6456
+ classification: "cross-domain";
6457
+ source_type: string;
6458
+ target_type: string;
6459
+ };
6390
6460
  milestone_gates_release: {
6391
6461
  forward_verb: string;
6392
6462
  reverse_verb: string;
@@ -11636,6 +11706,29 @@ declare function rotateSlug<T extends Pick<UPGBaseNode, 'slug' | 'aliases'>>(nod
11636
11706
  * the containment guardrail (persona↔job↔need decompositions,
11637
11707
  * product-local reasoning) that must stay in-graph.
11638
11708
  *
11709
+ * WILDCARD ENDPOINTS, STATED PLAINLY (0.41.0, field report `7aaa2f7e`). The
11710
+ * mechanism is described on `isCrossCapable` below, but its consequence was
11711
+ * never written down where a reader looks for it, so a reporter reasonably
11712
+ * read a `decision_*_node` edge's absent scope as unfinished work and proposed
11713
+ * declaring it `provisional`. It cannot be declared: scope is DERIVED, never
11714
+ * authored. And `node` is not a type, so it can never be `portfolio_shared`
11715
+ * and never carries the gate on its own. Therefore:
11716
+ *
11717
+ * **An edge with a `node` wildcard endpoint is `resident` unless its OTHER,
11718
+ * concrete endpoint is a portfolio-shared type, or the edge type is curated
11719
+ * into `UPG_CROSS_EDGE_TYPES` by name.**
11720
+ *
11721
+ * That is why `node_owned_by_team` is cross-capable (team is shared) while
11722
+ * `decision_influences_node`, `decision_produces_node`,
11723
+ * `decision_constrained_by_node` and `risk_threatens_node` are resident:
11724
+ * `decision` and `risk` are not in the shared tier. Only three wildcard edges
11725
+ * are cross-capable today, all of them curated BY NAME.
11726
+ *
11727
+ * Widening one is a two-option decision and BOTH amend a ratified list, which
11728
+ * is why it is not a spec-writer's call: curate the edge type by name, or
11729
+ * promote the concrete endpoint type into the portfolio-shared tier, which
11730
+ * moves every other edge touching that type at the same time.
11731
+ *
11639
11732
  * The gate (`isCrossCapable`) is DERIVED from `EntityTypeMeta.portfolio_shared`, so
11640
11733
  * the guardrail is self-maintaining: a new persona/job/need internal edge is
11641
11734
  * auto-rejected with no list to touch. The gate LOGIC itself never changes the
@@ -27035,7 +27128,7 @@ declare function checkHeaderSealText(text: string): UPGHeaderSealReport;
27035
27128
  * it stamps the `upg_version` field of every `.upg` file written by the SDK.
27036
27129
  * The `check:version-lockstep` gate enforces this at release time.
27037
27130
  */
27038
- declare const UPG_VERSION: "0.39.0";
27131
+ declare const UPG_VERSION: "0.41.0";
27039
27132
  /**
27040
27133
  * The `.upg` JSON document format version. Written to the `upg_version` field.
27041
27134
  * Evolves independently from `UPG_VERSION` (the catalogue version).
package/dist/index.d.ts CHANGED
@@ -6387,6 +6387,76 @@ declare const UPG_EDGE_CATALOG: {
6387
6387
  target_type: string;
6388
6388
  deliberate_only: true;
6389
6389
  };
6390
+ project_contains_epic: {
6391
+ forward_verb: string;
6392
+ reverse_verb: string;
6393
+ classification: "hierarchy";
6394
+ source_type: string;
6395
+ target_type: string;
6396
+ };
6397
+ project_contains_feature: {
6398
+ forward_verb: string;
6399
+ reverse_verb: string;
6400
+ classification: "hierarchy";
6401
+ source_type: string;
6402
+ target_type: string;
6403
+ };
6404
+ project_contains_user_story: {
6405
+ forward_verb: string;
6406
+ reverse_verb: string;
6407
+ classification: "hierarchy";
6408
+ source_type: string;
6409
+ target_type: string;
6410
+ };
6411
+ project_contains_task: {
6412
+ forward_verb: string;
6413
+ reverse_verb: string;
6414
+ classification: "hierarchy";
6415
+ source_type: string;
6416
+ target_type: string;
6417
+ };
6418
+ project_contains_bug: {
6419
+ forward_verb: string;
6420
+ reverse_verb: string;
6421
+ classification: "hierarchy";
6422
+ source_type: string;
6423
+ target_type: string;
6424
+ };
6425
+ project_advances_key_result: {
6426
+ forward_verb: string;
6427
+ reverse_verb: string;
6428
+ classification: "cross-domain";
6429
+ source_type: string;
6430
+ target_type: string;
6431
+ };
6432
+ project_drives_outcome: {
6433
+ forward_verb: string;
6434
+ reverse_verb: string;
6435
+ classification: "cross-domain";
6436
+ source_type: string;
6437
+ target_type: string;
6438
+ };
6439
+ project_assumes_assumption: {
6440
+ forward_verb: string;
6441
+ reverse_verb: string;
6442
+ classification: "cross-domain";
6443
+ source_type: string;
6444
+ target_type: string;
6445
+ };
6446
+ constraint_constrains_project: {
6447
+ forward_verb: string;
6448
+ reverse_verb: string;
6449
+ classification: "cross-domain";
6450
+ source_type: string;
6451
+ target_type: string;
6452
+ };
6453
+ strategic_theme_pursues_project: {
6454
+ forward_verb: string;
6455
+ reverse_verb: string;
6456
+ classification: "cross-domain";
6457
+ source_type: string;
6458
+ target_type: string;
6459
+ };
6390
6460
  milestone_gates_release: {
6391
6461
  forward_verb: string;
6392
6462
  reverse_verb: string;
@@ -11636,6 +11706,29 @@ declare function rotateSlug<T extends Pick<UPGBaseNode, 'slug' | 'aliases'>>(nod
11636
11706
  * the containment guardrail (persona↔job↔need decompositions,
11637
11707
  * product-local reasoning) that must stay in-graph.
11638
11708
  *
11709
+ * WILDCARD ENDPOINTS, STATED PLAINLY (0.41.0, field report `7aaa2f7e`). The
11710
+ * mechanism is described on `isCrossCapable` below, but its consequence was
11711
+ * never written down where a reader looks for it, so a reporter reasonably
11712
+ * read a `decision_*_node` edge's absent scope as unfinished work and proposed
11713
+ * declaring it `provisional`. It cannot be declared: scope is DERIVED, never
11714
+ * authored. And `node` is not a type, so it can never be `portfolio_shared`
11715
+ * and never carries the gate on its own. Therefore:
11716
+ *
11717
+ * **An edge with a `node` wildcard endpoint is `resident` unless its OTHER,
11718
+ * concrete endpoint is a portfolio-shared type, or the edge type is curated
11719
+ * into `UPG_CROSS_EDGE_TYPES` by name.**
11720
+ *
11721
+ * That is why `node_owned_by_team` is cross-capable (team is shared) while
11722
+ * `decision_influences_node`, `decision_produces_node`,
11723
+ * `decision_constrained_by_node` and `risk_threatens_node` are resident:
11724
+ * `decision` and `risk` are not in the shared tier. Only three wildcard edges
11725
+ * are cross-capable today, all of them curated BY NAME.
11726
+ *
11727
+ * Widening one is a two-option decision and BOTH amend a ratified list, which
11728
+ * is why it is not a spec-writer's call: curate the edge type by name, or
11729
+ * promote the concrete endpoint type into the portfolio-shared tier, which
11730
+ * moves every other edge touching that type at the same time.
11731
+ *
11639
11732
  * The gate (`isCrossCapable`) is DERIVED from `EntityTypeMeta.portfolio_shared`, so
11640
11733
  * the guardrail is self-maintaining: a new persona/job/need internal edge is
11641
11734
  * auto-rejected with no list to touch. The gate LOGIC itself never changes the
@@ -27035,7 +27128,7 @@ declare function checkHeaderSealText(text: string): UPGHeaderSealReport;
27035
27128
  * it stamps the `upg_version` field of every `.upg` file written by the SDK.
27036
27129
  * The `check:version-lockstep` gate enforces this at release time.
27037
27130
  */
27038
- declare const UPG_VERSION: "0.39.0";
27131
+ declare const UPG_VERSION: "0.41.0";
27039
27132
  /**
27040
27133
  * The `.upg` JSON document format version. Written to the `upg_version` field.
27041
27134
  * Evolves independently from `UPG_VERSION` (the catalogue version).
package/dist/index.js CHANGED
@@ -2735,6 +2735,93 @@ var UPG_EDGE_CATALOG = {
2735
2735
  // arbitrary work. deliberate_only because project membership is authored,
2736
2736
  // never inferred from co-occurrence.
2737
2737
  project_delivers_work_item: { forward_verb: "delivers", reverse_verb: "delivered_by", classification: "semantic", source_type: "project", target_type: "node", deliberate_only: true },
2738
+ // ── project as a first-class home for work (0.41.0, Captain-ratified) ──────
2739
+ //
2740
+ // Teams model in Linear, where an Initiative holds Projects and a Project
2741
+ // holds Issues; UPG must express that natively or every sync leaves a reader
2742
+ // guessing which node is which. `project_implements_initiative` already
2743
+ // carries the upper relation (read from the project's side). These five carry
2744
+ // the lower one, and the set is exactly what the Linear issue-type map
2745
+ // produces: an issue becomes a feature, bug, task, user_story or epic.
2746
+ //
2747
+ // WHY THESE ARE CONTAINMENT AND `project_delivers_work_item` IS NOT.
2748
+ // 0.33.0 widened `project_delivers_epic` into the polymorphic
2749
+ // `project_delivers_work_item` and ruled, correctly, that it could not be a
2750
+ // `contains` verb: containment obliges a `UPG_VALID_CHILDREN` pair keyed by
2751
+ // CONCRETE type names, which a wildcard endpoint can never supply. That
2752
+ // argument forecloses containment for the wildcard; it does not foreclose
2753
+ // containment for concrete pairs, which is what these are. So this is not a
2754
+ // reversal of that ruling — it is the half the wildcard could not reach.
2755
+ //
2756
+ // A KNOWN SHADOW PAIR, ACCEPTED DELIBERATELY (Captain, 2026-09-04). Both an
2757
+ // authored `project_contains_task` and a `project_delivers_work_item` can
2758
+ // name the same pair. That duplication is real and is taken on purpose, to
2759
+ // avoid a migration on live `project_delivers_work_item` instances. The
2760
+ // precedence is stated so it is never ambiguous: CONTAINMENT IS THE PARENT
2761
+ // AXIS AND WINS; the polymorphic edge stays the reference for targets outside
2762
+ // the concrete set. If a real graph is ever observed holding both for one
2763
+ // pair, that is the signal to retire the polymorphic edge for these five
2764
+ // types and migrate — the option deliberately not taken here.
2765
+ project_contains_epic: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "project", target_type: "epic" },
2766
+ project_contains_feature: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "project", target_type: "feature" },
2767
+ project_contains_user_story: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "project", target_type: "user_story" },
2768
+ project_contains_task: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "project", target_type: "task" },
2769
+ project_contains_bug: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "project", target_type: "bug" },
2770
+ // ── project's strategic reach (0.41.0) ────────────────────────────────────
2771
+ //
2772
+ // The parity half. `initiative` and `project` are two granularities of the
2773
+ // same idea, and teams pick one: Linear-shaped teams put the dated, owned,
2774
+ // key-result-linked bet in a PROJECT, while the standard's `initiative`
2775
+ // carried all the strategic reach and `project` reached milestone,
2776
+ // deliverable and a wildcard. A team choosing the honest name for its
2777
+ // granularity should not lose its connection to strategy.
2778
+ //
2779
+ // Mirrors of initiative's edges on the FORWARD verbs, so a reader moving
2780
+ // between the two granularities is never re-learning the vocabulary. The
2781
+ // mirror is deliberately not total, and the two places it stops are both
2782
+ // correct rather than sloppy:
2783
+ //
2784
+ // REVERSE VERBS. `strategic_theme_pursues_initiative` reverses to
2785
+ // `pursued_under`, which reads as membership in a theme's programme.
2786
+ // `strategic_theme_pursues_project` reverses to `pursued_by`, because a
2787
+ // project is the vehicle doing the pursuing, not a subdivision of the
2788
+ // theme. Same forward verb, different relationship on the way back.
2789
+ //
2790
+ // CLASSIFICATION. Initiative's five are causal / cross-domain / hierarchy
2791
+ // / semantic; all five of these are `cross-domain`. That is domain
2792
+ // arithmetic, not inconsistency: `initiative` sits in `strategy` alongside
2793
+ // its targets, so a `cross-domain` classification there would be T1.7
2794
+ // same-domain debt, while `project` is `program_mgmt` and every target
2795
+ // here is `strategy`, so `cross-domain` is the measured truth and adds
2796
+ // nothing to that debt.
2797
+ //
2798
+ // CROSS-PRODUCT SCOPE, ruled deliberately (2026-09-04, raised by Troi during
2799
+ // the 0.41.0 editorial pass). Three of initiative's carry
2800
+ // `cross_product_eligible: true` and none of these do, which looks at first
2801
+ // like reinstating at the portfolio seam the reach this change removes
2802
+ // elsewhere. It is not: `key_result`, `outcome` and `strategic_theme` are
2803
+ // all `portfolio_shared`, so these edges already resolve `provisional` and
2804
+ // ARE authorable across graphs today, with a warning, not rejected. The
2805
+ // difference is curated (hard-allow, silent) versus provisional (allowed,
2806
+ // warned). Left provisional on purpose: the curated set is a ratified
2807
+ // snapshot that grows on measured need, and no field evidence yet shows
2808
+ // anyone authoring these across graphs. Promote to curated when it appears,
2809
+ // which is a one-flag change per edge.
2810
+ //
2811
+ // Deliberately NOT mirrored: `initiative_enters_market_segment`,
2812
+ // `_realises_value_proposition`, `_unlocks_revenue_stream` and
2813
+ // `_raises_strategic_question`. A delivery-scoped project does not enter a
2814
+ // market or unlock a revenue stream; that reach belongs to the coarser level,
2815
+ // and copying it down would make the two types synonyms rather than
2816
+ // granularities. `capability` needs no edge either: a project reaches it
2817
+ // through the features it now contains (`capability_implemented_by_feature`),
2818
+ // and `decision` already reaches a project through the polymorphic
2819
+ // `decision_influences_node`.
2820
+ project_advances_key_result: { forward_verb: "advances", reverse_verb: "advanced_by", classification: "cross-domain", source_type: "project", target_type: "key_result" },
2821
+ project_drives_outcome: { forward_verb: "drives", reverse_verb: "driven_by", classification: "cross-domain", source_type: "project", target_type: "outcome" },
2822
+ project_assumes_assumption: { forward_verb: "assumes", reverse_verb: "assumed_by", classification: "cross-domain", source_type: "project", target_type: "assumption" },
2823
+ constraint_constrains_project: { forward_verb: "constrains", reverse_verb: "constrained_by", classification: "cross-domain", source_type: "constraint", target_type: "project" },
2824
+ strategic_theme_pursues_project: { forward_verb: "pursues", reverse_verb: "pursued_by", classification: "cross-domain", source_type: "strategic_theme", target_type: "project" },
2738
2825
  milestone_gates_release: { forward_verb: "gates", reverse_verb: "gated_by", classification: "cross-domain", source_type: "milestone", target_type: "release" },
2739
2826
  milestone_triggers_release: { forward_verb: "triggers", reverse_verb: "triggered_by", classification: "cross-domain", source_type: "milestone", target_type: "release" },
2740
2827
  deliverable_ships_feature: { forward_verb: "ships", reverse_verb: "shipped_by", classification: "cross-domain", source_type: "deliverable", target_type: "feature" },
@@ -7727,7 +7814,7 @@ var UPG_VALID_CHILDREN = {
7727
7814
  subscription: ["invoice"],
7728
7815
  // ── Program Management hierarchy ─────────────────────────────────────────────
7729
7816
  program: ["project", "risk_register", "change_request", "resource_allocation", "status_report"],
7730
- project: ["milestone", "deliverable"],
7817
+ project: ["milestone", "deliverable", "epic", "feature", "user_story", "task", "bug"],
7731
7818
  risk_register: ["risk"],
7732
7819
  // ── Accessibility hierarchy ──────────────────────────────────────────────────
7733
7820
  a11y_standard: ["a11y_guideline", "a11y_audit", "a11y_annotation"],
@@ -33894,7 +33981,7 @@ function checkHeaderSealText(text) {
33894
33981
  }
33895
33982
 
33896
33983
  // src/index.ts
33897
- var UPG_VERSION = "0.39.0";
33984
+ var UPG_VERSION = "0.41.0";
33898
33985
  var UPG_FORMAT_VERSION = "0.4.0";
33899
33986
  var MARKDOWN_FORMAT_VERSION = "0.1";
33900
33987
  var UPG_TYPES = getTypes();