@unified-product-graph/core 0.18.0 → 0.20.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.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" },
@@ -1366,6 +1375,8 @@ var UPG_EDGE_CATALOG = {
1366
1375
  product_builds_feature: { forward_verb: "builds", reverse_verb: "built_by", classification: "hierarchy", source_type: "product", target_type: "feature" },
1367
1376
  product_ships_via_release: { forward_verb: "ships_via", reverse_verb: "ships", classification: "hierarchy", source_type: "product", target_type: "release" },
1368
1377
  product_plans_via_roadmap: { forward_verb: "plans_via", reverse_verb: "plans_for", classification: "hierarchy", source_type: "product", target_type: "roadmap" },
1378
+ // (0.20.0) top-level attach for the cadence axis, mirroring product_plans_via_roadmap.
1379
+ product_runs_planning_cycle: { forward_verb: "runs", reverse_verb: "run_by", classification: "hierarchy", source_type: "product", target_type: "planning_cycle" },
1369
1380
  product_categorises_by_roadmap_theme: { forward_verb: "categorises_by", reverse_verb: "categorises", classification: "hierarchy", source_type: "product", target_type: "roadmap_theme" },
1370
1381
  feature_area_contains_feature: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "feature_area", target_type: "feature" },
1371
1382
  feature_area_contains_feature_area: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "feature_area", target_type: "feature_area" },
@@ -1404,6 +1415,33 @@ var UPG_EDGE_CATALOG = {
1404
1415
  roadmap_item_references_feature: { forward_verb: "references", reverse_verb: "referenced_by", classification: "cross-domain", source_type: "roadmap_item", target_type: "feature" },
1405
1416
  feature_decomposes_into_task: { forward_verb: "decomposes_into", reverse_verb: "implements", classification: "hierarchy", source_type: "feature", target_type: "task" },
1406
1417
  task_has_subtask: { forward_verb: "has_subtask", reverse_verb: "is_subtask_of", classification: "hierarchy", source_type: "task", target_type: "task" },
1418
+ // ── Planning cadence (0.20.0) ────────────────────────────────────────────────
1419
+ // The cadence axis. E1 self-nests the interval (a program-increment contains
1420
+ // iterations; a cycle contains its cooldown), mirroring team_contains_team.
1421
+ planning_cycle_contains_planning_cycle: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "planning_cycle", target_type: "planning_cycle" },
1422
+ // E3: scheduling work into a cycle is a deliberate authoring act, not a
1423
+ // containment nesting — user_story keeps its feature/epic parent and is merely
1424
+ // referenced by the cycle. deliberate_only so the generic-inference chokepoints
1425
+ // never auto-materialise it.
1426
+ 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 },
1427
+ // E2: the OKR cycle-scoping anchor. objective -> planning_cycle. planning_cycle
1428
+ // is portfolio_shared, so this passes the cross-scope gate and ships as
1429
+ // PROVISIONAL (allowed cross-product with a write-time warning, self-maintaining,
1430
+ // never added to the curated set) rather than carrying a cross_product_eligible
1431
+ // flag, which would make it curated. Eligibility is permission, not obligation.
1432
+ objective_scoped_to_planning_cycle: { forward_verb: "scoped_to", reverse_verb: "scopes", classification: "semantic", source_type: "objective", target_type: "planning_cycle" },
1433
+ // F: the promoted `strategic_theme.time_horizon`. Same shape as E2 — a theme's
1434
+ // bounded period becomes a scoping edge onto a shared, dated, nestable interval.
1435
+ // Provisional (planning_cycle portfolio_shared). The property stays @deprecated.
1436
+ strategic_theme_scoped_to_planning_cycle: { forward_verb: "scoped_to", reverse_verb: "scopes", classification: "semantic", source_type: "strategic_theme", target_type: "planning_cycle" },
1437
+ // Gap 2 — polymorphic issue links over the work-item set {feature, epic,
1438
+ // user_story, task, bug}. Endpoint-polymorphic (node -> node, registered in
1439
+ // UPG_POLYMORPHIC_EDGE_KEYS) so one edge covers every level teams link at; the
1440
+ // `work_item_` key names the intended semantic domain. All deliberate_only: an
1441
+ // issue link is authored, never inferred from hierarchy.
1442
+ work_item_blocks_work_item: { forward_verb: "blocks", reverse_verb: "blocked_by", classification: "causal", source_type: "node", target_type: "node", deliberate_only: true },
1443
+ 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 },
1444
+ work_item_duplicates_work_item: { forward_verb: "duplicates", reverse_verb: "duplicated_by", classification: "semantic", source_type: "node", target_type: "node", deliberate_only: true },
1407
1445
  // 2.3 Legal Domain
