@unified-product-graph/core 0.19.0 → 0.20.1

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.js CHANGED
@@ -92,7 +92,7 @@ var UPG_DOMAINS = [
92
92
  {
93
93
  id: "product_spec",
94
94
  label: "Product Specification",
95
- description: "What you are building and shipping. Feature areas group related capabilities. Features, epics, and user stories break work down. Acceptance criteria define done. Tasks and bugs track execution. Releases and changelogs mark what shipped. Roadmaps and roadmap items plan what comes next. Roadmap themes group roadmap work around the customer problem it solves, one level down from the strategic themes in Strategy. Translates Strategy into Engineering and tracks delivery through Program Management.",
95
+ description: "What you are building and shipping. Feature areas group related capabilities. Features, epics, and user stories break work down. Acceptance criteria define done. Tasks and bugs track execution. Releases and changelogs mark what shipped. Roadmaps and roadmap items plan what comes next. Roadmap themes group roadmap work around the customer problem it solves, one level down from the strategic themes in Strategy. Planning cycles are the cadence axis: the named, dated, self-nesting intervals (sprint, iteration, quarter, program increment, cooldown) that work is scheduled through. Translates Strategy into Engineering and tracks delivery through Program Management.",
96
96
  types: [
97
97
  "feature",
98
98
  "feature_area",
@@ -105,7 +105,8 @@ var UPG_DOMAINS = [
105
105
  "roadmap",
106
106
  "roadmap_item",
107
107
  "roadmap_theme",
108
- "changelog"
108
+ "changelog",
109
+ "planning_cycle"
109
110
  ]
110
111
  },
111
112
  {
@@ -683,6 +684,14 @@ var UPG_ENTITY_META = [
683
684
  { name: "theme", type_id: "ent_080", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.9.0", replacement: "roadmap_theme" },
684
685
  { name: "roadmap_theme", type_id: "ent_351", maturity: "stable", since: "0.1.0" },
685
686
  { name: "changelog", type_id: "ent_081", maturity: "stable", since: "0.1.0" },
687
+ // (0.20.0) The cadence axis: a named, dated interval that work flows through
688
+ // and which nests (a program-increment contains iterations; a cycle contains
689
+ // its cooldown). One self-nesting type with a `cadence_kind` discriminator
690
+ // spans sprint / iteration / quarter / PI / cooldown rather than minting a
691
+ // type per methodology. `portfolio_shared` because a coarse cycle is an
692
+ // org-shared interval an objective in a rollup graph and a story in a product
693
+ // graph both schedule into.
694
+ { name: "planning_cycle", type_id: "ent_358", maturity: "proposed", since: "0.20.0", portfolio_shared: true },
686
695
  // ── Engineering ──
687
696
  { name: "bounded_context", type_id: "ent_082", maturity: "stable", since: "0.1.0" },
688
697
  { name: "service", type_id: "ent_083", maturity: "stable", since: "0.1.0" },
@@ -1299,6 +1308,10 @@ var UPG_EDGE_CATALOG = {
1299
1308
  strategic_pillar_enables_capability: { forward_verb: "enables", reverse_verb: "enabled_by", classification: "hierarchy", source_type: "strategic_pillar", target_type: "capability" },
1300
1309
  strategic_pillar_delivers_value_stream: { forward_verb: "delivers", reverse_verb: "delivered_by", classification: "hierarchy", source_type: "strategic_pillar", target_type: "value_stream" },
1301
1310
  strategic_pillar_decided_via_decision: { forward_verb: "decided_via", reverse_verb: "decided_for", classification: "hierarchy", source_type: "strategic_pillar", target_type: "decision" },
1311
+ // 0.20.1: the durable pillar's own north-star, mirroring objective_measured_by_metric
1312
+ // one level up the cascade. A strategic_pillar is multi-year and org-wide, so its
1313
+ // measuring metric is portfolio-shared just like the pillar itself.
1314
+ strategic_pillar_measured_by_metric: { forward_verb: "measured_by", reverse_verb: "measures", classification: "hierarchy", source_type: "strategic_pillar", target_type: "metric", cross_product_eligible: true },
1302
1315
  strategic_theme_pursues_initiative: { forward_verb: "pursues", reverse_verb: "pursued_under", classification: "hierarchy", source_type: "strategic_theme", target_type: "initiative", cross_product_eligible: true },
1303
1316
  // v0.9.0 (UPG-660): the soft bridge from the annual strategy focus area to the
1304
1317
  // roadmap grouping that realises it. Semantic, NOT hierarchy: strategic_theme and
@@ -1331,6 +1344,17 @@ var UPG_EDGE_CATALOG = {
1331
1344
  strategic_theme_measured_by_key_result: { forward_verb: "measured_by", reverse_verb: "measures", classification: "causal", source_type: "strategic_theme", target_type: "key_result", cross_product_eligible: true },
1332
1345
  strategic_theme_contains_objective: { forward_verb: "contains", reverse_verb: "rolls_up_to", classification: "hierarchy", source_type: "strategic_theme", target_type: "objective", cross_product_eligible: true },
1333
1346
  initiative_assumes_assumption: { forward_verb: "assumes", reverse_verb: "assumed_by", classification: "hierarchy", source_type: "initiative", target_type: "assumption" },
1347
+ // 0.20.1: the G6 alignment-sheet bridge edges — an initiative reaches directly
1348
+ // into the OKR ladder and the roadmap, rather than only via its parent
1349
+ // strategic_theme. `advances_key_result` is intra-strategy execution (both
1350
+ // ends sit in the strategy domain), so causal not cross-domain; the KR is
1351
+ // portfolio-shared like the initiative, so the pair is cross-product-eligible.
1352
+ initiative_advances_key_result: { forward_verb: "advances", reverse_verb: "advanced_by", classification: "causal", source_type: "initiative", target_type: "key_result", cross_product_eligible: true },
1353
+ // `delivered_via_roadmap_theme` mirrors strategic_theme_realised_by_roadmap_theme:
1354
+ // a semantic cross-reference between the strategy spine and the roadmap spine,
1355
+ // not containment. roadmap_theme is not portfolio-shared, so this stays
1356
+ // within-graph (no cross_product_eligible flag).
1357
+ initiative_delivered_via_roadmap_theme: { forward_verb: "delivered_via", reverse_verb: "delivers", classification: "semantic", source_type: "initiative", target_type: "roadmap_theme" },
1334
1358
  initiative_drives_outcome: { forward_verb: "drives", reverse_verb: "driven_by", classification: "cross-domain", source_type: "initiative", target_type: "outcome", cross_product_eligible: true },
1335
1359
  capability_enables_value_stream: { forward_verb: "enables", reverse_verb: "enabled_by", classification: "cross-domain", source_type: "capability", target_type: "value_stream" },
1336
1360
  // v0.5.2 (UPG-528): three capability-anchored edges that complete the
@@ -1366,6 +1390,8 @@ var UPG_EDGE_CATALOG = {
1366
1390
  product_builds_feature: { forward_verb: "builds", reverse_verb: "built_by", classification: "hierarchy", source_type: "product", target_type: "feature" },
1367
1391
  product_ships_via_release: { forward_verb: "ships_via", reverse_verb: "ships", classification: "hierarchy", source_type: "product", target_type: "release" },
1368
1392
  product_plans_via_roadmap: { forward_verb: "plans_via", reverse_verb: "plans_for", classification: "hierarchy", source_type: "product", target_type: "roadmap" },
1393
+ // (0.20.0) top-level attach for the cadence axis, mirroring product_plans_via_roadmap.
1394
+ product_runs_planning_cycle: { forward_verb: "runs", reverse_verb: "run_by", classification: "hierarchy", source_type: "product", target_type: "planning_cycle" },
1369
1395
  product_categorises_by_roadmap_theme: { forward_verb: "categorises_by", reverse_verb: "categorises", classification: "hierarchy", source_type: "product", target_type: "roadmap_theme" },
1370
1396
  feature_area_contains_feature: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "feature_area", target_type: "feature" },
1371
1397
  feature_area_contains_feature_area: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "feature_area", target_type: "feature_area" },
@@ -1404,6 +1430,33 @@ var UPG_EDGE_CATALOG = {
1404
1430
  roadmap_item_references_feature: { forward_verb: "references", reverse_verb: "referenced_by", classification: "cross-domain", source_type: "roadmap_item", target_type: "feature" },
1405
1431
  feature_decomposes_into_task: { forward_verb: "decomposes_into", reverse_verb: "implements", classification: "hierarchy", source_type: "feature", target_type: "task" },
1406
1432
  task_has_subtask: { forward_verb: "has_subtask", reverse_verb: "is_subtask_of", classification: "hierarchy", source_type: "task", target_type: "task" },
1433
+ // ── Planning cadence (0.20.0) ────────────────────────────────────────────────
1434
+ // The cadence axis. E1 self-nests the interval (a program-increment contains
1435
+ // iterations; a cycle contains its cooldown), mirroring team_contains_team.
1436
+ planning_cycle_contains_planning_cycle: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "planning_cycle", target_type: "planning_cycle" },
1437
+ // E3: scheduling work into a cycle is a deliberate authoring act, not a
1438
+ // containment nesting — user_story keeps its feature/epic parent and is merely
1439
+ // referenced by the cycle. deliberate_only so the generic-inference chokepoints
1440
+ // never auto-materialise it.
1441
+ planning_cycle_schedules_user_story: { forward_verb: "schedules", reverse_verb: "scheduled_in", classification: "semantic", source_type: "planning_cycle", target_type: "user_story", deliberate_only: true },
1442
+ // E2: the OKR cycle-scoping anchor. objective -> planning_cycle. planning_cycle
1443
+ // is portfolio_shared, so this passes the cross-scope gate and ships as
1444
+ // PROVISIONAL (allowed cross-product with a write-time warning, self-maintaining,
1445
+ // never added to the curated set) rather than carrying a cross_product_eligible
1446
+ // flag, which would make it curated. Eligibility is permission, not obligation.
1447
+ objective_scoped_to_planning_cycle: { forward_verb: "scoped_to", reverse_verb: "scopes", classification: "semantic", source_type: "objective", target_type: "planning_cycle" },
1448
+ // F: the promoted `strategic_theme.time_horizon`. Same shape as E2 — a theme's
1449
+ // bounded period becomes a scoping edge onto a shared, dated, nestable interval.
1450
+ // Provisional (planning_cycle portfolio_shared). The property stays @deprecated.
1451
+ strategic_theme_scoped_to_planning_cycle: { forward_verb: "scoped_to", reverse_verb: "scopes", classification: "semantic", source_type: "strategic_theme", target_type: "planning_cycle" },
1452
+ // Gap 2 — polymorphic issue links over the work-item set {feature, epic,
1453
+ // user_story, task, bug}. Endpoint-polymorphic (node -> node, registered in
1454
+ // UPG_POLYMORPHIC_EDGE_KEYS) so one edge covers every level teams link at; the
1455
+ // `work_item_` key names the intended semantic domain. All deliberate_only: an
1456
+ // issue link is authored, never inferred from hierarchy.
1457
+ work_item_blocks_work_item: { forward_verb: "blocks", reverse_verb: "blocked_by", classification: "causal", source_type: "node", target_type: "node", deliberate_only: true },
1458
+ work_item_relates_to_work_item: { forward_verb: "relates_to", reverse_verb: "relates_to", classification: "semantic", source_type: "node", target_type: "node", deliberate_only: true },
1459
+ work_item_duplicates_work_item: { forward_verb: "duplicates", reverse_verb: "duplicated_by", classification: "semantic", source_type: "node", target_type: "node", deliberate_only: true },
1407
1460
  // 2.3 Legal Domain
1408
1461
  product_owned_by_legal_entity: { forward_verb: "owned_by", reverse_verb: "owns", classification: "hierarchy", source_type: "product", target_type: "legal_entity" },
1409
1462
  legal_entity_protects_ip_asset: { forward_verb: "protects", reverse_verb: "protected_by", classification: "hierarchy", source_type: "legal_entity", target_type: "ip_asset" },
@@ -3187,7 +3240,13 @@ var UPG_POLYMORPHIC_EDGE_KEYS = [
3187
3240
  // Framework exercises (an exercise can include any entity type)
3188
3241
  "framework_exercise_includes_node",
3189
3242
  // Universal classification (any node classified against a classification_value)
3190
- "node_classified_as_classification_value"
3243
+ "node_classified_as_classification_value",
3244
+ // Work-item issue links (0.20.0): endpoint-polymorphic over the work-item set
3245
+ // {feature, epic, user_story, task, bug}. The `work_item_` key names the
3246
+ // intended semantic domain; the endpoints are the `node` wildcard.
3247
+ "work_item_blocks_work_item",
3248
+ "work_item_relates_to_work_item",
3249
+ "work_item_duplicates_work_item"
3191
3250
  ];
3192
3251
  var _POLY_KEY_SET = new Set(UPG_POLYMORPHIC_EDGE_KEYS);
3193
3252
  function isPolymorphicEdge(key) {
@@ -3377,6 +3436,7 @@ var UPG_VALID_CHILDREN = {
3377
3436
  "release",
3378
3437
  "roadmap",
3379
3438
  "roadmap_theme",
3439
+ "planning_cycle",
3380
3440
  // Engineering
3381
3441
  "bounded_context",
3382
3442
  "code_repository",
@@ -3554,7 +3614,11 @@ var UPG_VALID_CHILDREN = {
3554
3614
  // That success-measurement asymmetry is the load-bearing distinction.
3555
3615
  vision: ["mission"],
3556
3616
  mission: ["strategic_pillar"],
3557
- strategic_pillar: ["strategic_theme", "capability", "value_stream", "decision"],
3617
+ // 0.20.1: metric added as a strategic_pillar child, backing
3618
+ // strategic_pillar_measured_by_metric (hierarchy) — the pillar's own
3619
+ // north-star, mirrored from objective: ['key_result', 'metric', ...] one
3620
+ // level down the cascade.
3621
+ strategic_pillar: ["strategic_theme", "capability", "value_stream", "decision", "metric"],
3558
3622
  // v0.5.4 (UPG-511): objectives are the specific quarterly bets *within* a
3559
3623
  // strategic theme. The theme is the multi-quarter focus area; objectives are
3560
3624
  // subordinate. Pairs with `strategic_theme_contains_objective` in the edge
@@ -3645,6 +3709,13 @@ var UPG_VALID_CHILDREN = {
3645
3709
  epic: ["user_story"],
3646
3710
  user_story: ["acceptance_criterion"],
3647
3711
  task: ["task"],
3712
+ // planning_cycle (0.20.0): the cadence axis self-nests (a program-increment
3713
+ // contains iterations; a cycle contains its cooldown) via
3714
+ // planning_cycle_contains_planning_cycle. It does NOT contain user_story as a
3715
+ // child: scheduling work into a cycle is the semantic, deliberate-only
3716
+ // planning_cycle_schedules_user_story edge, so a story keeps its feature/epic
3717
+ // containment parent and is merely referenced by the cycle.
3718
+ planning_cycle: ["planning_cycle"],
3648
3719
  // ── Engineering hierarchy ───────────────────────────────────────────────────
3649
3720
  bounded_context: [
3650
3721
  "service",
@@ -6288,6 +6359,16 @@ var SPECIFICATION_LIFECYCLE = {
6288
6359
  }
6289
6360
  ]
6290
6361
  };
6362
+ var PLANNING_CYCLE_LIFECYCLE = {
6363
+ entity_type: "planning_cycle",
6364
+ initial_phase: "planned",
6365
+ terminal_phases: ["closed"],
6366
+ phases: [
6367
+ { id: "planned", label: "Planned", description: "The cycle is being shaped: dates set, goal written, work scheduled into it. It has not started yet.", transitions_to: ["active"] },
6368
+ { id: "active", label: "Active", description: "The team is inside the interval; work is flowing through it.", transitions_to: ["closed"] },
6369
+ { id: "closed", label: "Closed", description: "The interval has ended. Scheduled work has shipped, carried over, or been dropped. May reopen if the team extends or reruns it.", transitions_to: ["active"] }
6370
+ ]
6371
+ };
6291
6372
  var UPG_LIFECYCLES = [
6292
6373
  // Product (root)
6293
6374
  PRODUCT_LIFECYCLE,
@@ -6331,6 +6412,7 @@ var UPG_LIFECYCLES = [
6331
6412
  TASK_LIFECYCLE,
6332
6413
  BUG_LIFECYCLE,
6333
6414
  ROADMAP_ITEM_LIFECYCLE,
6415
+ PLANNING_CYCLE_LIFECYCLE,
6334
6416
  IP_ASSET_LIFECYCLE,
6335
6417
  CONTRACT_LIFECYCLE,
6336
6418
  DESIGN_CONCEPT_LIFECYCLE,
@@ -10513,7 +10595,9 @@ var UPG_PROPERTY_SCHEMA = {
10513
10595
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Urgency relative to other work. Independent of `bug_severity` (a critical bug can have low priority if rare)." },
10514
10596
  assignee: { type: "string", description: "Assigned person. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
10515
10597
  due_date: { type: "string", description: "ISO date due. Often tied to a release gate or SLA." },
10516
- labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." }
10598
+ labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." },
10599
+ workflow_state: { type: "string", description: 'The source tool\'s raw custom workflow state, verbatim and opaque (e.g. "In Review", "QA", "Needs Triage"). Non-canonical and never reasoned over: it exists to round-trip an import losslessly. Map it onto a canonical bucket with `workflow_state_category`; canonical `status` stays the sole reasoning axis.' },
10600
+ workflow_state_category: { type: "string", description: 'Canonical bucket the raw `workflow_state` maps onto for reasoning (a source "In Review" and "QA" might both map to a verification phase). Optional companion to `workflow_state`: it lets a graph reason over an imported custom workflow without promoting the raw label to `status`.' }
10517
10601
  },
10518
10602
  // BuildArtifactProperties: Build artifact.
10519
10603
  build_artifact: {
@@ -11167,7 +11251,9 @@ var UPG_PROPERTY_SCHEMA = {
11167
11251
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Task-level priority" },
11168
11252
  owner: { type: "string", description: "Responsible person or team. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
11169
11253
  start_date: { type: "string", description: "ISO date work begins" },
11170
- target_date: { type: "string", description: "ISO date work completes" }
11254
+ target_date: { type: "string", description: "ISO date work completes" },
11255
+ workflow_state: { type: "string", description: 'The source tool\'s raw custom workflow state, verbatim and opaque (e.g. "In Review", "QA", "Needs Triage"). Non-canonical and never reasoned over: it exists to round-trip an import losslessly. Map it onto a canonical bucket with `workflow_state_category`; canonical `status` stays the sole reasoning axis.' },
11256
+ workflow_state_category: { type: "string", description: 'Canonical bucket the raw `workflow_state` maps onto for reasoning (a source "In Review" and "QA" might both map to a verification phase). Optional companion to `workflow_state`: it lets a graph reason over an imported custom workflow without promoting the raw label to `status`.' }
11171
11257
  },
11172
11258
  // ErrorBudgetProperties: Error budget.
11173
11259
  error_budget: {
@@ -11347,7 +11433,9 @@ var UPG_PROPERTY_SCHEMA = {
11347
11433
  owner: { type: "string", description: "Responsible person or team. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
11348
11434
  start_date: { type: "string", description: "ISO date work begins" },
11349
11435
  target_date: { type: "string", description: "ISO date work completes" },
11350
- health: { type: "string", enum: ["on_track", "at_risk", "off_track"], description: "Delivery health" }
11436
+ health: { type: "string", enum: ["on_track", "at_risk", "off_track"], description: "Delivery health" },
11437
+ workflow_state: { type: "string", description: 'The source tool\'s raw custom workflow state, verbatim and opaque (e.g. "In Review", "QA", "Needs Triage"). Non-canonical and never reasoned over: it exists to round-trip an import losslessly. Map it onto a canonical bucket with `workflow_state_category`; canonical `status` stays the sole reasoning axis.' },
11438
+ workflow_state_category: { type: "string", description: 'Canonical bucket the raw `workflow_state` maps onto for reasoning (a source "In Review" and "QA" might both map to a verification phase). Optional companion to `workflow_state`: it lets a graph reason over an imported custom workflow without promoting the raw label to `status`.' }
11351
11439
  },
11352
11440
  // FeatureAreaProperties: A structural grouping of related features within a product.
11353
11441
  feature_area: {
@@ -12267,6 +12355,16 @@ var UPG_PROPERTY_SCHEMA = {
12267
12355
  conversion_rate: { type: "number", description: "Percentage of deals that advance from this stage", modifier: "snapshot" },
12268
12356
  avg_days_in_stage: { type: "number", description: "Average number of days deals spend in this stage" }
12269
12357
  },
12358
+ // PlanningCycleProperties: Planning cycle: a named, dated interval work flows through, which nests.
12359
+ planning_cycle: {
12360
+ cadence_kind: { type: "string", enum: ["period", "iteration", "buffer"], description: "Methodology-neutral granularity of this interval. `period` is a coarse container (quarter / PI / OKR-cycle scale); `iteration` is a fine execution box (sprint / cycle); `buffer` is between-box slack (cooldown). Required: it is the discriminator that lets one type stand in for every methodology." },
12361
+ cadence_label: { type: "string", description: 'The source methodology term verbatim ("sprint", "cycle", "PI", "quarter", "cooldown"). The dual-band label: `cadence_kind` is the canonical granularity reasoned over; `cadence_label` preserves what the team actually calls it.' },
12362
+ starts_on: { type: "string", description: "ISO date the interval opens. A cycle is concretely dated, unlike a coarse `time_horizon` label." },
12363
+ ends_on: { type: "string", description: "ISO date the interval closes." },
12364
+ sequence: { type: "number", description: "The cycle / iteration number (e.g. Sprint 47, PI 3)." },
12365
+ goal: { type: "string", description: "The interval's goal or focus: what this cadence box is for." },
12366
+ appetite: { type: "string", description: 'Shape Up appetite: the fixed time budget a cycle is willing to spend on a bet (e.g. "6 weeks", "2 weeks").' }
12367
+ },
12270
12368
  // PlaybookProperties: Customer success playbook.
12271
12369
  playbook: {
12272
12370
  playbook_type: { type: "string", enum: ["onboarding", "expansion", "renewal", "rescue", "other"], description: "Scenario this playbook addresses" },
@@ -12818,7 +12916,7 @@ var UPG_PROPERTY_SCHEMA = {
12818
12916
  // StrategicThemeProperties: StrategicTheme entity.
12819
12917
  strategic_theme: {
12820
12918
  owner: { type: "string", description: "Owning person or team. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
12821
- time_horizon: { type: "string", description: 'Bounded period this theme is active (a theme is time-bound, within a pillar). @example "Q1 2026", "FY26"' },
12919
+ time_horizon: { type: "string", description: 'Bounded period this theme is active (a theme is time-bound, within a pillar). @deprecated since 0.20.0. Promote the period to a `planning_cycle` node and link it with the `strategic_theme_scoped_to_planning_cycle` edge, which points at a shared, dated, nestable interval instead of a drifting per-theme string. Kept (not removed) for back-compat; removal is a later major. `strategic_pillar.time_horizon` stays as-is (a durable pillar horizon is genuinely open-ended, not a dated cycle). @example "Q1 2026", "FY26"' },
12822
12920
  description: { type: "string", description: "Short narrative of this time-bound thrust within its pillar" },
12823
12921
  scope: { type: "string", description: "What the theme explicitly includes or excludes" }
12824
12922
  },
@@ -12914,7 +13012,9 @@ var UPG_PROPERTY_SCHEMA = {
12914
13012
  effort: { type: "string", description: 'Effort estimate (e.g. "2h", "1d", "3 points"). Use a consistent unit within your team.' },
12915
13013
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Relative importance against other tasks" },
12916
13014
  due_date: { type: "string", description: "ISO date due. Typically bounded by the containing story's due date." },
12917
- labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." }
13015
+ labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." },
13016
+ workflow_state: { type: "string", description: 'The source tool\'s raw custom workflow state, verbatim and opaque (e.g. "In Review", "QA", "Needs Triage"). Non-canonical and never reasoned over: it exists to round-trip an import losslessly. Map it onto a canonical bucket with `workflow_state_category`; canonical `status` stays the sole reasoning axis.' },
13017
+ workflow_state_category: { type: "string", description: 'Canonical bucket the raw `workflow_state` maps onto for reasoning (a source "In Review" and "QA" might both map to a verification phase). Optional companion to `workflow_state`: it lets a graph reason over an imported custom workflow without promoting the raw label to `status`.' }
12918
13018
  },
12919
13019
  // TeamProperties: Team entity.
12920
13020
  team: {
@@ -13129,7 +13229,13 @@ var UPG_PROPERTY_SCHEMA = {
13129
13229
  as_a: { type: "string", description: '"As a [persona], \u2026". Free-text persona name or slug ref.' },
13130
13230
  i_want_to: { type: "string", description: "Capability or action the persona wants." },
13131
13231
  so_that: { type: "string", description: "Benefit or outcome the persona expects." },
13132
- text: { type: "string", description: "Free-form story text. Used as a single-line rendered view." }
13232
+ text: { type: "string", description: "Free-form story text. Used as a single-line rendered view." },
13233
+ assignee: { type: "string", description: "Assigned person. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
13234
+ effort: { type: "string", description: 'Effort estimate (e.g. "2h", "1d", "3 points"). Use a consistent unit within your team.' },
13235
+ priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Relative importance against other stories. Lifted onto user_story (0.20.0) so the story is a first-class plannable unit alongside task, matching Jira/Linear where the story/issue is the estimated-and-assigned atom." },
13236
+ due_date: { type: "string", description: "ISO date due. Typically bounded by the release or planning_cycle the story is scheduled into." },
13237
+ workflow_state: { type: "string", description: 'The source tool\'s raw custom workflow state, verbatim and opaque (e.g. "In Review", "QA", "Needs Triage"). Non-canonical and never reasoned over: it exists to round-trip an import losslessly. Map it onto a canonical bucket with `workflow_state_category`; canonical `status` stays the sole reasoning axis.' },
13238
+ workflow_state_category: { type: "string", description: 'Canonical bucket the raw `workflow_state` maps onto for reasoning (a source "In Review" and "QA" might both map to a verification phase). Optional companion to `workflow_state`: it lets a graph reason over an imported custom workflow without promoting the raw label to `status`.' }
13133
13239
  },
13134
13240
  // ValueObjectProperties: DDD value object.
13135
13241
  value_object: {
@@ -19775,6 +19881,7 @@ var ENTITY_EMOJI = {
19775
19881
  persona: "\u{1F3AD}",
19776
19882
  pipeline_sales: "\u{1FA88}",
19777
19883
  pipeline_stage: "\u{1F9FD}",
19884
+ planning_cycle: "\u{1F5D3}\uFE0F",
19778
19885
  playbook: "\u{1F4D2}",
19779
19886
  portfolio: "\u{1F9F8}",
19780
19887
  positioning: "\u{1F3AA}",
@@ -20466,6 +20573,7 @@ var STANDARD_LABELS = {
20466
20573
  roadmap_item: { alt_labels: ["roadmap entry", "planned item"] },
20467
20574
  roadmap_theme: { alt_labels: ["product theme", "roadmap theme"] },
20468
20575
  // UPG-660: renamed from bare 'theme' (N6 lineage)
20576
+ planning_cycle: { alt_labels: ["sprint", "iteration", "cycle", "program increment", "pi", "cadence", "time-box", "cooldown", "quarter"] },
20469
20577
  // Engineering layer
20470
20578
  bounded_context: { alt_labels: ["context", "domain boundary", "module boundary"] },
20471
20579
  service: { alt_labels: ["microservice", "backend service", "api service"] },
@@ -22987,13 +23095,19 @@ var PRODUCT_SPEC_GUIDE = {
22987
23095
  // registered to product_spec but missing from the navigation order).
22988
23096
  // `changelog` lives here because it is a structural product-shipping
22989
23097
  // artefact; content domain references it only via cross-domain bridges.
22990
- creation_sequence: ["feature_area", "feature", "epic", "user_story", "acceptance_criterion", "task", "bug", "release", "roadmap", "roadmap_item", "roadmap_theme", "changelog"],
23098
+ creation_sequence: ["feature_area", "feature", "epic", "user_story", "acceptance_criterion", "task", "bug", "release", "roadmap", "roadmap_item", "roadmap_theme", "changelog", "planning_cycle"],
22991
23099
  patterns: [
22992
23100
  {
22993
23101
  name: "Feature Decomposition",
22994
23102
  description: "Features group into areas, decompose into epics, epics specify user stories (the templated promise), and tasks implement them as the engineering work",
22995
23103
  entity_types: ["feature_area", "feature", "epic", "user_story", "task"],
22996
23104
  edge_chain: ["feature_area_contains_feature", "feature_decomposed_into_epic", "epic_specified_by_user_story", "task_implements_user_story"]
23105
+ },
23106
+ {
23107
+ name: "Planning Cadence",
23108
+ description: "The cadence axis. Create a planning_cycle (a sprint, iteration, quarter, or PI), nest finer cycles inside a coarse one, schedule the stories that flow through it, and scope the objectives the cycle serves. Scheduling and scoping are deliberate links, not containment: a story keeps its feature/epic parent.",
23109
+ entity_types: ["planning_cycle", "user_story", "objective"],
23110
+ edge_chain: ["planning_cycle_contains_planning_cycle", "planning_cycle_schedules_user_story", "objective_scoped_to_planning_cycle"]
22997
23111
  }
22998
23112
  ],
22999
23113
  required_bridges: [
@@ -26262,6 +26376,40 @@ var UPG_ANTI_PATTERNS = [
26262
26376
  severity: "high",
26263
26377
  source: { kind: "practitioner", attribution: "John Cutler, Outcomes over Output" }
26264
26378
  },
26379
+ {
26380
+ id: "planning-cycle-without-scheduled-work",
26381
+ name: "Planning cycle with no scheduled work",
26382
+ description: "A planning_cycle exists but neither schedules any user_story nor contains a finer sub-cycle. An empty cadence box is a date range with nothing flowing through it: a sprint or iteration nobody planned work into, or a coarse period that was never broken down.",
26383
+ structured_condition: {
26384
+ operator: "and",
26385
+ checks: [
26386
+ { check: { type: "entity_count", entity_type: "planning_cycle", comparison: "nonzero" } },
26387
+ {
26388
+ check: {
26389
+ type: "relationship",
26390
+ source_type: "planning_cycle",
26391
+ edge_type: "planning_cycle_schedules_user_story",
26392
+ target_type: "user_story",
26393
+ comparison: "not_exists"
26394
+ }
26395
+ },
26396
+ {
26397
+ check: {
26398
+ type: "relationship",
26399
+ source_type: "planning_cycle",
26400
+ edge_type: "planning_cycle_contains_planning_cycle",
26401
+ target_type: "planning_cycle",
26402
+ comparison: "not_exists"
26403
+ }
26404
+ }
26405
+ ]
26406
+ },
26407
+ why_it_matters: "A cadence layer only earns its keep when work is planned through it. An interval with no scheduled stories and no nested cycles adds ceremony without telling anyone what the period is for.",
26408
+ remediation: "Schedule the stories the cycle will carry via `planning_cycle_schedules_user_story`, or break a coarse period into finer cycles via `planning_cycle_contains_planning_cycle`. If neither applies, the interval is not yet a real cadence box.",
26409
+ stages: ["build", "beta", "launch", "growth", "mature"],
26410
+ severity: "low",
26411
+ since: "0.20.0"
26412
+ },
26265
26413
  // ── Market intelligence layer ───────────────────────────────────────────
26266
26414
  {
26267
26415
  id: "competitors-missing-past-validation",
@@ -27090,6 +27238,16 @@ var UPG_REGIONS = [
27090
27238
  edge_id: "outcome_delivered_via_feature_area",
27091
27239
  crosses_into: "product_delivery"
27092
27240
  },
27241
+ {
27242
+ direction: "export",
27243
+ edge_id: "objective_scoped_to_planning_cycle",
27244
+ crosses_into: "product_delivery"
27245
+ },
27246
+ {
27247
+ direction: "export",
27248
+ edge_id: "strategic_theme_scoped_to_planning_cycle",
27249
+ crosses_into: "product_delivery"
27250
+ },
27093
27251
  {
27094
27252
  direction: "import",
27095
27253
  edge_id: "feature_drives_key_result",
@@ -27901,6 +28059,11 @@ var UPG_REGIONS = [
27901
28059
  {
27902
28060
  type: "status_report",
27903
28061
  role: "leaf"
28062
+ },
28063
+ {
28064
+ type: "planning_cycle",
28065
+ role: "container",
28066
+ notes: "self-nesting cadence axis"
27904
28067
  }
27905
28068
  ],
27906
28069
  anchor: {
@@ -27924,7 +28087,9 @@ var UPG_REGIONS = [
27924
28087
  "roadmap_theme_spans_feature_area",
27925
28088
  "milestone_gates_release",
27926
28089
  "roadmap_item_references_feature",
27927
- "feature_request_voted_on_by_feedback_vote"
28090
+ "feature_request_voted_on_by_feedback_vote",
28091
+ "planning_cycle_contains_planning_cycle",
28092
+ "planning_cycle_schedules_user_story"
27928
28093
  ],
27929
28094
  boundary_edges: [
27930
28095
  {
@@ -27932,6 +28097,16 @@ var UPG_REGIONS = [
27932
28097
  edge_id: "outcome_delivered_by_feature",
27933
28098
  crosses_into: "strategy_outcomes"
27934
28099
  },
28100
+ {
28101
+ direction: "import",
28102
+ edge_id: "objective_scoped_to_planning_cycle",
28103
+ crosses_into: "strategy_outcomes"
28104
+ },
28105
+ {
28106
+ direction: "import",
28107
+ edge_id: "strategic_theme_scoped_to_planning_cycle",
28108
+ crosses_into: "strategy_outcomes"
28109
+ },
27935
28110
  {
27936
28111
  direction: "import",
27937
28112
  edge_id: "outcome_delivered_via_feature_area",
@@ -29744,7 +29919,7 @@ function isCanonical(text) {
29744
29919
  }
29745
29920
 
29746
29921
  // src/index.ts
29747
- var UPG_VERSION = "0.19.0";
29922
+ var UPG_VERSION = "0.20.1";
29748
29923
  var UPG_FORMAT_VERSION = "0.4.0";
29749
29924
  var MARKDOWN_FORMAT_VERSION = "0.1";
29750
29925
  var UPG_TYPES = getTypes();