@unified-product-graph/core 0.19.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/CHANGELOG.md +19 -0
- package/README.md +1 -1
- package/dist/index.cjs +168 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +135 -6
- package/dist/index.d.ts +135 -6
- package/dist/index.js +168 -12
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.20.0] - 2026-07-04
|
|
11
|
+
|
|
12
|
+
**A cadence layer for the product-delivery region: one self-nesting `planning_cycle` entity models sprints, iterations, quarters, program increments, and cooldowns, so a Jira or Linear export round-trips its time-boxes losslessly.** The delivery region could describe what ships (feature → epic → story → task) and what governs it (release, roadmap, theme), but had no home for the interval work flows *through*. This release mints `planning_cycle` — a named, dated, self-nesting interval discriminated by `cadence_kind` (period / iteration / buffer) rather than a type per methodology — plus the edges that schedule work into it and scope objectives and themes to it. It also closes the smaller structural gaps a Jira ∪ Linear import hits: polymorphic issue links, a dual-band `workflow_state` that preserves an imported tool's raw states without displacing the canonical `status`, and the task-level planning fields lifted onto `user_story`. Additive; no breaking change.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **`planning_cycle` entity** (`proposed`) — the cadence axis. A self-nesting container in the `product_spec` domain / `product_delivery` region, `portfolio_shared`, lifecycle `planned → active → closed`. Properties: `cadence_kind` (period / iteration / buffer), `cadence_label`, `starts_on`, `ends_on`, `sequence`, `goal`, `appetite`. One type with a `cadence_kind` discriminator spans sprint / iteration / quarter / program-increment / cooldown; self-nesting handles the granularity ladder (a PI contains iterations; a cycle contains its cooldown).
|
|
16
|
+
- **Core cadence edges** — `planning_cycle_contains_planning_cycle` (self-nesting containment), `objective_scoped_to_planning_cycle` and `strategic_theme_scoped_to_planning_cycle` (OKR / theme cycle-scoping, provisional cross-scope), `planning_cycle_schedules_user_story` (deliberate-only work scheduling — a story keeps its feature/epic containment parent), and `product_runs_planning_cycle` (top-level attach).
|
|
17
|
+
- **Polymorphic work-item issue links** — `work_item_blocks_work_item`, `work_item_relates_to_work_item`, `work_item_duplicates_work_item` over the work-item set {feature, epic, user_story, task, bug}, all deliberate-only.
|
|
18
|
+
- **Dual-band `workflow_state`** — a freeform `workflow_state` plus an optional `workflow_state_category` on the work-item entities, so an imported custom workflow round-trips losslessly without displacing canonical `status` as the reasoning axis.
|
|
19
|
+
- **`planning-cycle-without-scheduled-work`** anti-pattern (low severity) — a cadence box that neither schedules work nor nests a sub-cycle.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- **`user_story` gains `priority`, `effort`, `assignee`, `due_date`** — the story is now a first-class plannable unit alongside `task`, matching the tools where the story / issue is the estimated-and-assigned atom.
|
|
23
|
+
|
|
24
|
+
### Deprecated
|
|
25
|
+
- **`strategic_theme.time_horizon`** — promote the bounded period to a `planning_cycle` node linked with `strategic_theme_scoped_to_planning_cycle`. The property is kept and readable; removal is a later major. `strategic_pillar.time_horizon` (a durable, open-ended pillar horizon, not a dated cycle) stays as-is.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
10
29
|
## [0.19.0] - 2026-07-04
|
|
11
30
|
|
|
12
31
|
**The MCP surface consolidates from 139 tools to 93 (+ 5 prompts): the static-spec introspection cluster folds into four faceted reads, and five procedural routers become skills.** The spec catalogue had accreted 45 near-identical `list_*` / `get_*` reads plus five routers — a surface an agent had to scan linearly before it could choose. This release collapses the reads into four facets and retires the routers, leaving the 89 write and live-graph-read verbs untouched. A parity gate (`retired-tools.json` + a byte-equality test wired as a prepublish stage) proves every retired tool's data is still reachable and identical through the facets, on both the local and cloud servers — which licenses a clean break with no deprecation window. Both servers' instructions gain a methodology preamble (the SEE → THINK → ACT → LEARN working loop) so the surface conveys *how to work*, not only *what exists*. Breaking. Local 139 → 93; cloud 100 → 54.
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
The TypeScript core for the **Unified Product Graph (UPG)**, an open specification that gives product tools a shared vocabulary for entities, edges, and frameworks.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
320 entity types across 37 domains. 1034 typed edges. 46 canonical framework definitions, with more added as each is reviewed.
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
package/dist/index.cjs
CHANGED
|
@@ -347,7 +347,7 @@ var UPG_DOMAINS = [
|
|
|
347
347
|
{
|
|
348
348
|
id: "product_spec",
|
|
349
349
|
label: "Product Specification",
|
|
350
|
-
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.",
|
|
350
|
+
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.",
|
|
351
351
|
types: [
|
|
352
352
|
"feature",
|
|
353
353
|
"feature_area",
|
|
@@ -360,7 +360,8 @@ var UPG_DOMAINS = [
|
|
|
360
360
|
"roadmap",
|
|
361
361
|
"roadmap_item",
|
|
362
362
|
"roadmap_theme",
|
|
363
|
-
"changelog"
|
|
363
|
+
"changelog",
|
|
364
|
+
"planning_cycle"
|
|
364
365
|
]
|
|
365
366
|
},
|
|
366
367
|
{
|
|
@@ -938,6 +939,14 @@ var UPG_ENTITY_META = [
|
|
|
938
939
|
{ name: "theme", type_id: "ent_080", maturity: "deprecated", since: "0.1.0", deprecated_in: "0.9.0", replacement: "roadmap_theme" },
|
|
939
940
|
{ name: "roadmap_theme", type_id: "ent_351", maturity: "stable", since: "0.1.0" },
|
|
940
941
|
{ name: "changelog", type_id: "ent_081", maturity: "stable", since: "0.1.0" },
|
|
942
|
+
// (0.20.0) The cadence axis: a named, dated interval that work flows through
|
|
943
|
+
// and which nests (a program-increment contains iterations; a cycle contains
|
|
944
|
+
// its cooldown). One self-nesting type with a `cadence_kind` discriminator
|
|
945
|
+
// spans sprint / iteration / quarter / PI / cooldown rather than minting a
|
|
946
|
+
// type per methodology. `portfolio_shared` because a coarse cycle is an
|
|
947
|
+
// org-shared interval an objective in a rollup graph and a story in a product
|
|
948
|
+
// graph both schedule into.
|
|
949
|
+
{ name: "planning_cycle", type_id: "ent_358", maturity: "proposed", since: "0.20.0", portfolio_shared: true },
|
|
941
950
|
// ── Engineering ──
|
|
942
951
|
{ name: "bounded_context", type_id: "ent_082", maturity: "stable", since: "0.1.0" },
|
|
943
952
|
{ name: "service", type_id: "ent_083", maturity: "stable", since: "0.1.0" },
|
|
@@ -1621,6 +1630,8 @@ var UPG_EDGE_CATALOG = {
|
|
|
1621
1630
|
product_builds_feature: { forward_verb: "builds", reverse_verb: "built_by", classification: "hierarchy", source_type: "product", target_type: "feature" },
|
|
1622
1631
|
product_ships_via_release: { forward_verb: "ships_via", reverse_verb: "ships", classification: "hierarchy", source_type: "product", target_type: "release" },
|
|
1623
1632
|
product_plans_via_roadmap: { forward_verb: "plans_via", reverse_verb: "plans_for", classification: "hierarchy", source_type: "product", target_type: "roadmap" },
|
|
1633
|
+
// (0.20.0) top-level attach for the cadence axis, mirroring product_plans_via_roadmap.
|
|
1634
|
+
product_runs_planning_cycle: { forward_verb: "runs", reverse_verb: "run_by", classification: "hierarchy", source_type: "product", target_type: "planning_cycle" },
|
|
1624
1635
|
product_categorises_by_roadmap_theme: { forward_verb: "categorises_by", reverse_verb: "categorises", classification: "hierarchy", source_type: "product", target_type: "roadmap_theme" },
|
|
1625
1636
|
feature_area_contains_feature: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "feature_area", target_type: "feature" },
|
|
1626
1637
|
feature_area_contains_feature_area: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "feature_area", target_type: "feature_area" },
|
|
@@ -1659,6 +1670,33 @@ var UPG_EDGE_CATALOG = {
|
|
|
1659
1670
|
roadmap_item_references_feature: { forward_verb: "references", reverse_verb: "referenced_by", classification: "cross-domain", source_type: "roadmap_item", target_type: "feature" },
|
|
1660
1671
|
feature_decomposes_into_task: { forward_verb: "decomposes_into", reverse_verb: "implements", classification: "hierarchy", source_type: "feature", target_type: "task" },
|
|
1661
1672
|
task_has_subtask: { forward_verb: "has_subtask", reverse_verb: "is_subtask_of", classification: "hierarchy", source_type: "task", target_type: "task" },
|
|
1673
|
+
// ── Planning cadence (0.20.0) ────────────────────────────────────────────────
|
|
1674
|
+
// The cadence axis. E1 self-nests the interval (a program-increment contains
|
|
1675
|
+
// iterations; a cycle contains its cooldown), mirroring team_contains_team.
|
|
1676
|
+
planning_cycle_contains_planning_cycle: { forward_verb: "contains", reverse_verb: "belongs_to", classification: "hierarchy", source_type: "planning_cycle", target_type: "planning_cycle" },
|
|
1677
|
+
// E3: scheduling work into a cycle is a deliberate authoring act, not a
|
|
1678
|
+
// containment nesting — user_story keeps its feature/epic parent and is merely
|
|
1679
|
+
// referenced by the cycle. deliberate_only so the generic-inference chokepoints
|
|
1680
|
+
// never auto-materialise it.
|
|
1681
|
+
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 },
|
|
1682
|
+
// E2: the OKR cycle-scoping anchor. objective -> planning_cycle. planning_cycle
|
|
1683
|
+
// is portfolio_shared, so this passes the cross-scope gate and ships as
|
|
1684
|
+
// PROVISIONAL (allowed cross-product with a write-time warning, self-maintaining,
|
|
1685
|
+
// never added to the curated set) rather than carrying a cross_product_eligible
|
|
1686
|
+
// flag, which would make it curated. Eligibility is permission, not obligation.
|
|
1687
|
+
objective_scoped_to_planning_cycle: { forward_verb: "scoped_to", reverse_verb: "scopes", classification: "semantic", source_type: "objective", target_type: "planning_cycle" },
|
|
1688
|
+
// F: the promoted `strategic_theme.time_horizon`. Same shape as E2 — a theme's
|
|
1689
|
+
// bounded period becomes a scoping edge onto a shared, dated, nestable interval.
|
|
1690
|
+
// Provisional (planning_cycle portfolio_shared). The property stays @deprecated.
|
|
1691
|
+
strategic_theme_scoped_to_planning_cycle: { forward_verb: "scoped_to", reverse_verb: "scopes", classification: "semantic", source_type: "strategic_theme", target_type: "planning_cycle" },
|
|
1692
|
+
// Gap 2 — polymorphic issue links over the work-item set {feature, epic,
|
|
1693
|
+
// user_story, task, bug}. Endpoint-polymorphic (node -> node, registered in
|
|
1694
|
+
// UPG_POLYMORPHIC_EDGE_KEYS) so one edge covers every level teams link at; the
|
|
1695
|
+
// `work_item_` key names the intended semantic domain. All deliberate_only: an
|
|
1696
|
+
// issue link is authored, never inferred from hierarchy.
|
|
1697
|
+
work_item_blocks_work_item: { forward_verb: "blocks", reverse_verb: "blocked_by", classification: "causal", source_type: "node", target_type: "node", deliberate_only: true },
|
|
1698
|
+
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 },
|
|
1699
|
+
work_item_duplicates_work_item: { forward_verb: "duplicates", reverse_verb: "duplicated_by", classification: "semantic", source_type: "node", target_type: "node", deliberate_only: true },
|
|
1662
1700
|
// 2.3 Legal Domain
|
|
1663
1701
|
product_owned_by_legal_entity: { forward_verb: "owned_by", reverse_verb: "owns", classification: "hierarchy", source_type: "product", target_type: "legal_entity" },
|
|
1664
1702
|
legal_entity_protects_ip_asset: { forward_verb: "protects", reverse_verb: "protected_by", classification: "hierarchy", source_type: "legal_entity", target_type: "ip_asset" },
|
|
@@ -3442,7 +3480,13 @@ var UPG_POLYMORPHIC_EDGE_KEYS = [
|
|
|
3442
3480
|
// Framework exercises (an exercise can include any entity type)
|
|
3443
3481
|
"framework_exercise_includes_node",
|
|
3444
3482
|
// Universal classification (any node classified against a classification_value)
|
|
3445
|
-
"node_classified_as_classification_value"
|
|
3483
|
+
"node_classified_as_classification_value",
|
|
3484
|
+
// Work-item issue links (0.20.0): endpoint-polymorphic over the work-item set
|
|
3485
|
+
// {feature, epic, user_story, task, bug}. The `work_item_` key names the
|
|
3486
|
+
// intended semantic domain; the endpoints are the `node` wildcard.
|
|
3487
|
+
"work_item_blocks_work_item",
|
|
3488
|
+
"work_item_relates_to_work_item",
|
|
3489
|
+
"work_item_duplicates_work_item"
|
|
3446
3490
|
];
|
|
3447
3491
|
var _POLY_KEY_SET = new Set(UPG_POLYMORPHIC_EDGE_KEYS);
|
|
3448
3492
|
function isPolymorphicEdge(key) {
|
|
@@ -3632,6 +3676,7 @@ var UPG_VALID_CHILDREN = {
|
|
|
3632
3676
|
"release",
|
|
3633
3677
|
"roadmap",
|
|
3634
3678
|
"roadmap_theme",
|
|
3679
|
+
"planning_cycle",
|
|
3635
3680
|
// Engineering
|
|
3636
3681
|
"bounded_context",
|
|
3637
3682
|
"code_repository",
|
|
@@ -3900,6 +3945,13 @@ var UPG_VALID_CHILDREN = {
|
|
|
3900
3945
|
epic: ["user_story"],
|
|
3901
3946
|
user_story: ["acceptance_criterion"],
|
|
3902
3947
|
task: ["task"],
|
|
3948
|
+
// planning_cycle (0.20.0): the cadence axis self-nests (a program-increment
|
|
3949
|
+
// contains iterations; a cycle contains its cooldown) via
|
|
3950
|
+
// planning_cycle_contains_planning_cycle. It does NOT contain user_story as a
|
|
3951
|
+
// child: scheduling work into a cycle is the semantic, deliberate-only
|
|
3952
|
+
// planning_cycle_schedules_user_story edge, so a story keeps its feature/epic
|
|
3953
|
+
// containment parent and is merely referenced by the cycle.
|
|
3954
|
+
planning_cycle: ["planning_cycle"],
|
|
3903
3955
|
// ── Engineering hierarchy ───────────────────────────────────────────────────
|
|
3904
3956
|
bounded_context: [
|
|
3905
3957
|
"service",
|
|
@@ -6543,6 +6595,16 @@ var SPECIFICATION_LIFECYCLE = {
|
|
|
6543
6595
|
}
|
|
6544
6596
|
]
|
|
6545
6597
|
};
|
|
6598
|
+
var PLANNING_CYCLE_LIFECYCLE = {
|
|
6599
|
+
entity_type: "planning_cycle",
|
|
6600
|
+
initial_phase: "planned",
|
|
6601
|
+
terminal_phases: ["closed"],
|
|
6602
|
+
phases: [
|
|
6603
|
+
{ 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"] },
|
|
6604
|
+
{ id: "active", label: "Active", description: "The team is inside the interval; work is flowing through it.", transitions_to: ["closed"] },
|
|
6605
|
+
{ 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"] }
|
|
6606
|
+
]
|
|
6607
|
+
};
|
|
6546
6608
|
var UPG_LIFECYCLES = [
|
|
6547
6609
|
// Product (root)
|
|
6548
6610
|
PRODUCT_LIFECYCLE,
|
|
@@ -6586,6 +6648,7 @@ var UPG_LIFECYCLES = [
|
|
|
6586
6648
|
TASK_LIFECYCLE,
|
|
6587
6649
|
BUG_LIFECYCLE,
|
|
6588
6650
|
ROADMAP_ITEM_LIFECYCLE,
|
|
6651
|
+
PLANNING_CYCLE_LIFECYCLE,
|
|
6589
6652
|
IP_ASSET_LIFECYCLE,
|
|
6590
6653
|
CONTRACT_LIFECYCLE,
|
|
6591
6654
|
DESIGN_CONCEPT_LIFECYCLE,
|
|
@@ -10768,7 +10831,9 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
10768
10831
|
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)." },
|
|
10769
10832
|
assignee: { type: "string", description: "Assigned person. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
|
|
10770
10833
|
due_date: { type: "string", description: "ISO date due. Often tied to a release gate or SLA." },
|
|
10771
|
-
labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." }
|
|
10834
|
+
labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." },
|
|
10835
|
+
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.' },
|
|
10836
|
+
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`.' }
|
|
10772
10837
|
},
|
|
10773
10838
|
// BuildArtifactProperties: Build artifact.
|
|
10774
10839
|
build_artifact: {
|
|
@@ -11422,7 +11487,9 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
11422
11487
|
priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Task-level priority" },
|
|
11423
11488
|
owner: { type: "string", description: "Responsible person or team. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
|
|
11424
11489
|
start_date: { type: "string", description: "ISO date work begins" },
|
|
11425
|
-
target_date: { type: "string", description: "ISO date work completes" }
|
|
11490
|
+
target_date: { type: "string", description: "ISO date work completes" },
|
|
11491
|
+
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.' },
|
|
11492
|
+
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`.' }
|
|
11426
11493
|
},
|
|
11427
11494
|
// ErrorBudgetProperties: Error budget.
|
|
11428
11495
|
error_budget: {
|
|
@@ -11602,7 +11669,9 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
11602
11669
|
owner: { type: "string", description: "Responsible person or team. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
|
|
11603
11670
|
start_date: { type: "string", description: "ISO date work begins" },
|
|
11604
11671
|
target_date: { type: "string", description: "ISO date work completes" },
|
|
11605
|
-
health: { type: "string", enum: ["on_track", "at_risk", "off_track"], description: "Delivery health" }
|
|
11672
|
+
health: { type: "string", enum: ["on_track", "at_risk", "off_track"], description: "Delivery health" },
|
|
11673
|
+
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.' },
|
|
11674
|
+
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`.' }
|
|
11606
11675
|
},
|
|
11607
11676
|
// FeatureAreaProperties: A structural grouping of related features within a product.
|
|
11608
11677
|
feature_area: {
|
|
@@ -12522,6 +12591,16 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
12522
12591
|
conversion_rate: { type: "number", description: "Percentage of deals that advance from this stage", modifier: "snapshot" },
|
|
12523
12592
|
avg_days_in_stage: { type: "number", description: "Average number of days deals spend in this stage" }
|
|
12524
12593
|
},
|
|
12594
|
+
// PlanningCycleProperties: Planning cycle: a named, dated interval work flows through, which nests.
|
|
12595
|
+
planning_cycle: {
|
|
12596
|
+
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." },
|
|
12597
|
+
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.' },
|
|
12598
|
+
starts_on: { type: "string", description: "ISO date the interval opens. A cycle is concretely dated, unlike a coarse `time_horizon` label." },
|
|
12599
|
+
ends_on: { type: "string", description: "ISO date the interval closes." },
|
|
12600
|
+
sequence: { type: "number", description: "The cycle / iteration number (e.g. Sprint 47, PI 3)." },
|
|
12601
|
+
goal: { type: "string", description: "The interval's goal or focus: what this cadence box is for." },
|
|
12602
|
+
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").' }
|
|
12603
|
+
},
|
|
12525
12604
|
// PlaybookProperties: Customer success playbook.
|
|
12526
12605
|
playbook: {
|
|
12527
12606
|
playbook_type: { type: "string", enum: ["onboarding", "expansion", "renewal", "rescue", "other"], description: "Scenario this playbook addresses" },
|
|
@@ -13073,7 +13152,7 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
13073
13152
|
// StrategicThemeProperties: StrategicTheme entity.
|
|
13074
13153
|
strategic_theme: {
|
|
13075
13154
|
owner: { type: "string", description: "Owning person or team. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
|
|
13076
|
-
time_horizon: { type: "string", description: 'Bounded period this theme is active (a theme is time-bound, within a pillar). @example "Q1 2026", "FY26"' },
|
|
13155
|
+
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"' },
|
|
13077
13156
|
description: { type: "string", description: "Short narrative of this time-bound thrust within its pillar" },
|
|
13078
13157
|
scope: { type: "string", description: "What the theme explicitly includes or excludes" }
|
|
13079
13158
|
},
|
|
@@ -13169,7 +13248,9 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
13169
13248
|
effort: { type: "string", description: 'Effort estimate (e.g. "2h", "1d", "3 points"). Use a consistent unit within your team.' },
|
|
13170
13249
|
priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"], description: "Relative importance against other tasks" },
|
|
13171
13250
|
due_date: { type: "string", description: "ISO date due. Typically bounded by the containing story's due date." },
|
|
13172
|
-
labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." }
|
|
13251
|
+
labels: { type: "string[]", description: "Free-form classification tags. Applied uniformly across work item types." },
|
|
13252
|
+
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.' },
|
|
13253
|
+
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`.' }
|
|
13173
13254
|
},
|
|
13174
13255
|
// TeamProperties: Team entity.
|
|
13175
13256
|
team: {
|
|
@@ -13384,7 +13465,13 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
13384
13465
|
as_a: { type: "string", description: '"As a [persona], \u2026". Free-text persona name or slug ref.' },
|
|
13385
13466
|
i_want_to: { type: "string", description: "Capability or action the persona wants." },
|
|
13386
13467
|
so_that: { type: "string", description: "Benefit or outcome the persona expects." },
|
|
13387
|
-
text: { type: "string", description: "Free-form story text. Used as a single-line rendered view." }
|
|
13468
|
+
text: { type: "string", description: "Free-form story text. Used as a single-line rendered view." },
|
|
13469
|
+
assignee: { type: "string", description: "Assigned person. Promote to a `node_owned_by_person` edge if ownership must be queryable." },
|
|
13470
|
+
effort: { type: "string", description: 'Effort estimate (e.g. "2h", "1d", "3 points"). Use a consistent unit within your team.' },
|
|
13471
|
+
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." },
|
|
13472
|
+
due_date: { type: "string", description: "ISO date due. Typically bounded by the release or planning_cycle the story is scheduled into." },
|
|
13473
|
+
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.' },
|
|
13474
|
+
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`.' }
|
|
13388
13475
|
},
|
|
13389
13476
|
// ValueObjectProperties: DDD value object.
|
|
13390
13477
|
value_object: {
|
|
@@ -20030,6 +20117,7 @@ var ENTITY_EMOJI = {
|
|
|
20030
20117
|
persona: "\u{1F3AD}",
|
|
20031
20118
|
pipeline_sales: "\u{1FA88}",
|
|
20032
20119
|
pipeline_stage: "\u{1F9FD}",
|
|
20120
|
+
planning_cycle: "\u{1F5D3}\uFE0F",
|
|
20033
20121
|
playbook: "\u{1F4D2}",
|
|
20034
20122
|
portfolio: "\u{1F9F8}",
|
|
20035
20123
|
positioning: "\u{1F3AA}",
|
|
@@ -20721,6 +20809,7 @@ var STANDARD_LABELS = {
|
|
|
20721
20809
|
roadmap_item: { alt_labels: ["roadmap entry", "planned item"] },
|
|
20722
20810
|
roadmap_theme: { alt_labels: ["product theme", "roadmap theme"] },
|
|
20723
20811
|
// UPG-660: renamed from bare 'theme' (N6 lineage)
|
|
20812
|
+
planning_cycle: { alt_labels: ["sprint", "iteration", "cycle", "program increment", "pi", "cadence", "time-box", "cooldown", "quarter"] },
|
|
20724
20813
|
// Engineering layer
|
|
20725
20814
|
bounded_context: { alt_labels: ["context", "domain boundary", "module boundary"] },
|
|
20726
20815
|
service: { alt_labels: ["microservice", "backend service", "api service"] },
|
|
@@ -23242,13 +23331,19 @@ var PRODUCT_SPEC_GUIDE = {
|
|
|
23242
23331
|
// registered to product_spec but missing from the navigation order).
|
|
23243
23332
|
// `changelog` lives here because it is a structural product-shipping
|
|
23244
23333
|
// artefact; content domain references it only via cross-domain bridges.
|
|
23245
|
-
creation_sequence: ["feature_area", "feature", "epic", "user_story", "acceptance_criterion", "task", "bug", "release", "roadmap", "roadmap_item", "roadmap_theme", "changelog"],
|
|
23334
|
+
creation_sequence: ["feature_area", "feature", "epic", "user_story", "acceptance_criterion", "task", "bug", "release", "roadmap", "roadmap_item", "roadmap_theme", "changelog", "planning_cycle"],
|
|
23246
23335
|
patterns: [
|
|
23247
23336
|
{
|
|
23248
23337
|
name: "Feature Decomposition",
|
|
23249
23338
|
description: "Features group into areas, decompose into epics, epics specify user stories (the templated promise), and tasks implement them as the engineering work",
|
|
23250
23339
|
entity_types: ["feature_area", "feature", "epic", "user_story", "task"],
|
|
23251
23340
|
edge_chain: ["feature_area_contains_feature", "feature_decomposed_into_epic", "epic_specified_by_user_story", "task_implements_user_story"]
|
|
23341
|
+
},
|
|
23342
|
+
{
|
|
23343
|
+
name: "Planning Cadence",
|
|
23344
|
+
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.",
|
|
23345
|
+
entity_types: ["planning_cycle", "user_story", "objective"],
|
|
23346
|
+
edge_chain: ["planning_cycle_contains_planning_cycle", "planning_cycle_schedules_user_story", "objective_scoped_to_planning_cycle"]
|
|
23252
23347
|
}
|
|
23253
23348
|
],
|
|
23254
23349
|
required_bridges: [
|
|
@@ -26517,6 +26612,40 @@ var UPG_ANTI_PATTERNS = [
|
|
|
26517
26612
|
severity: "high",
|
|
26518
26613
|
source: { kind: "practitioner", attribution: "John Cutler, Outcomes over Output" }
|
|
26519
26614
|
},
|
|
26615
|
+
{
|
|
26616
|
+
id: "planning-cycle-without-scheduled-work",
|
|
26617
|
+
name: "Planning cycle with no scheduled work",
|
|
26618
|
+
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.",
|
|
26619
|
+
structured_condition: {
|
|
26620
|
+
operator: "and",
|
|
26621
|
+
checks: [
|
|
26622
|
+
{ check: { type: "entity_count", entity_type: "planning_cycle", comparison: "nonzero" } },
|
|
26623
|
+
{
|
|
26624
|
+
check: {
|
|
26625
|
+
type: "relationship",
|
|
26626
|
+
source_type: "planning_cycle",
|
|
26627
|
+
edge_type: "planning_cycle_schedules_user_story",
|
|
26628
|
+
target_type: "user_story",
|
|
26629
|
+
comparison: "not_exists"
|
|
26630
|
+
}
|
|
26631
|
+
},
|
|
26632
|
+
{
|
|
26633
|
+
check: {
|
|
26634
|
+
type: "relationship",
|
|
26635
|
+
source_type: "planning_cycle",
|
|
26636
|
+
edge_type: "planning_cycle_contains_planning_cycle",
|
|
26637
|
+
target_type: "planning_cycle",
|
|
26638
|
+
comparison: "not_exists"
|
|
26639
|
+
}
|
|
26640
|
+
}
|
|
26641
|
+
]
|
|
26642
|
+
},
|
|
26643
|
+
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.",
|
|
26644
|
+
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.",
|
|
26645
|
+
stages: ["build", "beta", "launch", "growth", "mature"],
|
|
26646
|
+
severity: "low",
|
|
26647
|
+
since: "0.20.0"
|
|
26648
|
+
},
|
|
26520
26649
|
// ── Market intelligence layer ───────────────────────────────────────────
|
|
26521
26650
|
{
|
|
26522
26651
|
id: "competitors-missing-past-validation",
|
|
@@ -27345,6 +27474,16 @@ var UPG_REGIONS = [
|
|
|
27345
27474
|
edge_id: "outcome_delivered_via_feature_area",
|
|
27346
27475
|
crosses_into: "product_delivery"
|
|
27347
27476
|
},
|
|
27477
|
+
{
|
|
27478
|
+
direction: "export",
|
|
27479
|
+
edge_id: "objective_scoped_to_planning_cycle",
|
|
27480
|
+
crosses_into: "product_delivery"
|
|
27481
|
+
},
|
|
27482
|
+
{
|
|
27483
|
+
direction: "export",
|
|
27484
|
+
edge_id: "strategic_theme_scoped_to_planning_cycle",
|
|
27485
|
+
crosses_into: "product_delivery"
|
|
27486
|
+
},
|
|
27348
27487
|
{
|
|
27349
27488
|
direction: "import",
|
|
27350
27489
|
edge_id: "feature_drives_key_result",
|
|
@@ -28156,6 +28295,11 @@ var UPG_REGIONS = [
|
|
|
28156
28295
|
{
|
|
28157
28296
|
type: "status_report",
|
|
28158
28297
|
role: "leaf"
|
|
28298
|
+
},
|
|
28299
|
+
{
|
|
28300
|
+
type: "planning_cycle",
|
|
28301
|
+
role: "container",
|
|
28302
|
+
notes: "self-nesting cadence axis"
|
|
28159
28303
|
}
|
|
28160
28304
|
],
|
|
28161
28305
|
anchor: {
|
|
@@ -28179,7 +28323,9 @@ var UPG_REGIONS = [
|
|
|
28179
28323
|
"roadmap_theme_spans_feature_area",
|
|
28180
28324
|
"milestone_gates_release",
|
|
28181
28325
|
"roadmap_item_references_feature",
|
|
28182
|
-
"feature_request_voted_on_by_feedback_vote"
|
|
28326
|
+
"feature_request_voted_on_by_feedback_vote",
|
|
28327
|
+
"planning_cycle_contains_planning_cycle",
|
|
28328
|
+
"planning_cycle_schedules_user_story"
|
|
28183
28329
|
],
|
|
28184
28330
|
boundary_edges: [
|
|
28185
28331
|
{
|
|
@@ -28187,6 +28333,16 @@ var UPG_REGIONS = [
|
|
|
28187
28333
|
edge_id: "outcome_delivered_by_feature",
|
|
28188
28334
|
crosses_into: "strategy_outcomes"
|
|
28189
28335
|
},
|
|
28336
|
+
{
|
|
28337
|
+
direction: "import",
|
|
28338
|
+
edge_id: "objective_scoped_to_planning_cycle",
|
|
28339
|
+
crosses_into: "strategy_outcomes"
|
|
28340
|
+
},
|
|
28341
|
+
{
|
|
28342
|
+
direction: "import",
|
|
28343
|
+
edge_id: "strategic_theme_scoped_to_planning_cycle",
|
|
28344
|
+
crosses_into: "strategy_outcomes"
|
|
28345
|
+
},
|
|
28190
28346
|
{
|
|
28191
28347
|
direction: "import",
|
|
28192
28348
|
edge_id: "outcome_delivered_via_feature_area",
|
|
@@ -29999,7 +30155,7 @@ function isCanonical(text) {
|
|
|
29999
30155
|
}
|
|
30000
30156
|
|
|
30001
30157
|
// src/index.ts
|
|
30002
|
-
var UPG_VERSION = "0.
|
|
30158
|
+
var UPG_VERSION = "0.20.0";
|
|
30003
30159
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
30004
30160
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
30005
30161
|
var UPG_TYPES = getTypes();
|