1408
1446
  product_owned_by_legal_entity: { forward_verb: "owned_by", reverse_verb: "owns", classification: "hierarchy", source_type: "product", target_type: "legal_entity" },
1409
1447
  legal_entity_protects_ip_asset: { forward_verb: "protects", reverse_verb: "protected_by", classification: "hierarchy", source_type: "legal_entity", target_type: "ip_asset" },
@@ -3187,7 +3225,13 @@ var UPG_POLYMORPHIC_EDGE_KEYS = [
3187
3225
  // Framework exercises (an exercise can include any entity type)
3188
3226
  "framework_exercise_includes_node",
3189
3227
  // Universal classification (any node classified against a classification_value)
3190
- "node_classified_as_classification_value"
3228
+ "node_classified_as_classification_value",
3229
+ // Work-item issue links (0.20.0): endpoint-polymorphic over the work-item set
3230
+ // {feature, epic, user_story, task, bug}. The `work_item_` key names the
3231
+ // intended semantic domain; the endpoints are the `node` wildcard.
3232
+ "work_item_blocks_work_item",
3233
+ "work_item_relates_to_work_item",
3234
+ "work_item_duplicates_work_item"
3191
3235
  ];
3192
3236
  var _POLY_KEY_SET = new Set(UPG_POLYMORPHIC_EDGE_KEYS);
3193
3237
  function isPolymorphicEdge(key) {
@@ -3377,6 +3421,7 @@ var UPG_VALID_CHILDREN = {
3377
3421
  "release",
3378
3422
  "roadmap",
3379
3423
  "roadmap_theme",
3424
+ "planning_cycle",
3380
3425
  // Engineering
3381
3426
  "bounded_context",
3382
3427
  "code_repository",
@@ -3645,6 +3690,13 @@ var UPG_VALID_CHILDREN = {
3645
3690
  epic: ["user_story"],
3646
3691
  user_story: ["acceptance_criterion"],
3647
3692
  task: ["task"],
3693
+ // planning_cycle (0.20.0): the cadence axis self-nests (a program-increment
3694
+ // contains iterations; a cycle contains its cooldown) via
3695
+ // planning_cycle_contains_planning_cycle. It does NOT contain user_story as a
3696
+ // child: scheduling work into a cycle is the semantic, deliberate-only
3697
+ // planning_cycle_schedules_user_story edge, so a story keeps its feature/epic
3698
+ // containment parent and is merely referenced by the cycle.
3699
+ planning_cycle: ["planning_cycle"],
3648
3700
  // ── Engineering hierarchy ───────────────────────────────────────────────────
3649
3701
  bounded_context: [
3650
3702
  "service",
@@ -6288,6 +6340,16 @@ var SPECIFICATION_LIFECYCLE = {
6288
6340
  }
6289
6341
  ]
6290
6342
  };
6343
+ var PLANNING_CYCLE_LIFECYCLE = {
6344
+ entity_type: "planning_cycle",
6345
+ initial_phase: "planned",
6346
+ terminal_phases: ["closed"],
6347
+ phases: [
6348
+ { 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"] },
6349
+ { id: "active", label: "Active", description: "The team is inside the interval; work is flowing through it.", transitions_to: ["closed"] },
6350
+ { 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"] }
6351
+ ]
6352
+ };
6291
6353
  var UPG_LIFECYCLES = [
6292
6354
  // Product (root)
6293
6355
  PRODUCT_LIFECYCLE,
@@ -6331,6 +6393,7 @@ var UPG_LIFECYCLES = [
6331
6393
  TASK_LIFECYCLE,
6332
6394
  BUG_LIFECYCLE,
6333
6395
  ROADMAP_ITEM_LIFECYCLE,
6396
+ PLANNING_CYCLE_LIFECYCLE,
6334
6397
  IP_ASSET_LIFECYCLE,
6335
6398
  CONTRACT_LIFECYCLE,
6336
6399
  DESIGN_CONCEPT_LIFECYCLE,
@@ -10513,7 +10576,9 @@ var UPG_PROPERTY_SCHEMA = {
10513
10576
  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
10577
  assignee: { type: "string", description: "Assigned person. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
10515
10578
  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." }
10579
+ labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." },
10580
+ 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.' },
10581
+ 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
10582
  },
10518
10583
  // BuildArtifactProperties: Build artifact.
10519
10584
  build_artifact: {
@@ -11167,7 +11232,9 @@ var UPG_PROPERTY_SCHEMA = {
11167
11232
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Task-level priority" },
11168
11233
  owner: { type: "string", description: "Responsible person or team. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
11169
11234
  start_date: { type: "string", description: "ISO date work begins" },
11170
- target_date: { type: "string", description: "ISO date work completes" }
11235
+ target_date: { type: "string", description: "ISO date work completes" },
11236
+ 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.' },
11237
+ 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
11238
  },
11172
11239
  // ErrorBudgetProperties: Error budget.
11173
11240
  error_budget: {
@@ -11347,7 +11414,9 @@ var UPG_PROPERTY_SCHEMA = {
11347
11414
  owner: { type: "string", description: "Responsible person or team. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
11348
11415
  start_date: { type: "string", description: "ISO date work begins" },
11349
11416
  target_date: { type: "string", description: "ISO date work completes" },
11350
- health: { type: "string", enum: ["on_track", "at_risk", "off_track"], description: "Delivery health" }
11417
+ health: { type: "string", enum: ["on_track", "at_risk", "off_track"], description: "Delivery health" },
11418
+ 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.' },
11419
+ 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
11420
  },
11352
11421
  // FeatureAreaProperties: A structural grouping of related features within a product.
11353
11422
  feature_area: {
@@ -12267,6 +12336,16 @@ var UPG_PROPERTY_SCHEMA = {
12267
12336
  conversion_rate: { type: "number", description: "Percentage of deals that advance from this stage", modifier: "snapshot" },
12268
12337
  avg_days_in_stage: { type: "number", description: "Average number of days deals spend in this stage" }
12269
12338
  },
12339
+ // PlanningCycleProperties: Planning cycle: a named, dated interval work flows through, which nests.
12340
+ planning_cycle: {
12341
+ 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." },
12342
+ 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.' },
12343
+ starts_on: { type: "string", description: "ISO date the interval opens. A cycle is concretely dated, unlike a coarse `time_horizon` label." },
12344
+ ends_on: { type: "string", description: "ISO date the interval closes." },
12345
+ sequence: { type: "number", description: "The cycle / iteration number (e.g. Sprint 47, PI 3)." },
12346
+ goal: { type: "string", description: "The interval's goal or focus: what this cadence box is for." },
12347
+ 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").' }
12348
+ },
12270
12349
  // PlaybookProperties: Customer success playbook.
12271
12350
  playbook: {
12272
12351
  playbook_type: { type: "string", enum: ["onboarding", "expansion", "renewal", "rescue", "other"], description: "Scenario this playbook addresses" },
@@ -12818,7 +12897,7 @@ var UPG_PROPERTY_SCHEMA = {
12818
12897
  // StrategicThemeProperties: StrategicTheme entity.
12819
12898
  strategic_theme: {
12820
12899
  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"' },
12900
+ 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
12901
  description: { type: "string", description: "Short narrative of this time-bound thrust within its pillar" },
12823
12902
  scope: { type: "string", description: "What the theme explicitly includes or excludes" }
12824
12903
  },
@@ -12914,7 +12993,9 @@ var UPG_PROPERTY_SCHEMA = {
12914
12993
  effort: { type: "string", description: 'Effort estimate (e.g. "2h", "1d", "3 points"). Use a consistent unit within your team.' },
12915
12994
  priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Relative importance against other tasks" },
12916
12995
  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." }
12996
+ labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." },
12997
+ 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.' },
12998
+ 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
12999
  },
12919
13000
  // TeamProperties: Team entity.
12920
13001
  team: {
@@ -13129,7 +13210,13 @@ var UPG_PROPERTY_SCHEMA = {
13129
13210
  as_a: { type: "string", description: '"As a [persona], \u2026". Free-text persona name or slug ref.' },
13130
13211
  i_want_to: { type: "string", description: "Capability or action the persona wants." },
13131
13212
  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." }
13213
+ text: { type: "string", description: "Free-form story text. Used as a single-line rendered view." },
13214
+ assignee: { type: "string", description: "Assigned person. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
13215
+ effort: { type: "string", description: 'Effort estimate (e.g. "2h", "1d", "3 points"). Use a consistent unit within your team.' },
13216
+ 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." },
13217
+ due_date: { type: "string", description: "ISO date due. Typically bounded by the release or planning_cycle the story is scheduled into." },
13218
+ 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.' },
13219
+ 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
13220
  },
13134
13221
  // ValueObjectProperties: DDD value object.
13135
13222
  value_object: {
@@ -19775,6 +19862,7 @@ var ENTITY_EMOJI = {
19775
19862
  persona: "\u{1F3AD}",
19776
19863
  pipeline_sales: "\u{1FA88}",
19777
19864
  pipeline_stage: "\u{1F9FD}",
19865
+ planning_cycle: "\u{1F5D3}\uFE0F",
19778
19866
  playbook: "\u{1F4D2}",
19779
19867
  portfolio: "\u{1F9F8}",
19780
19868
  positioning: "\u{1F3AA}",
@@ -20466,6 +20554,7 @@ var STANDARD_LABELS = {
20466
20554
  roadmap_item: { alt_labels: ["roadmap entry", "planned item"] },
20467
20555
  roadmap_theme: { alt_labels: ["product theme", "roadmap theme"] },
20468
20556
  // UPG-660: renamed from bare 'theme' (N6 lineage)
20557
+ planning_cycle: { alt_labels: ["sprint", "iteration", "cycle", "program increment", "pi", "cadence", "time-box", "cooldown", "quarter"] },
20469
20558
  // Engineering layer
20470
20559
  bounded_context: { alt_labels: ["context", "domain boundary", "module boundary"] },
20471
20560
  service: { alt_labels: ["microservice", "backend service", "api service"] },
@@ -22987,13 +23076,19 @@ var PRODUCT_SPEC_GUIDE = {
22987
23076
  // registered to product_spec but missing from the navigation order).
22988
23077
  // `changelog` lives here because it is a structural product-shipping
22989
23078
  // 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"],
23079
+ creation_sequence: ["feature_area", "feature", "epic", "user_story", "acceptance_criterion", "task", "bug", "release", "roadmap", "roadmap_item", "roadmap_theme", "changelog", "planning_cycle"],
22991
23080
  patterns: [
22992
23081
  {
22993
23082
  name: "Feature Decomposition",
22994
23083
  description: "Features group into areas, decompose into epics, epics specify user stories (the templated promise), and tasks implement them as the engineering work",
22995
23084
  entity_types: ["feature_area", "feature", "epic", "user_story", "task"],
22996
23085
  edge_chain: ["feature_area_contains_feature", "feature_decomposed_into_epic", "epic_specified_by_user_story", "task_implements_user_story"]
23086
+ },
23087
+ {
23088
+ name: "Planning Cadence",
23089
+ 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.",
23090
+ entity_types: ["planning_cycle", "user_story", "objective"],
23091
+ edge_chain: ["planning_cycle_contains_planning_cycle", "planning_cycle_schedules_user_story", "objective_scoped_to_planning_cycle"]
22997
23092
  }
22998
23093
  ],
22999
23094
  required_bridges: [
@@ -26262,6 +26357,40 @@ var UPG_ANTI_PATTERNS = [
26262
26357
  severity: "high",
26263
26358
  source: { kind: "practitioner", attribution: "John Cutler, Outcomes over Output" }
26264
26359
  },
26360
+ {
26361
+ id: "planning-cycle-without-scheduled-work",
26362
+ name: "Planning cycle with no scheduled work",
26363
+ 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.",
26364
+ structured_condition: {
26365
+ operator: "and",
26366
+ checks: [
26367
+ { check: { type: "entity_count", entity_type: "planning_cycle", comparison: "nonzero" } },
26368
+ {
26369
+ check: {
26370
+ type: "relationship",
26371
+ source_type: "planning_cycle",
26372
+ edge_type: "planning_cycle_schedules_user_story",
26373
+ target_type: "user_story",
26374
+ comparison: "not_exists"
26375
+ }
26376
+ },
26377
+ {
26378
+ check: {
26379
+ type: "relationship",
26380
+ source_type: "planning_cycle",
26381
+ edge_type: "planning_cycle_contains_planning_cycle",
26382
+ target_type: "planning_cycle",
26383
+ comparison: "not_exists"
26384
+ }
26385
+ }
26386
+ ]
26387
+ },
26388
+ 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.",
26389
+ 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.",
26390
+ stages: ["build", "beta", "launch", "growth", "mature"],
26391
+ severity: "low",
26392
+ since: "0.20.0"
26393
+ },
26265
26394
  // ── Market intelligence layer ───────────────────────────────────────────
26266
26395
  {
26267
26396
  id: "competitors-missing-past-validation",
@@ -27090,6 +27219,16 @@ var UPG_REGIONS = [
27090
27219
  edge_id: "outcome_delivered_via_feature_area",
27091
27220
  crosses_into: "product_delivery"
27092
27221
  },
27222
+ {
27223
+ direction: "export",
27224
+ edge_id: "objective_scoped_to_planning_cycle",
27225
+ crosses_into: "product_delivery"
27226
+ },
27227
+ {
27228
+ direction: "export",
27229
+ edge_id: "strategic_theme_scoped_to_planning_cycle",
27230
+ crosses_into: "product_delivery"
27231
+ },
27093
27232
  {
27094
27233
  direction: "import",
27095
27234
  edge_id: "feature_drives_key_result",
@@ -27901,6 +28040,11 @@ var UPG_REGIONS = [
27901
28040
  {
27902
28041
  type: "status_report",
27903
28042
  role: "leaf"
28043
+ },
28044
+ {
28045
+ type: "planning_cycle",
28046
+ role: "container",
28047
+ notes: "self-nesting cadence axis"
27904
28048
  }
27905
28049
  ],
27906
28050
  anchor: {
@@ -27924,7 +28068,9 @@ var UPG_REGIONS = [
27924
28068
  "roadmap_theme_spans_feature_area",
27925
28069
  "milestone_gates_release",
27926
28070
  "roadmap_item_references_feature",
27927
- "feature_request_voted_on_by_feedback_vote"
28071
+ "feature_request_voted_on_by_feedback_vote",
28072
+ "planning_cycle_contains_planning_cycle",
28073
+ "planning_cycle_schedules_user_story"
27928
28074
  ],
27929
28075
  boundary_edges: [
27930
28076
  {
@@ -27932,6 +28078,16 @@ var UPG_REGIONS = [
27932
28078
  edge_id: "outcome_delivered_by_feature",
27933
28079
  crosses_into: "strategy_outcomes"
27934
28080
  },
28081
+ {
28082
+ direction: "import",
28083
+ edge_id: "objective_scoped_to_planning_cycle",
28084
+ crosses_into: "strategy_outcomes"
28085
+ },
28086
+ {
28087
+ direction: "import",
28088
+ edge_id: "strategic_theme_scoped_to_planning_cycle",
28089
+ crosses_into: "strategy_outcomes"
28090
+ },
27935
28091
  {
27936
28092
  direction: "import",
27937
28093
  edge_id: "outcome_delivered_via_feature_area",
@@ -29744,7 +29900,7 @@ function isCanonical(text) {
29744
29900
  }
29745
29901
 
29746
29902
  // src/index.ts
29747
- var UPG_VERSION = "0.18.0";
29903
+ var UPG_VERSION = "0.20.0";
29748
29904
  var UPG_FORMAT_VERSION = "0.4.0";
29749
29905
  var MARKDOWN_FORMAT_VERSION = "0.1";
29750
29906
  var UPG_TYPES = getTypes();