@unified-product-graph/mcp-server 0.8.1 → 0.8.4
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 +22 -0
- package/README.md +1 -1
- package/TOOLS.md +79 -16
- package/dist/index.js +1440 -290
- package/dist/index.js.map +1 -1
- package/dist/tools-manifest.json +197 -82
- package/package.json +1 -1
- package/scripts/claudemd-snippet.md +7 -7
- package/scripts/install-skills.sh +2 -2
- package/skills/upg/SKILL.md +41 -41
- package/skills/{upg-gaps → upg-check-gaps}/SKILL.md +40 -43
- package/skills/{upg-schema-health → upg-check-schema}/SKILL.md +7 -7
- package/skills/{upg-schema-evolve → upg-check-schema-coverage}/SKILL.md +12 -12
- package/skills/{upg-schema-edges → upg-check-schema-edges}/SKILL.md +3 -3
- package/skills/{upg-schema-consolidate → upg-check-schema-merge}/SKILL.md +5 -5
- package/skills/upg-context/SKILL.md +96 -72
- package/skills/upg-context-intelligence/SKILL.md +23 -27
- package/skills/upg-design-system/SKILL.md +21 -26
- package/skills/{upg-verify → upg-find-untracked}/SKILL.md +7 -12
- package/skills/{upg-rollback → upg-fix-rollback}/SKILL.md +6 -12
- package/skills/{upg-migrate → upg-fix-types}/SKILL.md +5 -9
- package/skills/upg-link/SKILL.md +125 -0
- package/skills/{upg-discover → upg-new-discovery}/SKILL.md +42 -58
- package/skills/{upg-capture → upg-new-from-session}/SKILL.md +13 -15
- package/skills/{upg-template → upg-new-from-template}/SKILL.md +8 -12
- package/skills/{upg-init → upg-new-graph}/SKILL.md +50 -82
- package/skills/{upg-hypothesis → upg-new-hypothesis}/SKILL.md +27 -36
- package/skills/{upg-launch → upg-new-launch}/SKILL-DETAIL.md +36 -92
- package/skills/{upg-launch → upg-new-launch}/SKILL.md +8 -18
- package/skills/{upg-okr → upg-new-okr}/SKILL-DETAIL.md +28 -46
- package/skills/{upg-okr → upg-new-okr}/SKILL.md +3 -3
- package/skills/{upg-persona → upg-new-persona}/SKILL.md +35 -67
- package/skills/{upg-research → upg-new-research}/SKILL.md +25 -33
- package/skills/{upg-schema-update → upg-new-schema-type}/SKILL.md +2 -2
- package/skills/{upg-strategy → upg-new-strategy}/SKILL.md +21 -27
- package/skills/upg-prioritise/SKILL.md +4 -4
- package/skills/upg-reflect/SKILL.md +7 -7
- package/skills/{upg-feedback → upg-send-feedback}/SKILL.md +30 -51
- package/skills/{upg-diff → upg-show-diff}/SKILL.md +6 -12
- package/skills/{upg-inspect → upg-show-entity}/SKILL.md +7 -9
- package/skills/{upg-impact → upg-show-impact}/SKILL.md +11 -15
- package/skills/{upg-journey → upg-show-journey}/SKILL.md +31 -32
- package/skills/{upg-analytics → upg-show-metrics}/SKILL.md +9 -12
- package/skills/{upg-schema-changelog → upg-show-schema-changelog}/SKILL.md +5 -5
- package/skills/{upg-status → upg-show-status}/SKILL.md +39 -40
- package/skills/{upg-tree → upg-show-tree}/SKILL.md +15 -15
- package/skills/{upg-export → upg-sync-export}/SKILL.md +10 -13
- package/skills/{upg-import → upg-sync-import}/SKILL.md +7 -13
- package/skills/{upg-pull → upg-sync-pull}/SKILL-DETAIL.md +13 -17
- package/skills/{upg-pull → upg-sync-pull}/SKILL.md +3 -3
- package/skills/{upg-push → upg-sync-push}/SKILL-DETAIL.md +4 -10
- package/skills/{upg-push → upg-sync-push}/SKILL.md +4 -4
- package/skills/{upg-snapshot → upg-sync-snapshot}/SKILL.md +2 -6
- package/skills/upg-trace/SKILL.md +7 -7
- package/skills/{upg-workspace → upg-use-workspace}/SKILL.md +8 -14
- package/skills/{upg-run → upg-walk-playbook}/SKILL.md +10 -10
- package/skills/upg-walk-region/SKILL-DETAIL.md +320 -0
- package/skills/upg-walk-region/SKILL.md +89 -0
- package/skills/upg-connect/SKILL.md +0 -167
- package/skills/upg-explore/SKILL-DETAIL.md +0 -481
- package/skills/upg-explore/SKILL.md +0 -297
package/dist/index.js
CHANGED
|
@@ -20,50 +20,10 @@ import {
|
|
|
20
20
|
// src/lib/server-context.ts
|
|
21
21
|
import * as fsp from "fs/promises";
|
|
22
22
|
import * as path from "path";
|
|
23
|
-
import { createHash } from "crypto";
|
|
24
|
-
function text(s) {
|
|
25
|
-
return { content: [{ type: "text", text: s }] };
|
|
26
|
-
}
|
|
27
|
-
function textError(s) {
|
|
28
|
-
return { content: [{ type: "text", text: s }], isError: true };
|
|
29
|
-
}
|
|
30
|
-
function createSessionContext() {
|
|
31
|
-
return {
|
|
32
|
-
lens: "product",
|
|
33
|
-
skills_invoked: [],
|
|
34
|
-
recommendations_given: [],
|
|
35
|
-
focus_area: null,
|
|
36
|
-
custom: {}
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function createQueryCache() {
|
|
40
|
-
return { entries: /* @__PURE__ */ new Map(), counter: 0 };
|
|
41
|
-
}
|
|
42
|
-
function syncFilePath(upgPath) {
|
|
43
|
-
const dir = path.dirname(upgPath);
|
|
44
|
-
const base = path.basename(upgPath, ".upg");
|
|
45
|
-
return path.join(dir, `${base}.upg-sync`);
|
|
46
|
-
}
|
|
47
|
-
async function readSyncState(upgPath) {
|
|
48
|
-
const p = syncFilePath(upgPath);
|
|
49
|
-
try {
|
|
50
|
-
const raw = await fsp.readFile(p, "utf-8");
|
|
51
|
-
return JSON.parse(raw);
|
|
52
|
-
} catch {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async function writeSyncState(upgPath, state) {
|
|
57
|
-
const p = syncFilePath(upgPath);
|
|
58
|
-
await fsp.writeFile(p, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
59
|
-
}
|
|
60
|
-
async function hashFile(filePath) {
|
|
61
|
-
const content = await fsp.readFile(filePath, "utf-8");
|
|
62
|
-
return createHash("sha256").update(content).digest("hex");
|
|
63
|
-
}
|
|
23
|
+
import { createHash as createHash2 } from "crypto";
|
|
64
24
|
|
|
65
25
|
// ../upg-spec/dist/index.js
|
|
66
|
-
import { createHash
|
|
26
|
+
import { createHash } from "crypto";
|
|
67
27
|
var UPG_DOMAINS = [
|
|
68
28
|
{
|
|
69
29
|
id: "strategy",
|
|
@@ -551,8 +511,8 @@ var UPG_DOMAINS = [
|
|
|
551
511
|
{
|
|
552
512
|
id: "workspace",
|
|
553
513
|
label: "Workspace",
|
|
554
|
-
description: "Spatial thinking spaces for arranging entities, debating decisions, and committing to the graph. Workspaces are transient canvases that sit alongside all other domains, letting you compose and explore relationships before they become permanent graph structure.",
|
|
555
|
-
types: ["workspace"]
|
|
514
|
+
description: "Spatial thinking spaces for arranging entities, debating decisions, and committing to the graph. Workspaces are transient canvases that sit alongside all other domains, letting you compose and explore relationships before they become permanent graph structure. A framework exercise is a structured workspace: one run of a framework (MoSCoW, RICE, Kano, \u2026) applied to a chosen set of entities, with each entity's result recorded on the exercise-to-entity edge rather than the entity itself.",
|
|
515
|
+
types: ["workspace", "framework_exercise"]
|
|
556
516
|
}
|
|
557
517
|
];
|
|
558
518
|
var UPG_ENTITY_TO_DOMAIN = Object.freeze(
|
|
@@ -973,7 +933,8 @@ var UPG_ENTITY_META = [
|
|
|
973
933
|
{ name: "integration_partner", type_id: "ent_311", maturity: "stable", since: "0.1.0" },
|
|
974
934
|
{ name: "partner_revenue_share", type_id: "ent_312", maturity: "stable", since: "0.1.0" },
|
|
975
935
|
// ── Workspace ──
|
|
976
|
-
{ name: "workspace", type_id: "ent_328", maturity: "proposed", since: "0.2.0" }
|
|
936
|
+
{ name: "workspace", type_id: "ent_328", maturity: "proposed", since: "0.2.0" },
|
|
937
|
+
{ name: "framework_exercise", type_id: "ent_350", maturity: "proposed", since: "0.8.4" }
|
|
977
938
|
];
|
|
978
939
|
var UPG_ENTITY_META_BY_NAME = new Map(
|
|
979
940
|
UPG_ENTITY_META.map((m) => [m.name, m])
|
|
@@ -993,7 +954,7 @@ var UPG_EDGE_CATALOG = {
|
|
|
993
954
|
// product → persona is the most fundamental relationship in the
|
|
994
955
|
// user domain ("who is this product for?") and was the only way to
|
|
995
956
|
// anchor a fresh persona to its product before this edge existed. Without
|
|
996
|
-
// it, the natural agent path through `/upg-persona` produced an orphan
|
|
957
|
+
// it, the natural agent path through `/upg-new-persona` produced an orphan
|
|
997
958
|
// persona only attached laterally via `ideal_customer_profile_maps_to_persona`
|
|
998
959
|
// or `positioning_resonates_with_persona`. Semantic, not hierarchy: a
|
|
999
960
|
// product doesn't "contain" personas; it targets them.
|
|
@@ -2113,6 +2074,18 @@ var UPG_EDGE_CATALOG = {
|
|
|
2113
2074
|
// universal across the DDD type family. Verbs follow DDD literature:
|
|
2114
2075
|
// aggregate belongs_to context, context contains aggregates.
|
|
2115
2076
|
node_belongs_to_bounded_context: { forward_verb: "belongs_to", reverse_verb: "contains", classification: "cross-domain", source_type: "node", target_type: "bounded_context" },
|
|
2077
|
+
// ── Cross-domain: Framework exercises ────────────────────────────────────────
|
|
2078
|
+
// A framework_exercise is one run of a framework (MoSCoW, RICE, Kano, …) over a
|
|
2079
|
+
// set of entities. It `includes` each entity it touches; the framework's
|
|
2080
|
+
// per-entity result — a MoSCoW bucket, a RICE score, a canvas slot, a funnel
|
|
2081
|
+
// stage — rides on this edge's `properties`, NOT on the entity node. A value
|
|
2082
|
+
// that exists only within a specific exercise is a fact about the relationship,
|
|
2083
|
+
// not the entity, so it lives on the edge (same principle as owner-as-edge).
|
|
2084
|
+
// Polymorphic (`target_type: 'node'`): an exercise can include any entity type,
|
|
2085
|
+
// which closes the feature-only limitation structurally. `carries_properties`
|
|
2086
|
+
// opts this edge into the gated edge-property model. See ADR
|
|
2087
|
+
// 2026-06-02-framework-exercises.
|
|
2088
|
+
framework_exercise_includes_node: { forward_verb: "includes", reverse_verb: "included_in", classification: "cross-domain", source_type: "framework_exercise", target_type: "node", carries_properties: true },
|
|
2116
2089
|
// ── New edges replacing deleted string properties ────────────────────
|
|
2117
2090
|
// Marketing
|
|
2118
2091
|
marketing_strategy_pursues_outcome: { forward_verb: "pursues", reverse_verb: "pursued_by", classification: "cross-domain", source_type: "marketing_strategy", target_type: "outcome" },
|
|
@@ -2208,6 +2181,13 @@ var UPG_EDGE_CATALOG = {
|
|
|
2208
2181
|
// release strategy to the concrete deployments it governs.
|
|
2209
2182
|
incident_affects_feature: { forward_verb: "affects", reverse_verb: "affected_by", classification: "cross-domain", source_type: "incident", target_type: "feature" },
|
|
2210
2183
|
release_strategy_used_by_deployment: { forward_verb: "used_by", reverse_verb: "uses", classification: "cross-domain", source_type: "release_strategy", target_type: "deployment" },
|
|
2184
|
+
// v0.8.2 (UPG-615): ITIL/ITSM incident management explicitly links an incident
|
|
2185
|
+
// to the customer-facing support tickets/cases it spawns — when a service
|
|
2186
|
+
// breaks, customers raise tickets; the incident is the cause, the ticket the
|
|
2187
|
+
// effect. Closes the otherwise-unmediated "Customer Support" island in
|
|
2188
|
+
// `playbook:operations-quality`, binding `support_ticket` to the incident/ops
|
|
2189
|
+
// spine. Source: ITIL v4 incident management; DORA/SRE customer-facing impact.
|
|
2190
|
+
incident_generates_support_ticket: { forward_verb: "generates", reverse_verb: "generated_by", classification: "cross-domain", source_type: "incident", target_type: "support_ticket" },
|
|
2211
2191
|
// Cluster C: User Research linkage matrix.
|
|
2212
2192
|
// research_study → {participant, research_question, survey_response,
|
|
2213
2193
|
// interview_guide} containment edges already exist (49/49 in Wave 4).
|
|
@@ -2735,7 +2715,9 @@ var UPG_POLYMORPHIC_EDGE_KEYS = [
|
|
|
2735
2715
|
"node_owned_by_department",
|
|
2736
2716
|
"node_owned_by_person",
|
|
2737
2717
|
// Universal architecture references
|
|
2738
|
-
"node_belongs_to_bounded_context"
|
|
2718
|
+
"node_belongs_to_bounded_context",
|
|
2719
|
+
// Framework exercises (an exercise can include any entity type)
|
|
2720
|
+
"framework_exercise_includes_node"
|
|
2739
2721
|
];
|
|
2740
2722
|
var _POLY_KEY_SET = new Set(UPG_POLYMORPHIC_EDGE_KEYS);
|
|
2741
2723
|
var LEGACY_PRODUCT_STAGES = Object.freeze({
|
|
@@ -5658,6 +5640,31 @@ var TEMPLATE_LIFECYCLES = [
|
|
|
5658
5640
|
// ── Phase B: SALES_DEAL (qualified → proposal → negotiation → won/lost) ─────
|
|
5659
5641
|
fromTemplate("deal", SALES_DEAL_TEMPLATE)
|
|
5660
5642
|
];
|
|
5643
|
+
var FRAMEWORK_EXERCISE_LIFECYCLE = {
|
|
5644
|
+
entity_type: "framework_exercise",
|
|
5645
|
+
initial_phase: "draft",
|
|
5646
|
+
terminal_phases: ["archived"],
|
|
5647
|
+
phases: [
|
|
5648
|
+
{
|
|
5649
|
+
id: "draft",
|
|
5650
|
+
label: "Draft",
|
|
5651
|
+
description: "The exercise has been created and entities pulled into scope, but the framework's inputs have not all been filled in yet.",
|
|
5652
|
+
transitions_to: ["active", "archived"]
|
|
5653
|
+
},
|
|
5654
|
+
{
|
|
5655
|
+
id: "active",
|
|
5656
|
+
label: "Active",
|
|
5657
|
+
description: "The current, authoritative run of its framework. Its include edges carry the live results consumers read, rank, and render by.",
|
|
5658
|
+
transitions_to: ["archived"]
|
|
5659
|
+
},
|
|
5660
|
+
{
|
|
5661
|
+
id: "archived",
|
|
5662
|
+
label: "Archived",
|
|
5663
|
+
description: "A past run, retained for provenance. Still queryable but superseded by a newer exercise and hidden from default views. Can be revived to active.",
|
|
5664
|
+
transitions_to: ["active"]
|
|
5665
|
+
}
|
|
5666
|
+
]
|
|
5667
|
+
};
|
|
5661
5668
|
var UPG_LIFECYCLES = [
|
|
5662
5669
|
// Product (root)
|
|
5663
5670
|
PRODUCT_LIFECYCLE,
|
|
@@ -5759,6 +5766,8 @@ var UPG_LIFECYCLES = [
|
|
|
5759
5766
|
// Product & Growth
|
|
5760
5767
|
VARIANT_LIFECYCLE,
|
|
5761
5768
|
GROWTH_CAMPAIGN_LIFECYCLE,
|
|
5769
|
+
// Workspace
|
|
5770
|
+
FRAMEWORK_EXERCISE_LIFECYCLE,
|
|
5762
5771
|
// ── Template-generated lifecycles ─────────────────────────────────
|
|
5763
5772
|
...TEMPLATE_LIFECYCLES
|
|
5764
5773
|
];
|
|
@@ -9236,6 +9245,11 @@ var UPG_PROPERTY_SCHEMA = {
|
|
|
9236
9245
|
},
|
|
9237
9246
|
methodology: { type: "string", description: "Forecasting methodology used" }
|
|
9238
9247
|
},
|
|
9248
|
+
// FrameworkExerciseProperties: Framework exercise: one run of a framework over a set of entities.
|
|
9249
|
+
framework_exercise: {
|
|
9250
|
+
framework_id: { type: "string", description: "Which framework this exercise runs: a framework id (e.g. 'moscow', 'rice-scoring', 'kano-model'). Resolves against the framework catalog." },
|
|
9251
|
+
inputs_snapshot: { type: "object", description: "Optional frozen copy of the framework's input spec at apply time, so a historical exercise still renders correctly if the framework definition later evolves (inputs added, removed, or rescaled)." }
|
|
9252
|
+
},
|
|
9239
9253
|
// FunnelProperties: Funnel entity.
|
|
9240
9254
|
funnel: {
|
|
9241
9255
|
funnel_type: { type: "string", enum: ["acquisition", "activation", "retention", "revenue", "referral", "custom"], description: "Which stage of the customer lifecycle this funnel measures" },
|
|
@@ -11261,6 +11275,9 @@ var UPG_FRAMEWORKS = [
|
|
|
11261
11275
|
},
|
|
11262
11276
|
{
|
|
11263
11277
|
"id": "value-proposition-canvas",
|
|
11278
|
+
"approach_ids": [
|
|
11279
|
+
"trace"
|
|
11280
|
+
],
|
|
11264
11281
|
"name": "Value Proposition Canvas",
|
|
11265
11282
|
"version": "1.0.0",
|
|
11266
11283
|
"description": "Map customer jobs, pains, and gains on one side, then align product features, pain relievers, and gain creators on the other to achieve product-market fit.",
|
|
@@ -11365,6 +11382,9 @@ var UPG_FRAMEWORKS = [
|
|
|
11365
11382
|
},
|
|
11366
11383
|
{
|
|
11367
11384
|
"id": "persona-canvas",
|
|
11385
|
+
"approach_ids": [
|
|
11386
|
+
"trace"
|
|
11387
|
+
],
|
|
11368
11388
|
"name": "Persona Canvas",
|
|
11369
11389
|
"version": "1.0.0",
|
|
11370
11390
|
"description": "Demographics, goals, frustrations, JTBD: a structured template for creating research-backed personas.",
|
|
@@ -11477,6 +11497,9 @@ var UPG_FRAMEWORKS = [
|
|
|
11477
11497
|
},
|
|
11478
11498
|
{
|
|
11479
11499
|
"id": "empathy-map",
|
|
11500
|
+
"approach_ids": [
|
|
11501
|
+
"trace"
|
|
11502
|
+
],
|
|
11480
11503
|
"name": "Empathy Map",
|
|
11481
11504
|
"version": "1.0.0",
|
|
11482
11505
|
"description": "Visualise what a user says, thinks, does, and feels to build deeper empathy and uncover hidden needs.",
|
|
@@ -11637,6 +11660,7 @@ var UPG_FRAMEWORKS = [
|
|
|
11637
11660
|
"property": "evolution_stage",
|
|
11638
11661
|
"type": "enum",
|
|
11639
11662
|
"required": true,
|
|
11663
|
+
"scope": "framework",
|
|
11640
11664
|
"label": "Evolution Stage",
|
|
11641
11665
|
"description": "Where this component sits on the evolution axis",
|
|
11642
11666
|
"enum_values": [
|
|
@@ -11650,6 +11674,7 @@ var UPG_FRAMEWORKS = [
|
|
|
11650
11674
|
"property": "visibility",
|
|
11651
11675
|
"type": "number",
|
|
11652
11676
|
"required": true,
|
|
11677
|
+
"scope": "framework",
|
|
11653
11678
|
"label": "Visibility",
|
|
11654
11679
|
"description": "Y-axis position (0=infrastructure, 1=anchor/user)"
|
|
11655
11680
|
}
|
|
@@ -11659,6 +11684,7 @@ var UPG_FRAMEWORKS = [
|
|
|
11659
11684
|
"property": "evolution_stage",
|
|
11660
11685
|
"type": "enum",
|
|
11661
11686
|
"required": true,
|
|
11687
|
+
"scope": "framework",
|
|
11662
11688
|
"label": "Evolution Stage",
|
|
11663
11689
|
"description": "Where this component sits on the evolution axis",
|
|
11664
11690
|
"enum_values": [
|
|
@@ -11672,6 +11698,7 @@ var UPG_FRAMEWORKS = [
|
|
|
11672
11698
|
"property": "visibility",
|
|
11673
11699
|
"type": "number",
|
|
11674
11700
|
"required": true,
|
|
11701
|
+
"scope": "framework",
|
|
11675
11702
|
"label": "Visibility",
|
|
11676
11703
|
"description": "Y-axis position (0=infrastructure, 1=anchor/user)"
|
|
11677
11704
|
}
|
|
@@ -11681,6 +11708,7 @@ var UPG_FRAMEWORKS = [
|
|
|
11681
11708
|
"property": "evolution_stage",
|
|
11682
11709
|
"type": "enum",
|
|
11683
11710
|
"required": true,
|
|
11711
|
+
"scope": "framework",
|
|
11684
11712
|
"label": "Evolution Stage",
|
|
11685
11713
|
"description": "Where this component sits on the evolution axis",
|
|
11686
11714
|
"enum_values": [
|
|
@@ -11694,6 +11722,7 @@ var UPG_FRAMEWORKS = [
|
|
|
11694
11722
|
"property": "visibility",
|
|
11695
11723
|
"type": "number",
|
|
11696
11724
|
"required": true,
|
|
11725
|
+
"scope": "framework",
|
|
11697
11726
|
"label": "Visibility",
|
|
11698
11727
|
"description": "Y-axis position (0=infrastructure, 1=anchor/user)"
|
|
11699
11728
|
}
|
|
@@ -11703,6 +11732,7 @@ var UPG_FRAMEWORKS = [
|
|
|
11703
11732
|
"property": "evolution_stage",
|
|
11704
11733
|
"type": "enum",
|
|
11705
11734
|
"required": true,
|
|
11735
|
+
"scope": "framework",
|
|
11706
11736
|
"label": "Evolution Stage",
|
|
11707
11737
|
"description": 'Where this component sits on the evolution axis (need anchor is usually at "product" or "commodity")',
|
|
11708
11738
|
"enum_values": [
|
|
@@ -11716,6 +11746,7 @@ var UPG_FRAMEWORKS = [
|
|
|
11716
11746
|
"property": "visibility",
|
|
11717
11747
|
"type": "number",
|
|
11718
11748
|
"required": true,
|
|
11749
|
+
"scope": "framework",
|
|
11719
11750
|
"label": "Visibility",
|
|
11720
11751
|
"description": "Y-axis position (0=infrastructure, 1=anchor/user); needs sit at 1.0"
|
|
11721
11752
|
}
|
|
@@ -11756,6 +11787,9 @@ var UPG_FRAMEWORKS = [
|
|
|
11756
11787
|
},
|
|
11757
11788
|
{
|
|
11758
11789
|
"id": "business-model-canvas",
|
|
11790
|
+
"approach_ids": [
|
|
11791
|
+
"plan"
|
|
11792
|
+
],
|
|
11759
11793
|
"name": "Business Model Canvas",
|
|
11760
11794
|
"version": "1.0.0",
|
|
11761
11795
|
"description": "Nine building blocks that describe how an organisation creates, delivers, and captures value.",
|
|
@@ -11895,6 +11929,9 @@ var UPG_FRAMEWORKS = [
|
|
|
11895
11929
|
},
|
|
11896
11930
|
{
|
|
11897
11931
|
"id": "porter-five-forces",
|
|
11932
|
+
"approach_ids": [
|
|
11933
|
+
"inspect"
|
|
11934
|
+
],
|
|
11898
11935
|
"name": "Porter Five Forces",
|
|
11899
11936
|
"version": "1.0.0",
|
|
11900
11937
|
"description": "Analyse industry competitiveness through five forces: rivalry, new entrants, substitutes, buyer power, and supplier power.",
|
|
@@ -11984,6 +12021,9 @@ var UPG_FRAMEWORKS = [
|
|
|
11984
12021
|
},
|
|
11985
12022
|
{
|
|
11986
12023
|
"id": "swot-analysis",
|
|
12024
|
+
"approach_ids": [
|
|
12025
|
+
"inspect"
|
|
12026
|
+
],
|
|
11987
12027
|
"name": "SWOT Analysis",
|
|
11988
12028
|
"version": "1.0.0",
|
|
11989
12029
|
"description": "Map Strengths, Weaknesses, Opportunities, and Threats in a 2x2 grid. Internal vs external, helpful vs harmful.",
|
|
@@ -12310,6 +12350,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12310
12350
|
"type": "assessment",
|
|
12311
12351
|
"scale_id": "reach_5",
|
|
12312
12352
|
"required": true,
|
|
12353
|
+
"scope": "framework",
|
|
12313
12354
|
"label": "Reach",
|
|
12314
12355
|
"description": "How many users will this impact per quarter?"
|
|
12315
12356
|
},
|
|
@@ -12318,6 +12359,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12318
12359
|
"type": "assessment",
|
|
12319
12360
|
"scale_id": "impact_5",
|
|
12320
12361
|
"required": true,
|
|
12362
|
+
"scope": "framework",
|
|
12321
12363
|
"label": "Impact",
|
|
12322
12364
|
"description": "How much will this impact each user, on the impact scale?"
|
|
12323
12365
|
},
|
|
@@ -12326,6 +12368,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12326
12368
|
"type": "assessment",
|
|
12327
12369
|
"scale_id": "confidence_5",
|
|
12328
12370
|
"required": true,
|
|
12371
|
+
"scope": "framework",
|
|
12329
12372
|
"label": "Confidence",
|
|
12330
12373
|
"description": "How confident are you in the reach, impact, and effort estimates?"
|
|
12331
12374
|
},
|
|
@@ -12334,6 +12377,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12334
12377
|
"type": "assessment",
|
|
12335
12378
|
"scale_id": "effort_5",
|
|
12336
12379
|
"required": true,
|
|
12380
|
+
"scope": "framework",
|
|
12337
12381
|
"label": "Effort",
|
|
12338
12382
|
"description": "How much work is required to build and ship this, on the effort scale?"
|
|
12339
12383
|
}
|
|
@@ -12420,6 +12464,9 @@ var UPG_FRAMEWORKS = [
|
|
|
12420
12464
|
},
|
|
12421
12465
|
{
|
|
12422
12466
|
"id": "build-measure-learn",
|
|
12467
|
+
"approach_ids": [
|
|
12468
|
+
"reflect"
|
|
12469
|
+
],
|
|
12423
12470
|
"name": "Build-Measure-Learn",
|
|
12424
12471
|
"version": "1.0.0",
|
|
12425
12472
|
"description": "The core Lean Startup feedback loop: build a minimum viable product, measure its impact with actionable metrics, and learn whether to pivot or persevere.",
|
|
@@ -12555,6 +12602,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12555
12602
|
"property": "functional_response",
|
|
12556
12603
|
"type": "enum",
|
|
12557
12604
|
"required": true,
|
|
12605
|
+
"scope": "framework",
|
|
12558
12606
|
"label": "Functional Response",
|
|
12559
12607
|
"description": "How users feel when the feature IS present",
|
|
12560
12608
|
"enum_values": [
|
|
@@ -12569,6 +12617,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12569
12617
|
"property": "dysfunctional_response",
|
|
12570
12618
|
"type": "enum",
|
|
12571
12619
|
"required": true,
|
|
12620
|
+
"scope": "framework",
|
|
12572
12621
|
"label": "Dysfunctional Response",
|
|
12573
12622
|
"description": "How users feel when the feature IS NOT present",
|
|
12574
12623
|
"enum_values": [
|
|
@@ -12583,6 +12632,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12583
12632
|
"property": "delighter_count",
|
|
12584
12633
|
"type": "number",
|
|
12585
12634
|
"required": false,
|
|
12635
|
+
"scope": "framework",
|
|
12586
12636
|
"label": "Delighter classifications",
|
|
12587
12637
|
"description": "Count of survey responses classifying this feature as a delighter (attractive)"
|
|
12588
12638
|
},
|
|
@@ -12590,6 +12640,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12590
12640
|
"property": "performance_count",
|
|
12591
12641
|
"type": "number",
|
|
12592
12642
|
"required": false,
|
|
12643
|
+
"scope": "framework",
|
|
12593
12644
|
"label": "Performance classifications",
|
|
12594
12645
|
"description": "Count of survey responses classifying this feature as performance (one-dimensional)"
|
|
12595
12646
|
},
|
|
@@ -12597,6 +12648,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12597
12648
|
"property": "must_be_count",
|
|
12598
12649
|
"type": "number",
|
|
12599
12650
|
"required": false,
|
|
12651
|
+
"scope": "framework",
|
|
12600
12652
|
"label": "Must-be classifications",
|
|
12601
12653
|
"description": "Count of survey responses classifying this feature as must-be (basic)"
|
|
12602
12654
|
},
|
|
@@ -12604,6 +12656,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12604
12656
|
"property": "indifferent_count",
|
|
12605
12657
|
"type": "number",
|
|
12606
12658
|
"required": false,
|
|
12659
|
+
"scope": "framework",
|
|
12607
12660
|
"label": "Indifferent classifications",
|
|
12608
12661
|
"description": "Count of survey responses classifying this feature as indifferent"
|
|
12609
12662
|
}
|
|
@@ -12796,6 +12849,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12796
12849
|
"property": "moscow",
|
|
12797
12850
|
"type": "enum",
|
|
12798
12851
|
"required": true,
|
|
12852
|
+
"scope": "framework",
|
|
12799
12853
|
"label": "MoSCoW priority",
|
|
12800
12854
|
"description": "Which scope bucket this requirement falls into for the current release",
|
|
12801
12855
|
"enum_values": [
|
|
@@ -12806,16 +12860,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12806
12860
|
]
|
|
12807
12861
|
}
|
|
12808
12862
|
]
|
|
12809
|
-
}
|
|
12810
|
-
"computed_properties": [
|
|
12811
|
-
{
|
|
12812
|
-
"property": "must_have_ratio",
|
|
12813
|
-
"expression": "must_count / total_count",
|
|
12814
|
-
"entity_type": "feature",
|
|
12815
|
-
"label": "Must-have Ratio",
|
|
12816
|
-
"format": "percentage"
|
|
12817
|
-
}
|
|
12818
|
-
]
|
|
12863
|
+
}
|
|
12819
12864
|
},
|
|
12820
12865
|
"structure": {
|
|
12821
12866
|
"pattern": "table"
|
|
@@ -12979,6 +13024,9 @@ var UPG_FRAMEWORKS = [
|
|
|
12979
13024
|
},
|
|
12980
13025
|
{
|
|
12981
13026
|
"id": "c4-model",
|
|
13027
|
+
"approach_ids": [
|
|
13028
|
+
"trace"
|
|
13029
|
+
],
|
|
12982
13030
|
"name": "C4 Model",
|
|
12983
13031
|
"version": "1.0.0",
|
|
12984
13032
|
"description": "Visualise software architecture at four levels of abstraction: System Context, Container, Component, and Code. Each level zooms in to reveal more detail.",
|
|
@@ -13071,6 +13119,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13071
13119
|
},
|
|
13072
13120
|
{
|
|
13073
13121
|
"id": "adr-log",
|
|
13122
|
+
"approach_ids": [
|
|
13123
|
+
"inspect"
|
|
13124
|
+
],
|
|
13074
13125
|
"name": "ADR Log",
|
|
13075
13126
|
"version": "1.0.0",
|
|
13076
13127
|
"description": "Architecture Decision Records: log decisions with context, options, and rationale.",
|
|
@@ -13184,6 +13235,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13184
13235
|
},
|
|
13185
13236
|
{
|
|
13186
13237
|
"id": "atomic-design",
|
|
13238
|
+
"approach_ids": [
|
|
13239
|
+
"trace"
|
|
13240
|
+
],
|
|
13187
13241
|
"name": "Atomic Design",
|
|
13188
13242
|
"version": "1.0.0",
|
|
13189
13243
|
"description": "Atoms, Molecules, Organisms, Templates, Pages: a methodology for creating design systems from the smallest elements up.",
|
|
@@ -13281,6 +13335,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13281
13335
|
},
|
|
13282
13336
|
{
|
|
13283
13337
|
"id": "double-diamond",
|
|
13338
|
+
"approach_ids": [
|
|
13339
|
+
"plan"
|
|
13340
|
+
],
|
|
13284
13341
|
"name": "Double Diamond",
|
|
13285
13342
|
"version": "1.0.0",
|
|
13286
13343
|
"description": "Discover, Define, Develop, Deliver: a four-phase divergent/convergent design process.",
|
|
@@ -13371,6 +13428,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13371
13428
|
},
|
|
13372
13429
|
{
|
|
13373
13430
|
"id": "dora-metrics",
|
|
13431
|
+
"approach_ids": [
|
|
13432
|
+
"inspect"
|
|
13433
|
+
],
|
|
13374
13434
|
"name": "DORA Metrics",
|
|
13375
13435
|
"version": "1.0.0",
|
|
13376
13436
|
"description": "Four key metrics for software delivery performance: deployment frequency, lead time, change failure rate, and time to restore.",
|
|
@@ -13430,16 +13490,7 @@ var UPG_FRAMEWORKS = [
|
|
|
13430
13490
|
]
|
|
13431
13491
|
}
|
|
13432
13492
|
]
|
|
13433
|
-
}
|
|
13434
|
-
"computed_properties": [
|
|
13435
|
-
{
|
|
13436
|
-
"property": "stability_index",
|
|
13437
|
-
"expression": "change_failure_rate * mean_time_to_recovery",
|
|
13438
|
-
"entity_type": "metric",
|
|
13439
|
-
"label": "Stability Index",
|
|
13440
|
-
"format": "number"
|
|
13441
|
-
}
|
|
13442
|
-
]
|
|
13493
|
+
}
|
|
13443
13494
|
},
|
|
13444
13495
|
"structure": {
|
|
13445
13496
|
"pattern": "collection"
|
|
@@ -13568,6 +13619,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13568
13619
|
},
|
|
13569
13620
|
{
|
|
13570
13621
|
"id": "pirate-metrics-aarrr",
|
|
13622
|
+
"approach_ids": [
|
|
13623
|
+
"trace"
|
|
13624
|
+
],
|
|
13571
13625
|
"name": "Pirate Metrics AARRR",
|
|
13572
13626
|
"version": "1.0.0",
|
|
13573
13627
|
"description": "Track user lifecycle across five stages: Acquisition, Activation, Retention, Revenue, and Referral.",
|
|
@@ -13601,7 +13655,7 @@ var UPG_FRAMEWORKS = [
|
|
|
13601
13655
|
"required_properties": {
|
|
13602
13656
|
"metric": [
|
|
13603
13657
|
{
|
|
13604
|
-
"property": "
|
|
13658
|
+
"property": "metric_category",
|
|
13605
13659
|
"type": "enum",
|
|
13606
13660
|
"required": true,
|
|
13607
13661
|
"label": "Lifecycle stage",
|
|
@@ -13615,16 +13669,7 @@ var UPG_FRAMEWORKS = [
|
|
|
13615
13669
|
]
|
|
13616
13670
|
}
|
|
13617
13671
|
]
|
|
13618
|
-
}
|
|
13619
|
-
"computed_properties": [
|
|
13620
|
-
{
|
|
13621
|
-
"property": "conversion_rate",
|
|
13622
|
-
"expression": "(stage_exits / stage_entries) * 100",
|
|
13623
|
-
"entity_type": "metric",
|
|
13624
|
-
"label": "Stage Conversion",
|
|
13625
|
-
"format": "percentage"
|
|
13626
|
-
}
|
|
13627
|
-
]
|
|
13672
|
+
}
|
|
13628
13673
|
},
|
|
13629
13674
|
"structure": {
|
|
13630
13675
|
"pattern": "funnel",
|
|
@@ -13688,6 +13733,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13688
13733
|
},
|
|
13689
13734
|
{
|
|
13690
13735
|
"id": "north-star-metric",
|
|
13736
|
+
"approach_ids": [
|
|
13737
|
+
"plan"
|
|
13738
|
+
],
|
|
13691
13739
|
"name": "North Star Metric",
|
|
13692
13740
|
"version": "1.0.0",
|
|
13693
13741
|
"description": "One metric that best captures the core value you deliver. Supported by 3-5 input metrics that drive it.",
|
|
@@ -13721,7 +13769,7 @@ var UPG_FRAMEWORKS = [
|
|
|
13721
13769
|
"required_properties": {
|
|
13722
13770
|
"metric": [
|
|
13723
13771
|
{
|
|
13724
|
-
"property": "
|
|
13772
|
+
"property": "designation",
|
|
13725
13773
|
"type": "enum",
|
|
13726
13774
|
"required": true,
|
|
13727
13775
|
"label": "Metric role",
|
|
@@ -13739,16 +13787,7 @@ var UPG_FRAMEWORKS = [
|
|
|
13739
13787
|
"description": "How strongly this input metric moves the North Star (input metrics only)"
|
|
13740
13788
|
}
|
|
13741
13789
|
]
|
|
13742
|
-
}
|
|
13743
|
-
"computed_properties": [
|
|
13744
|
-
{
|
|
13745
|
-
"property": "nsm_impact",
|
|
13746
|
-
"expression": "input_metric_value * leverage",
|
|
13747
|
-
"entity_type": "metric",
|
|
13748
|
-
"label": "NSM Impact",
|
|
13749
|
-
"format": "number"
|
|
13750
|
-
}
|
|
13751
|
-
]
|
|
13790
|
+
}
|
|
13752
13791
|
},
|
|
13753
13792
|
"structure": {
|
|
13754
13793
|
"pattern": "collection"
|
|
@@ -13784,6 +13823,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13784
13823
|
},
|
|
13785
13824
|
{
|
|
13786
13825
|
"id": "marketing-mix-4ps",
|
|
13826
|
+
"approach_ids": [
|
|
13827
|
+
"plan"
|
|
13828
|
+
],
|
|
13787
13829
|
"name": "Marketing Mix 4Ps",
|
|
13788
13830
|
"version": "1.0.0",
|
|
13789
13831
|
"description": "The foundational marketing framework. Every marketing strategy must address four decisions: what to sell (Product), what to charge (Price), where to sell (Place), and how to promote (Promotion).",
|
|
@@ -13877,6 +13919,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13877
13919
|
},
|
|
13878
13920
|
{
|
|
13879
13921
|
"id": "bullseye-framework",
|
|
13922
|
+
"approach_ids": [
|
|
13923
|
+
"plan"
|
|
13924
|
+
],
|
|
13880
13925
|
"name": "Bullseye Framework",
|
|
13881
13926
|
"version": "1.0.0",
|
|
13882
13927
|
"description": "Test 19 traction channels systematically. Start with the outer ring (what's possible), narrow to the middle ring (what's probable), then focus on the inner ring (what's working). Run cheap tests across all channels to find your bullseye.",
|
|
@@ -13963,6 +14008,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13963
14008
|
},
|
|
13964
14009
|
{
|
|
13965
14010
|
"id": "product-led-growth-framework",
|
|
14011
|
+
"approach_ids": [
|
|
14012
|
+
"plan"
|
|
14013
|
+
],
|
|
13966
14014
|
"name": "PLG Framework",
|
|
13967
14015
|
"version": "1.0.0",
|
|
13968
14016
|
"description": "Product-led go-to-market motion. Free entry gives users access, the aha moment hooks them, they expand usage within their team, and monetisation captures value from power users.",
|
|
@@ -14145,6 +14193,9 @@ var UPG_FRAMEWORKS = [
|
|
|
14145
14193
|
},
|
|
14146
14194
|
{
|
|
14147
14195
|
"id": "raci-matrix",
|
|
14196
|
+
"approach_ids": [
|
|
14197
|
+
"inspect"
|
|
14198
|
+
],
|
|
14148
14199
|
"name": "RACI Matrix",
|
|
14149
14200
|
"version": "1.0.0",
|
|
14150
14201
|
"description": "Assign roles: Responsible, Accountable, Consulted, Informed for each activity.",
|
|
@@ -14196,16 +14247,7 @@ var UPG_FRAMEWORKS = [
|
|
|
14196
14247
|
"role": "item"
|
|
14197
14248
|
}
|
|
14198
14249
|
],
|
|
14199
|
-
"required_properties": {}
|
|
14200
|
-
"computed_properties": [
|
|
14201
|
-
{
|
|
14202
|
-
"property": "coverage_score",
|
|
14203
|
-
"expression": "assigned_count / total_activities",
|
|
14204
|
-
"entity_type": "team",
|
|
14205
|
-
"label": "RACI Coverage",
|
|
14206
|
-
"format": "percentage"
|
|
14207
|
-
}
|
|
14208
|
-
]
|
|
14250
|
+
"required_properties": {}
|
|
14209
14251
|
},
|
|
14210
14252
|
"structure": {
|
|
14211
14253
|
"pattern": "matrix"
|
|
@@ -14429,6 +14471,9 @@ var UPG_FRAMEWORKS = [
|
|
|
14429
14471
|
},
|
|
14430
14472
|
{
|
|
14431
14473
|
"id": "team-health-check",
|
|
14474
|
+
"approach_ids": [
|
|
14475
|
+
"inspect"
|
|
14476
|
+
],
|
|
14432
14477
|
"name": "Team Health Check",
|
|
14433
14478
|
"version": "1.0.0",
|
|
14434
14479
|
"description": "A facilitated team self-assessment across dimensions like mission, fun, learning, speed, and support, using traffic-light voting to surface strengths and improvement areas in a safe format.",
|
|
@@ -14486,16 +14531,7 @@ var UPG_FRAMEWORKS = [
|
|
|
14486
14531
|
"role": "item"
|
|
14487
14532
|
}
|
|
14488
14533
|
],
|
|
14489
|
-
"required_properties": {}
|
|
14490
|
-
"computed_properties": [
|
|
14491
|
-
{
|
|
14492
|
-
"property": "health_index",
|
|
14493
|
-
"expression": "(green_count - red_count) / total_indicators",
|
|
14494
|
-
"entity_type": "team",
|
|
14495
|
-
"label": "Health Index",
|
|
14496
|
-
"format": "number"
|
|
14497
|
-
}
|
|
14498
|
-
]
|
|
14534
|
+
"required_properties": {}
|
|
14499
14535
|
},
|
|
14500
14536
|
"structure": {
|
|
14501
14537
|
"pattern": "table"
|
|
@@ -14552,6 +14588,9 @@ var UPG_FRAMEWORKS = [
|
|
|
14552
14588
|
},
|
|
14553
14589
|
{
|
|
14554
14590
|
"id": "raid-log",
|
|
14591
|
+
"approach_ids": [
|
|
14592
|
+
"inspect"
|
|
14593
|
+
],
|
|
14555
14594
|
"name": "RAID Log",
|
|
14556
14595
|
"version": "1.0.0",
|
|
14557
14596
|
"description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies, the four categories most likely to derail a project if left unmanaged.",
|
|
@@ -14571,6 +14610,11 @@ var UPG_FRAMEWORKS = [
|
|
|
14571
14610
|
"entityTypeId": "risk_register",
|
|
14572
14611
|
"description": "Risk or assumption logged with likelihood, impact, owner, and mitigation strategy"
|
|
14573
14612
|
},
|
|
14613
|
+
{
|
|
14614
|
+
"label": "Risk",
|
|
14615
|
+
"entityTypeId": "risk",
|
|
14616
|
+
"description": "Individual risk scored by probability and impact; severity = probability * impact"
|
|
14617
|
+
},
|
|
14574
14618
|
{
|
|
14575
14619
|
"label": "Dependency",
|
|
14576
14620
|
"entityTypeId": "dependency",
|
|
@@ -14593,6 +14637,10 @@ var UPG_FRAMEWORKS = [
|
|
|
14593
14637
|
"type": "risk_register",
|
|
14594
14638
|
"role": "item"
|
|
14595
14639
|
},
|
|
14640
|
+
{
|
|
14641
|
+
"type": "risk",
|
|
14642
|
+
"role": "scored_item"
|
|
14643
|
+
},
|
|
14596
14644
|
{
|
|
14597
14645
|
"type": "dependency",
|
|
14598
14646
|
"role": "item"
|
|
@@ -14606,7 +14654,24 @@ var UPG_FRAMEWORKS = [
|
|
|
14606
14654
|
"role": "item"
|
|
14607
14655
|
}
|
|
14608
14656
|
],
|
|
14609
|
-
"required_properties": {
|
|
14657
|
+
"required_properties": {
|
|
14658
|
+
"risk": [
|
|
14659
|
+
{
|
|
14660
|
+
"property": "probability",
|
|
14661
|
+
"type": "number",
|
|
14662
|
+
"required": true,
|
|
14663
|
+
"label": "Probability",
|
|
14664
|
+
"description": "Likelihood the risk materialises (risk.probability assessment)"
|
|
14665
|
+
},
|
|
14666
|
+
{
|
|
14667
|
+
"property": "impact",
|
|
14668
|
+
"type": "number",
|
|
14669
|
+
"required": true,
|
|
14670
|
+
"label": "Impact",
|
|
14671
|
+
"description": "Consequence severity if the risk materialises (risk.impact assessment)"
|
|
14672
|
+
}
|
|
14673
|
+
]
|
|
14674
|
+
},
|
|
14610
14675
|
"computed_properties": [
|
|
14611
14676
|
{
|
|
14612
14677
|
"property": "severity",
|
|
@@ -14667,90 +14732,992 @@ var UPG_FRAMEWORKS = [
|
|
|
14667
14732
|
"Formal programme management would create overhead without value"
|
|
14668
14733
|
]
|
|
14669
14734
|
}
|
|
14670
|
-
}
|
|
14671
|
-
];
|
|
14672
|
-
var UPG_FRAMEWORKS_BY_ID = Object.fromEntries(
|
|
14673
|
-
UPG_FRAMEWORKS.map((fw) => [fw.id, fw])
|
|
14674
|
-
);
|
|
14675
|
-
var UPG_FRAMEWORKS_BY_CATEGORY = {};
|
|
14676
|
-
for (const fw of UPG_FRAMEWORKS) {
|
|
14677
|
-
if (!UPG_FRAMEWORKS_BY_CATEGORY[fw.category]) UPG_FRAMEWORKS_BY_CATEGORY[fw.category] = [];
|
|
14678
|
-
UPG_FRAMEWORKS_BY_CATEGORY[fw.category].push(fw);
|
|
14679
|
-
}
|
|
14680
|
-
var PRIORITY_LABELS = [
|
|
14681
|
-
// ── need (CONSOLIDATED: replaces pain_point + user_need) ─────────────────────
|
|
14682
|
-
{
|
|
14683
|
-
id: "need",
|
|
14684
|
-
canonical_label: "Need",
|
|
14685
|
-
alt_labels: [
|
|
14686
|
-
"pain point",
|
|
14687
|
-
"pain",
|
|
14688
|
-
"user need",
|
|
14689
|
-
"customer need",
|
|
14690
|
-
"problem",
|
|
14691
|
-
"struggle",
|
|
14692
|
-
"customer pain",
|
|
14693
|
-
"frustration",
|
|
14694
|
-
"gap",
|
|
14695
|
-
"unmet need",
|
|
14696
|
-
"user problem"
|
|
14697
|
-
],
|
|
14698
|
-
framework_labels: {
|
|
14699
|
-
lean_canvas: "Problem",
|
|
14700
|
-
design_thinking: "Pain Point",
|
|
14701
|
-
ost: "Opportunity (need)",
|
|
14702
|
-
jtbd: "Struggle",
|
|
14703
|
-
vpc: "Customer Pain"
|
|
14704
|
-
},
|
|
14705
|
-
designations: {
|
|
14706
|
-
pain: "Pain Point",
|
|
14707
|
-
gap: "Need",
|
|
14708
|
-
desire: "Desire",
|
|
14709
|
-
constraint: "Constraint"
|
|
14710
|
-
}
|
|
14711
|
-
},
|
|
14712
|
-
// ── opportunity ──────────────────────────────────────────────────────────────
|
|
14713
|
-
{
|
|
14714
|
-
id: "opportunity",
|
|
14715
|
-
canonical_label: "Opportunity",
|
|
14716
|
-
alt_labels: ["product opportunity", "market opportunity", "user opportunity"],
|
|
14717
|
-
framework_labels: {
|
|
14718
|
-
ost: "Opportunity"
|
|
14719
|
-
}
|
|
14720
|
-
},
|
|
14721
|
-
// ── solution ─────────────────────────────────────────────────────────────────
|
|
14722
|
-
{
|
|
14723
|
-
id: "solution",
|
|
14724
|
-
canonical_label: "Solution",
|
|
14725
|
-
alt_labels: ["proposed solution", "solution idea", "concept", "approach"],
|
|
14726
|
-
framework_labels: {
|
|
14727
|
-
ost: "Solution",
|
|
14728
|
-
design_thinking: "Solution",
|
|
14729
|
-
lean_canvas: "Solution",
|
|
14730
|
-
rice: "Scored Solution"
|
|
14731
|
-
}
|
|
14732
14735
|
},
|
|
14733
|
-
// ── experiment (CONSOLIDATED: absorbs ab_test, growth_experiment, pricing_experiment) ──
|
|
14734
14736
|
{
|
|
14735
|
-
id: "
|
|
14736
|
-
|
|
14737
|
-
|
|
14738
|
-
"test",
|
|
14739
|
-
"validation",
|
|
14740
|
-
"ab test",
|
|
14741
|
-
"a/b test",
|
|
14742
|
-
"split test",
|
|
14743
|
-
"growth experiment",
|
|
14744
|
-
"pricing experiment",
|
|
14745
|
-
"usability test",
|
|
14746
|
-
"discovery experiment"
|
|
14737
|
+
"id": "ice-scoring",
|
|
14738
|
+
"approach_ids": [
|
|
14739
|
+
"prioritise"
|
|
14747
14740
|
],
|
|
14748
|
-
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
|
|
14752
|
-
|
|
14753
|
-
|
|
14741
|
+
"name": "ICE Scoring",
|
|
14742
|
+
"version": "1.0.0",
|
|
14743
|
+
"description": "Rate ideas by Impact, Confidence, and Ease on a 1-10 scale. Multiply for a composite score. Fast and lightweight.",
|
|
14744
|
+
"category": "prioritization",
|
|
14745
|
+
"origin": {
|
|
14746
|
+
"type": "practitioner",
|
|
14747
|
+
"attribution": "Sean Ellis",
|
|
14748
|
+
"description": "Created by Sean Ellis as a lightweight growth experiment scoring method. Widely adopted in growth teams.",
|
|
14749
|
+
"year": 2010,
|
|
14750
|
+
"license": "open_attribution"
|
|
14751
|
+
},
|
|
14752
|
+
"tags": [
|
|
14753
|
+
"prioritization",
|
|
14754
|
+
"table"
|
|
14755
|
+
],
|
|
14756
|
+
"slots": [
|
|
14757
|
+
{
|
|
14758
|
+
"label": "Items to score",
|
|
14759
|
+
"entityTypeId": "feature",
|
|
14760
|
+
"description": "Features or experiments being evaluated"
|
|
14761
|
+
},
|
|
14762
|
+
{
|
|
14763
|
+
"label": "Impact",
|
|
14764
|
+
"entityTypeId": "outcome",
|
|
14765
|
+
"description": "How much will this move the needle?"
|
|
14766
|
+
},
|
|
14767
|
+
{
|
|
14768
|
+
"label": "Confidence",
|
|
14769
|
+
"entityTypeId": "assumption",
|
|
14770
|
+
"description": "How sure are we about the impact?"
|
|
14771
|
+
},
|
|
14772
|
+
{
|
|
14773
|
+
"label": "Ease",
|
|
14774
|
+
"entityTypeId": "feature",
|
|
14775
|
+
"description": "How easy is this to implement?"
|
|
14776
|
+
}
|
|
14777
|
+
],
|
|
14778
|
+
"data": {
|
|
14779
|
+
"entity_types": [
|
|
14780
|
+
{
|
|
14781
|
+
"type": "feature",
|
|
14782
|
+
"role": "scored_item"
|
|
14783
|
+
},
|
|
14784
|
+
{
|
|
14785
|
+
"type": "outcome",
|
|
14786
|
+
"role": "item"
|
|
14787
|
+
},
|
|
14788
|
+
{
|
|
14789
|
+
"type": "assumption",
|
|
14790
|
+
"role": "item"
|
|
14791
|
+
}
|
|
14792
|
+
],
|
|
14793
|
+
"required_properties": {
|
|
14794
|
+
"feature": [
|
|
14795
|
+
{
|
|
14796
|
+
"property": "impact",
|
|
14797
|
+
"type": "number",
|
|
14798
|
+
"required": true,
|
|
14799
|
+
"scope": "framework",
|
|
14800
|
+
"label": "Impact",
|
|
14801
|
+
"description": "Expected impact on the target metric (1-10)"
|
|
14802
|
+
},
|
|
14803
|
+
{
|
|
14804
|
+
"property": "confidence",
|
|
14805
|
+
"type": "number",
|
|
14806
|
+
"required": true,
|
|
14807
|
+
"scope": "framework",
|
|
14808
|
+
"label": "Confidence",
|
|
14809
|
+
"description": "Confidence in the impact estimate (1-10)"
|
|
14810
|
+
},
|
|
14811
|
+
{
|
|
14812
|
+
"property": "ease",
|
|
14813
|
+
"type": "number",
|
|
14814
|
+
"required": true,
|
|
14815
|
+
"scope": "framework",
|
|
14816
|
+
"label": "Ease",
|
|
14817
|
+
"description": "Ease of implementation (1-10)"
|
|
14818
|
+
}
|
|
14819
|
+
]
|
|
14820
|
+
},
|
|
14821
|
+
"computed_properties": [
|
|
14822
|
+
{
|
|
14823
|
+
"property": "ice_score",
|
|
14824
|
+
"expression": "impact * confidence * ease",
|
|
14825
|
+
"entity_type": "feature",
|
|
14826
|
+
"label": "ICE Score",
|
|
14827
|
+
"format": "number"
|
|
14828
|
+
}
|
|
14829
|
+
]
|
|
14830
|
+
},
|
|
14831
|
+
"structure": {
|
|
14832
|
+
"pattern": "table"
|
|
14833
|
+
},
|
|
14834
|
+
"presentation": {
|
|
14835
|
+
"layout": {
|
|
14836
|
+
"type": "table",
|
|
14837
|
+
"columns": [
|
|
14838
|
+
{
|
|
14839
|
+
"property": "title",
|
|
14840
|
+
"label": "Items to score",
|
|
14841
|
+
"sortable": true
|
|
14842
|
+
},
|
|
14843
|
+
{
|
|
14844
|
+
"property": "impact",
|
|
14845
|
+
"label": "Impact",
|
|
14846
|
+
"sortable": true
|
|
14847
|
+
},
|
|
14848
|
+
{
|
|
14849
|
+
"property": "confidence",
|
|
14850
|
+
"label": "Confidence",
|
|
14851
|
+
"sortable": true
|
|
14852
|
+
},
|
|
14853
|
+
{
|
|
14854
|
+
"property": "ease",
|
|
14855
|
+
"label": "Ease",
|
|
14856
|
+
"sortable": true
|
|
14857
|
+
}
|
|
14858
|
+
]
|
|
14859
|
+
},
|
|
14860
|
+
"sort_by": {
|
|
14861
|
+
"property": "title",
|
|
14862
|
+
"direction": "asc"
|
|
14863
|
+
},
|
|
14864
|
+
"colour_by": "type",
|
|
14865
|
+
"card_fields": [
|
|
14866
|
+
"title",
|
|
14867
|
+
"description",
|
|
14868
|
+
"status"
|
|
14869
|
+
]
|
|
14870
|
+
},
|
|
14871
|
+
"education": {
|
|
14872
|
+
"purpose": "Provide a lightweight scoring model for early-stage ideas when detailed effort estimates are unavailable. Faster than RICE and useful for brainstorm triage.",
|
|
14873
|
+
"core_question": "Which ideas should we investigate further based on their potential impact, confidence in our assumptions, and implementation ease?",
|
|
14874
|
+
"when_to_use": [
|
|
14875
|
+
"You have more ideas or features than capacity to build them",
|
|
14876
|
+
"Stakeholders disagree on what to build next",
|
|
14877
|
+
"You need a transparent, defensible prioritisation process"
|
|
14878
|
+
],
|
|
14879
|
+
"when_not_to_use": [
|
|
14880
|
+
"You have a single obvious next step with no contention",
|
|
14881
|
+
"The backlog is small enough to sequence intuitively"
|
|
14882
|
+
]
|
|
14883
|
+
}
|
|
14884
|
+
},
|
|
14885
|
+
{
|
|
14886
|
+
"id": "wsjf",
|
|
14887
|
+
"approach_ids": [
|
|
14888
|
+
"prioritise"
|
|
14889
|
+
],
|
|
14890
|
+
"name": "WSJF (Weighted Shortest Job First)",
|
|
14891
|
+
"version": "1.0.0",
|
|
14892
|
+
"description": "Prioritise work by dividing Cost of Delay (user value + time criticality + risk reduction) by job duration to maximise economic throughput.",
|
|
14893
|
+
"category": "planning",
|
|
14894
|
+
"origin": {
|
|
14895
|
+
"type": "practitioner",
|
|
14896
|
+
"attribution": "Reinertsen / SAFe",
|
|
14897
|
+
"description": "Developed by Don Reinertsen and adopted as a core practice in the Scaled Agile Framework (SAFe). Combines urgency (Cost of Delay) with job size to produce an economic prioritisation sequence.",
|
|
14898
|
+
"url": "https://www.scaledagileframework.com/wsjf/",
|
|
14899
|
+
"year": 2011,
|
|
14900
|
+
"license": "open_attribution"
|
|
14901
|
+
},
|
|
14902
|
+
"tags": [
|
|
14903
|
+
"planning",
|
|
14904
|
+
"table"
|
|
14905
|
+
],
|
|
14906
|
+
"slots": [
|
|
14907
|
+
{
|
|
14908
|
+
"label": "Backlog Items",
|
|
14909
|
+
"entityTypeId": "feature",
|
|
14910
|
+
"description": "Backlog Items: feature entries to evaluate"
|
|
14911
|
+
},
|
|
14912
|
+
{
|
|
14913
|
+
"label": "User/Business Value",
|
|
14914
|
+
"entityTypeId": "metric",
|
|
14915
|
+
"description": "User/Business Value: metric entries to evaluate"
|
|
14916
|
+
},
|
|
14917
|
+
{
|
|
14918
|
+
"label": "Time Criticality",
|
|
14919
|
+
"entityTypeId": "metric",
|
|
14920
|
+
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
14921
|
+
},
|
|
14922
|
+
{
|
|
14923
|
+
"label": "Risk Reduction / Opportunity Enablement",
|
|
14924
|
+
"entityTypeId": "metric",
|
|
14925
|
+
"description": "Risk Reduction / Opportunity Enablement: metric entries to evaluate"
|
|
14926
|
+
},
|
|
14927
|
+
{
|
|
14928
|
+
"label": "Job Size",
|
|
14929
|
+
"entityTypeId": "metric",
|
|
14930
|
+
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
14931
|
+
}
|
|
14932
|
+
],
|
|
14933
|
+
"data": {
|
|
14934
|
+
"entity_types": [
|
|
14935
|
+
{
|
|
14936
|
+
"type": "feature",
|
|
14937
|
+
"role": "scored_item"
|
|
14938
|
+
},
|
|
14939
|
+
{
|
|
14940
|
+
"type": "metric",
|
|
14941
|
+
"role": "item"
|
|
14942
|
+
}
|
|
14943
|
+
],
|
|
14944
|
+
"required_properties": {
|
|
14945
|
+
"feature": [
|
|
14946
|
+
{
|
|
14947
|
+
"property": "user_value",
|
|
14948
|
+
"type": "number",
|
|
14949
|
+
"required": true,
|
|
14950
|
+
"scope": "framework",
|
|
14951
|
+
"label": "User/Business Value",
|
|
14952
|
+
"description": "Relative value to users and the business if delivered"
|
|
14953
|
+
},
|
|
14954
|
+
{
|
|
14955
|
+
"property": "time_criticality",
|
|
14956
|
+
"type": "number",
|
|
14957
|
+
"required": true,
|
|
14958
|
+
"scope": "framework",
|
|
14959
|
+
"label": "Time Criticality",
|
|
14960
|
+
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
14961
|
+
},
|
|
14962
|
+
{
|
|
14963
|
+
"property": "risk_reduction",
|
|
14964
|
+
"type": "number",
|
|
14965
|
+
"required": true,
|
|
14966
|
+
"scope": "framework",
|
|
14967
|
+
"label": "Risk Reduction / Opportunity Enablement",
|
|
14968
|
+
"description": "Value from reducing risk or enabling future opportunities"
|
|
14969
|
+
},
|
|
14970
|
+
{
|
|
14971
|
+
"property": "job_size",
|
|
14972
|
+
"type": "number",
|
|
14973
|
+
"required": true,
|
|
14974
|
+
"scope": "framework",
|
|
14975
|
+
"label": "Job Size",
|
|
14976
|
+
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
14977
|
+
}
|
|
14978
|
+
]
|
|
14979
|
+
},
|
|
14980
|
+
"computed_properties": [
|
|
14981
|
+
{
|
|
14982
|
+
"property": "wsjf_score",
|
|
14983
|
+
"expression": "(user_value + time_criticality + risk_reduction) / job_size",
|
|
14984
|
+
"entity_type": "feature",
|
|
14985
|
+
"label": "WSJF Score",
|
|
14986
|
+
"format": "number"
|
|
14987
|
+
}
|
|
14988
|
+
]
|
|
14989
|
+
},
|
|
14990
|
+
"structure": {
|
|
14991
|
+
"pattern": "table"
|
|
14992
|
+
},
|
|
14993
|
+
"presentation": {
|
|
14994
|
+
"layout": {
|
|
14995
|
+
"type": "table",
|
|
14996
|
+
"columns": [
|
|
14997
|
+
{
|
|
14998
|
+
"property": "title",
|
|
14999
|
+
"label": "Backlog Items",
|
|
15000
|
+
"sortable": true
|
|
15001
|
+
},
|
|
15002
|
+
{
|
|
15003
|
+
"property": "user_value",
|
|
15004
|
+
"label": "User/Business Value",
|
|
15005
|
+
"sortable": true
|
|
15006
|
+
},
|
|
15007
|
+
{
|
|
15008
|
+
"property": "time_criticality",
|
|
15009
|
+
"label": "Time Criticality",
|
|
15010
|
+
"sortable": true
|
|
15011
|
+
},
|
|
15012
|
+
{
|
|
15013
|
+
"property": "risk_reduction",
|
|
15014
|
+
"label": "Risk Reduction / Opportunity Enablement",
|
|
15015
|
+
"sortable": true
|
|
15016
|
+
},
|
|
15017
|
+
{
|
|
15018
|
+
"property": "job_size",
|
|
15019
|
+
"label": "Job Size",
|
|
15020
|
+
"sortable": true
|
|
15021
|
+
},
|
|
15022
|
+
{
|
|
15023
|
+
"property": "wsjf_score",
|
|
15024
|
+
"label": "WSJF Score",
|
|
15025
|
+
"sortable": true
|
|
15026
|
+
}
|
|
15027
|
+
]
|
|
15028
|
+
},
|
|
15029
|
+
"sort_by": {
|
|
15030
|
+
"property": "title",
|
|
15031
|
+
"direction": "asc"
|
|
15032
|
+
},
|
|
15033
|
+
"colour_by": "type",
|
|
15034
|
+
"card_fields": [
|
|
15035
|
+
"title",
|
|
15036
|
+
"description",
|
|
15037
|
+
"status"
|
|
15038
|
+
]
|
|
15039
|
+
},
|
|
15040
|
+
"education": {
|
|
15041
|
+
"purpose": "Prioritise work by dividing the Cost of Delay by job duration, ensuring the most time-sensitive, valuable items are done first.",
|
|
15042
|
+
"core_question": "Considering the cost of waiting, which items should we start now to maximise economic benefit?",
|
|
15043
|
+
"when_to_use": [
|
|
15044
|
+
"You need to coordinate work across multiple teams or time horizons",
|
|
15045
|
+
"Stakeholders need visibility into what is coming and when",
|
|
15046
|
+
"You want to balance commitments with flexibility"
|
|
15047
|
+
],
|
|
15048
|
+
"when_not_to_use": [
|
|
15049
|
+
"The team is small enough that informal coordination works",
|
|
15050
|
+
"Plans would create false precision about uncertain outcomes"
|
|
15051
|
+
]
|
|
15052
|
+
}
|
|
15053
|
+
},
|
|
15054
|
+
{
|
|
15055
|
+
"id": "cost-of-delay",
|
|
15056
|
+
"approach_ids": [
|
|
15057
|
+
"prioritise"
|
|
15058
|
+
],
|
|
15059
|
+
"name": "Cost of Delay",
|
|
15060
|
+
"version": "1.0.0",
|
|
15061
|
+
"description": "Quantify the economic cost of not shipping a feature to drive priority decisions. Combines urgency with value.",
|
|
15062
|
+
"category": "prioritization",
|
|
15063
|
+
"origin": {
|
|
15064
|
+
"type": "practitioner",
|
|
15065
|
+
"attribution": "Don Reinertsen",
|
|
15066
|
+
"description": "Formalised in The Principles of Product Development Flow (Celeritas Publishing). Foundational to lean product economics.",
|
|
15067
|
+
"year": 2009,
|
|
15068
|
+
"license": "public_domain"
|
|
15069
|
+
},
|
|
15070
|
+
"tags": [
|
|
15071
|
+
"prioritization",
|
|
15072
|
+
"table"
|
|
15073
|
+
],
|
|
15074
|
+
"slots": [
|
|
15075
|
+
{
|
|
15076
|
+
"label": "Items to evaluate",
|
|
15077
|
+
"entityTypeId": "feature",
|
|
15078
|
+
"description": "Features or initiatives being assessed"
|
|
15079
|
+
},
|
|
15080
|
+
{
|
|
15081
|
+
"label": "User-Business Value",
|
|
15082
|
+
"entityTypeId": "outcome",
|
|
15083
|
+
"description": "Revenue, retention, or strategic value"
|
|
15084
|
+
},
|
|
15085
|
+
{
|
|
15086
|
+
"label": "Time Criticality",
|
|
15087
|
+
"entityTypeId": "metric",
|
|
15088
|
+
"description": "How much value decays with delay"
|
|
15089
|
+
},
|
|
15090
|
+
{
|
|
15091
|
+
"label": "Risk Reduction",
|
|
15092
|
+
"entityTypeId": "risk",
|
|
15093
|
+
"description": "What risk does this mitigate?"
|
|
15094
|
+
}
|
|
15095
|
+
],
|
|
15096
|
+
"data": {
|
|
15097
|
+
"entity_types": [
|
|
15098
|
+
{
|
|
15099
|
+
"type": "feature",
|
|
15100
|
+
"role": "scored_item"
|
|
15101
|
+
},
|
|
15102
|
+
{
|
|
15103
|
+
"type": "metric",
|
|
15104
|
+
"role": "item"
|
|
15105
|
+
},
|
|
15106
|
+
{
|
|
15107
|
+
"type": "outcome",
|
|
15108
|
+
"role": "item"
|
|
15109
|
+
},
|
|
15110
|
+
{
|
|
15111
|
+
"type": "risk",
|
|
15112
|
+
"role": "item"
|
|
15113
|
+
}
|
|
15114
|
+
],
|
|
15115
|
+
"required_properties": {
|
|
15116
|
+
"feature": [
|
|
15117
|
+
{
|
|
15118
|
+
"property": "cost_of_delay",
|
|
15119
|
+
"type": "number",
|
|
15120
|
+
"required": true,
|
|
15121
|
+
"scope": "framework",
|
|
15122
|
+
"label": "Cost of Delay",
|
|
15123
|
+
"description": "Weekly revenue impact of not shipping"
|
|
15124
|
+
},
|
|
15125
|
+
{
|
|
15126
|
+
"property": "job_size",
|
|
15127
|
+
"type": "number",
|
|
15128
|
+
"required": true,
|
|
15129
|
+
"scope": "framework",
|
|
15130
|
+
"label": "Job Size",
|
|
15131
|
+
"description": "Weeks of development effort"
|
|
15132
|
+
}
|
|
15133
|
+
]
|
|
15134
|
+
},
|
|
15135
|
+
"computed_properties": [
|
|
15136
|
+
{
|
|
15137
|
+
"property": "wsjf_score",
|
|
15138
|
+
"expression": "cost_of_delay / job_size",
|
|
15139
|
+
"entity_type": "feature",
|
|
15140
|
+
"label": "WSJF Score",
|
|
15141
|
+
"format": "number"
|
|
15142
|
+
}
|
|
15143
|
+
]
|
|
15144
|
+
},
|
|
15145
|
+
"structure": {
|
|
15146
|
+
"pattern": "table"
|
|
15147
|
+
},
|
|
15148
|
+
"presentation": {
|
|
15149
|
+
"layout": {
|
|
15150
|
+
"type": "table",
|
|
15151
|
+
"columns": [
|
|
15152
|
+
{
|
|
15153
|
+
"property": "title",
|
|
15154
|
+
"label": "Items to evaluate",
|
|
15155
|
+
"sortable": true
|
|
15156
|
+
},
|
|
15157
|
+
{
|
|
15158
|
+
"property": "cost_of_delay",
|
|
15159
|
+
"label": "User-Business Value",
|
|
15160
|
+
"sortable": true
|
|
15161
|
+
},
|
|
15162
|
+
{
|
|
15163
|
+
"property": "job_size",
|
|
15164
|
+
"label": "Job Size",
|
|
15165
|
+
"sortable": true
|
|
15166
|
+
},
|
|
15167
|
+
{
|
|
15168
|
+
"property": "wsjf_score",
|
|
15169
|
+
"label": "CoD Score",
|
|
15170
|
+
"sortable": true
|
|
15171
|
+
}
|
|
15172
|
+
]
|
|
15173
|
+
},
|
|
15174
|
+
"sort_by": {
|
|
15175
|
+
"property": "title",
|
|
15176
|
+
"direction": "asc"
|
|
15177
|
+
},
|
|
15178
|
+
"colour_by": "type",
|
|
15179
|
+
"card_fields": [
|
|
15180
|
+
"title",
|
|
15181
|
+
"description",
|
|
15182
|
+
"status"
|
|
15183
|
+
]
|
|
15184
|
+
},
|
|
15185
|
+
"education": {
|
|
15186
|
+
"purpose": "Quantify the economic impact of not delivering a feature by a given date, making urgency visible and enabling time-sensitive prioritisation.",
|
|
15187
|
+
"core_question": "How much value are we losing every week this feature is not in production, and does that urgency justify fast-tracking it?",
|
|
15188
|
+
"when_to_use": [
|
|
15189
|
+
"You have more ideas or features than capacity to build them",
|
|
15190
|
+
"Stakeholders disagree on what to build next",
|
|
15191
|
+
"You need a transparent, defensible prioritisation process"
|
|
15192
|
+
],
|
|
15193
|
+
"when_not_to_use": [
|
|
15194
|
+
"You have a single obvious next step with no contention",
|
|
15195
|
+
"The backlog is small enough to sequence intuitively"
|
|
15196
|
+
]
|
|
15197
|
+
}
|
|
15198
|
+
},
|
|
15199
|
+
{
|
|
15200
|
+
"id": "five-whys",
|
|
15201
|
+
"approach_ids": [
|
|
15202
|
+
"reflect",
|
|
15203
|
+
"inspect"
|
|
15204
|
+
],
|
|
15205
|
+
"name": "Five Whys",
|
|
15206
|
+
"version": "1.0.0",
|
|
15207
|
+
"description": 'Iteratively ask "why?", typically five times, starting from a symptom; each answer becomes the subject of the next question. The chain of answers reveals the underlying root cause behind the surface problem.',
|
|
15208
|
+
"category": "team_process",
|
|
15209
|
+
"origin": {
|
|
15210
|
+
"type": "practitioner",
|
|
15211
|
+
"attribution": "Sakichi Toyoda / Toyota Production System",
|
|
15212
|
+
"description": "Developed within the Toyota Production System as a root-cause analysis technique. Popularised through Lean and Six Sigma practice; now a widely used incident-review and design-debug staple.",
|
|
15213
|
+
"year": 1930,
|
|
15214
|
+
"license": "public_domain"
|
|
15215
|
+
},
|
|
15216
|
+
"tags": [
|
|
15217
|
+
"team_process",
|
|
15218
|
+
"reflection",
|
|
15219
|
+
"root_cause",
|
|
15220
|
+
"tree"
|
|
15221
|
+
],
|
|
15222
|
+
"slots": [
|
|
15223
|
+
{
|
|
15224
|
+
"label": "Symptom",
|
|
15225
|
+
"entityTypeId": "need",
|
|
15226
|
+
"description": "The observed problem the analysis starts from."
|
|
15227
|
+
},
|
|
15228
|
+
{
|
|
15229
|
+
"label": "Why chain",
|
|
15230
|
+
"entityTypeId": "insight",
|
|
15231
|
+
"description": 'Each "why?" answer along the chain, typically five iterations deep.'
|
|
15232
|
+
},
|
|
15233
|
+
{
|
|
15234
|
+
"label": "Root cause",
|
|
15235
|
+
"entityTypeId": "insight",
|
|
15236
|
+
"description": "The terminal answer at the bottom of the chain: the underlying cause to address."
|
|
15237
|
+
}
|
|
15238
|
+
],
|
|
15239
|
+
"data": {
|
|
15240
|
+
"entity_types": [
|
|
15241
|
+
{
|
|
15242
|
+
"type": "need",
|
|
15243
|
+
"role": "root"
|
|
15244
|
+
},
|
|
15245
|
+
{
|
|
15246
|
+
"type": "insight",
|
|
15247
|
+
"role": "branch"
|
|
15248
|
+
}
|
|
15249
|
+
],
|
|
15250
|
+
"required_properties": {}
|
|
15251
|
+
},
|
|
15252
|
+
"structure": {
|
|
15253
|
+
"pattern": "tree"
|
|
15254
|
+
},
|
|
15255
|
+
"presentation": {
|
|
15256
|
+
"layout": {
|
|
15257
|
+
"type": "tree",
|
|
15258
|
+
"direction": "TB"
|
|
15259
|
+
},
|
|
15260
|
+
"sort_by": {
|
|
15261
|
+
"property": "title",
|
|
15262
|
+
"direction": "asc"
|
|
15263
|
+
},
|
|
15264
|
+
"card_fields": [
|
|
15265
|
+
"title",
|
|
15266
|
+
"description"
|
|
15267
|
+
]
|
|
15268
|
+
},
|
|
15269
|
+
"education": {
|
|
15270
|
+
"purpose": 'Move past surface symptoms by chaining "why?" questions until the underlying root cause surfaces, so fixes target the real driver rather than a downstream effect.',
|
|
15271
|
+
"core_question": "Why is this happening, and why is THAT happening, until we reach a cause we can act on?",
|
|
15272
|
+
"when_to_use": [
|
|
15273
|
+
"A problem keeps recurring after surface fixes",
|
|
15274
|
+
"Post-incident review where the obvious cause feels too obvious",
|
|
15275
|
+
"Designing a fix and you want to confirm you understand the actual driver"
|
|
15276
|
+
],
|
|
15277
|
+
"when_not_to_use": [
|
|
15278
|
+
"The problem has multiple independent root causes (use a fishbone or richer RCA tool)",
|
|
15279
|
+
"You need quantitative attribution rather than a single-thread narrative",
|
|
15280
|
+
'Five linear "whys" oversimplify a systems problem with feedback loops'
|
|
15281
|
+
]
|
|
15282
|
+
}
|
|
15283
|
+
},
|
|
15284
|
+
{
|
|
15285
|
+
"id": "pre-mortem",
|
|
15286
|
+
"approach_ids": [
|
|
15287
|
+
"reflect"
|
|
15288
|
+
],
|
|
15289
|
+
"name": "Pre-mortem",
|
|
15290
|
+
"version": "1.0.0",
|
|
15291
|
+
"description": "Imagine the project has already failed; work backward listing the plausible causes of the failure. Produce a risk register and matching mitigations before the work starts.",
|
|
15292
|
+
"category": "team_process",
|
|
15293
|
+
"origin": {
|
|
15294
|
+
"type": "practitioner",
|
|
15295
|
+
"attribution": "Gary Klein",
|
|
15296
|
+
"description": "Popularised by Gary Klein in Harvard Business Review (2007) as a prospective-hindsight technique. Inverts the post-mortem: imagine failure first, then list causes, while there is still time to act.",
|
|
15297
|
+
"url": "https://hbr.org/2007/09/performing-a-project-premortem",
|
|
15298
|
+
"year": 2007,
|
|
15299
|
+
"license": "published_methodology"
|
|
15300
|
+
},
|
|
15301
|
+
"tags": [
|
|
15302
|
+
"team_process",
|
|
15303
|
+
"reflection",
|
|
15304
|
+
"risk",
|
|
15305
|
+
"collection"
|
|
15306
|
+
],
|
|
15307
|
+
"slots": [
|
|
15308
|
+
{
|
|
15309
|
+
"label": "Imagined failures",
|
|
15310
|
+
"entityTypeId": "risk",
|
|
15311
|
+
"description": "Plausible failure modes named as if they had already occurred."
|
|
15312
|
+
},
|
|
15313
|
+
{
|
|
15314
|
+
"label": "Causes",
|
|
15315
|
+
"entityTypeId": "insight",
|
|
15316
|
+
"description": "For each imagined failure, the contributing causes the team can foresee."
|
|
15317
|
+
},
|
|
15318
|
+
{
|
|
15319
|
+
"label": "Mitigations",
|
|
15320
|
+
"entityTypeId": "initiative",
|
|
15321
|
+
"description": "Mitigation actions the team will take before failure can occur."
|
|
15322
|
+
}
|
|
15323
|
+
],
|
|
15324
|
+
"data": {
|
|
15325
|
+
"entity_types": [
|
|
15326
|
+
{
|
|
15327
|
+
"type": "risk",
|
|
15328
|
+
"role": "bucket"
|
|
15329
|
+
},
|
|
15330
|
+
{
|
|
15331
|
+
"type": "insight",
|
|
15332
|
+
"role": "bucket"
|
|
15333
|
+
},
|
|
15334
|
+
{
|
|
15335
|
+
"type": "initiative",
|
|
15336
|
+
"role": "bucket"
|
|
15337
|
+
}
|
|
15338
|
+
],
|
|
15339
|
+
"required_properties": {}
|
|
15340
|
+
},
|
|
15341
|
+
"structure": {
|
|
15342
|
+
"pattern": "collection"
|
|
15343
|
+
},
|
|
15344
|
+
"presentation": {
|
|
15345
|
+
"layout": {
|
|
15346
|
+
"type": "grid",
|
|
15347
|
+
"groupBy": "type"
|
|
15348
|
+
},
|
|
15349
|
+
"sort_by": {
|
|
15350
|
+
"property": "title",
|
|
15351
|
+
"direction": "asc"
|
|
15352
|
+
},
|
|
15353
|
+
"colour_by": "group",
|
|
15354
|
+
"card_fields": [
|
|
15355
|
+
"title",
|
|
15356
|
+
"description"
|
|
15357
|
+
]
|
|
15358
|
+
},
|
|
15359
|
+
"education": {
|
|
15360
|
+
"purpose": "Surface project risks early by inverting hindsight: imagine the project has already failed and ask why, while there is still time to mitigate.",
|
|
15361
|
+
"core_question": "It is six months from now and the project has failed catastrophically. What happened, and why?",
|
|
15362
|
+
"when_to_use": [
|
|
15363
|
+
"Kicking off a project with significant downside or irreversible commitment",
|
|
15364
|
+
"A plan looks too clean and the team senses unspoken concerns",
|
|
15365
|
+
"Stakeholders disagree on risk; the exercise externalises and ranks them"
|
|
15366
|
+
],
|
|
15367
|
+
"when_not_to_use": [
|
|
15368
|
+
"The work is small, reversible, and cheap to course-correct",
|
|
15369
|
+
"The team is in execution mode and reflective ceremonies will derail momentum",
|
|
15370
|
+
"Risk surfacing has become performative: the team names risks but never mitigates them"
|
|
15371
|
+
]
|
|
15372
|
+
}
|
|
15373
|
+
},
|
|
15374
|
+
{
|
|
15375
|
+
"id": "red-team",
|
|
15376
|
+
"approach_ids": [
|
|
15377
|
+
"reflect",
|
|
15378
|
+
"inspect"
|
|
15379
|
+
],
|
|
15380
|
+
"name": "Red Team",
|
|
15381
|
+
"version": "1.0.0",
|
|
15382
|
+
"description": "Structured adversarial review. A designated group is assigned to attack a plan, design, or proposal from an outside-in stance, surfacing weaknesses the inside-out builders cannot see.",
|
|
15383
|
+
"category": "team_process",
|
|
15384
|
+
"origin": {
|
|
15385
|
+
"type": "practitioner",
|
|
15386
|
+
"attribution": "US Department of Defense (Cold War era); broadened by security and intelligence practice",
|
|
15387
|
+
"description": `Originated in Cold War-era military strategic exercises ("red" team takes the adversary role against the "blue" team's defence). Adopted by cybersecurity, intelligence analysis, and product teams as a structured contrarian-review practice.`,
|
|
15388
|
+
"year": 1960,
|
|
15389
|
+
"license": "public_domain"
|
|
15390
|
+
},
|
|
15391
|
+
"tags": [
|
|
15392
|
+
"team_process",
|
|
15393
|
+
"reflection",
|
|
15394
|
+
"adversarial",
|
|
15395
|
+
"collection"
|
|
15396
|
+
],
|
|
15397
|
+
"slots": [
|
|
15398
|
+
{
|
|
15399
|
+
"label": "Target",
|
|
15400
|
+
"entityTypeId": "initiative",
|
|
15401
|
+
"description": "The plan, design, or proposal under adversarial review."
|
|
15402
|
+
},
|
|
15403
|
+
{
|
|
15404
|
+
"label": "Attack vectors",
|
|
15405
|
+
"entityTypeId": "risk",
|
|
15406
|
+
"description": "The angles the red team uses to probe weaknesses."
|
|
15407
|
+
},
|
|
15408
|
+
{
|
|
15409
|
+
"label": "Findings",
|
|
15410
|
+
"entityTypeId": "insight",
|
|
15411
|
+
"description": "Weaknesses, blind spots, or unstated assumptions surfaced by the review."
|
|
15412
|
+
}
|
|
15413
|
+
],
|
|
15414
|
+
"data": {
|
|
15415
|
+
"entity_types": [
|
|
15416
|
+
{
|
|
15417
|
+
"type": "initiative",
|
|
15418
|
+
"role": "root"
|
|
15419
|
+
},
|
|
15420
|
+
{
|
|
15421
|
+
"type": "risk",
|
|
15422
|
+
"role": "bucket"
|
|
15423
|
+
},
|
|
15424
|
+
{
|
|
15425
|
+
"type": "insight",
|
|
15426
|
+
"role": "bucket"
|
|
15427
|
+
}
|
|
15428
|
+
],
|
|
15429
|
+
"required_properties": {}
|
|
15430
|
+
},
|
|
15431
|
+
"structure": {
|
|
15432
|
+
"pattern": "collection"
|
|
15433
|
+
},
|
|
15434
|
+
"presentation": {
|
|
15435
|
+
"layout": {
|
|
15436
|
+
"type": "grid",
|
|
15437
|
+
"groupBy": "type"
|
|
15438
|
+
},
|
|
15439
|
+
"sort_by": {
|
|
15440
|
+
"property": "title",
|
|
15441
|
+
"direction": "asc"
|
|
15442
|
+
},
|
|
15443
|
+
"colour_by": "group",
|
|
15444
|
+
"card_fields": [
|
|
15445
|
+
"title",
|
|
15446
|
+
"description"
|
|
15447
|
+
]
|
|
15448
|
+
},
|
|
15449
|
+
"education": {
|
|
15450
|
+
"purpose": "Stress-test a plan against an explicit adversary by assigning reviewers to attack rather than agree, so weaknesses surface before reality finds them.",
|
|
15451
|
+
"core_question": "If a competent adversary wanted this to fail, where would they push first, and would we hold?",
|
|
15452
|
+
"when_to_use": [
|
|
15453
|
+
"A high-stakes decision, launch, or security posture needs hardening",
|
|
15454
|
+
"Inside-out thinking is dominant and dissent has gone quiet",
|
|
15455
|
+
"Risk register is suspiciously short for the size of the bet"
|
|
15456
|
+
],
|
|
15457
|
+
"when_not_to_use": [
|
|
15458
|
+
"Early-stage exploration where adversarial framing would crush a fragile idea prematurely",
|
|
15459
|
+
"Team trust is too low: red-teaming will read as personal attack rather than role-play",
|
|
15460
|
+
"The work is small enough that a lightweight devil's-advocate pass is sufficient"
|
|
15461
|
+
]
|
|
15462
|
+
}
|
|
15463
|
+
},
|
|
15464
|
+
{
|
|
15465
|
+
"id": "devils-advocate",
|
|
15466
|
+
"approach_ids": [
|
|
15467
|
+
"reflect"
|
|
15468
|
+
],
|
|
15469
|
+
"name": "Devil's Advocate",
|
|
15470
|
+
"version": "1.0.0",
|
|
15471
|
+
"description": "Designate one reviewer to formally take the opposing position regardless of personal view. The assigned-role contrarian defangs groupthink by making dissent legitimate and structured.",
|
|
15472
|
+
"category": "team_process",
|
|
15473
|
+
"origin": {
|
|
15474
|
+
"type": "practitioner",
|
|
15475
|
+
"attribution": "Roman Catholic Church (advocatus diaboli); broadened by decision-quality practice",
|
|
15476
|
+
"description": "Originated in 16th-century canonisation proceedings as the advocatus diaboli, an official assigned to argue against canonising a candidate. Adopted by decision-science and product-team practice as a structured antidote to groupthink.",
|
|
15477
|
+
"year": 1587,
|
|
15478
|
+
"license": "public_domain"
|
|
15479
|
+
},
|
|
15480
|
+
"tags": [
|
|
15481
|
+
"team_process",
|
|
15482
|
+
"reflection",
|
|
15483
|
+
"decision_quality",
|
|
15484
|
+
"collection"
|
|
15485
|
+
],
|
|
15486
|
+
"slots": [
|
|
15487
|
+
{
|
|
15488
|
+
"label": "Proposal",
|
|
15489
|
+
"entityTypeId": "initiative",
|
|
15490
|
+
"description": "The plan or recommendation under consideration."
|
|
15491
|
+
},
|
|
15492
|
+
{
|
|
15493
|
+
"label": "Opposing arguments",
|
|
15494
|
+
"entityTypeId": "insight",
|
|
15495
|
+
"description": "The case against the proposal, voiced by the assigned contrarian regardless of personal view."
|
|
15496
|
+
},
|
|
15497
|
+
{
|
|
15498
|
+
"label": "Counter-evidence",
|
|
15499
|
+
"entityTypeId": "evidence",
|
|
15500
|
+
"description": "Data points the contrarian raises that the proposal does not yet account for."
|
|
15501
|
+
}
|
|
15502
|
+
],
|
|
15503
|
+
"data": {
|
|
15504
|
+
"entity_types": [
|
|
15505
|
+
{
|
|
15506
|
+
"type": "initiative",
|
|
15507
|
+
"role": "root"
|
|
15508
|
+
},
|
|
15509
|
+
{
|
|
15510
|
+
"type": "insight",
|
|
15511
|
+
"role": "bucket"
|
|
15512
|
+
},
|
|
15513
|
+
{
|
|
15514
|
+
"type": "evidence",
|
|
15515
|
+
"role": "bucket"
|
|
15516
|
+
}
|
|
15517
|
+
],
|
|
15518
|
+
"required_properties": {}
|
|
15519
|
+
},
|
|
15520
|
+
"structure": {
|
|
15521
|
+
"pattern": "collection"
|
|
15522
|
+
},
|
|
15523
|
+
"presentation": {
|
|
15524
|
+
"layout": {
|
|
15525
|
+
"type": "grid",
|
|
15526
|
+
"groupBy": "type"
|
|
15527
|
+
},
|
|
15528
|
+
"sort_by": {
|
|
15529
|
+
"property": "title",
|
|
15530
|
+
"direction": "asc"
|
|
15531
|
+
},
|
|
15532
|
+
"colour_by": "group",
|
|
15533
|
+
"card_fields": [
|
|
15534
|
+
"title",
|
|
15535
|
+
"description"
|
|
15536
|
+
]
|
|
15537
|
+
},
|
|
15538
|
+
"education": {
|
|
15539
|
+
"purpose": "Make dissent a legitimate role rather than a personal stance. Assigning one reviewer to argue against the proposal forces the team to confront the strongest counter-case.",
|
|
15540
|
+
"core_question": "If we had to argue against this proposal (not because we believe it, but because the role demands it) what is the strongest case?",
|
|
15541
|
+
"when_to_use": [
|
|
15542
|
+
"A decision is heading toward consensus and you suspect groupthink",
|
|
15543
|
+
"Stakes are high and the team has not heard a serious counter-argument",
|
|
15544
|
+
"Cultural norms make raw dissent costly; assigning the role lowers the social cost"
|
|
15545
|
+
],
|
|
15546
|
+
"when_not_to_use": [
|
|
15547
|
+
"Genuine disagreement already exists in the room (let it surface; do not theatricalise it)",
|
|
15548
|
+
"The decision is small enough that the ceremony costs more than the insight returned",
|
|
15549
|
+
"The assigned contrarian will be punished socially for the role; set the norms first or skip"
|
|
15550
|
+
]
|
|
15551
|
+
}
|
|
15552
|
+
},
|
|
15553
|
+
{
|
|
15554
|
+
"id": "second-order-thinking",
|
|
15555
|
+
"approach_ids": [
|
|
15556
|
+
"reflect"
|
|
15557
|
+
],
|
|
15558
|
+
"name": "Second-order Thinking",
|
|
15559
|
+
"version": "1.0.0",
|
|
15560
|
+
"description": 'After deciding a move, ask "and then what?" repeatedly. Trace second-, third-, and higher-order consequences to surface downstream effects that first-order reasoning misses.',
|
|
15561
|
+
"category": "team_process",
|
|
15562
|
+
"origin": {
|
|
15563
|
+
"type": "practitioner",
|
|
15564
|
+
"attribution": "Howard Marks / Charlie Munger",
|
|
15565
|
+
"description": `Howard Marks distinguished first-order vs second-order thinking in The Most Important Thing (2011) as the essential discipline of consequential decision-making. Charlie Munger's "and then what?" framing is the practical heuristic.`,
|
|
15566
|
+
"url": "https://www.oaktreecapital.com/insights/memo/dare-to-be-great-ii",
|
|
15567
|
+
"year": 2011,
|
|
15568
|
+
"license": "published_methodology"
|
|
15569
|
+
},
|
|
15570
|
+
"tags": [
|
|
15571
|
+
"team_process",
|
|
15572
|
+
"reflection",
|
|
15573
|
+
"consequences",
|
|
15574
|
+
"tree"
|
|
15575
|
+
],
|
|
15576
|
+
"slots": [
|
|
15577
|
+
{
|
|
15578
|
+
"label": "First-order move",
|
|
15579
|
+
"entityTypeId": "decision",
|
|
15580
|
+
"description": "The decision or move under consideration."
|
|
15581
|
+
},
|
|
15582
|
+
{
|
|
15583
|
+
"label": "Second-order consequences",
|
|
15584
|
+
"entityTypeId": "insight",
|
|
15585
|
+
"description": "Downstream effects that follow from the first-order move."
|
|
15586
|
+
},
|
|
15587
|
+
{
|
|
15588
|
+
"label": "Higher-order consequences",
|
|
15589
|
+
"entityTypeId": "insight",
|
|
15590
|
+
"description": "Third-, fourth-, fifth-order ripples: second-order consequences of the second-order consequences."
|
|
15591
|
+
}
|
|
15592
|
+
],
|
|
15593
|
+
"data": {
|
|
15594
|
+
"entity_types": [
|
|
15595
|
+
{
|
|
15596
|
+
"type": "decision",
|
|
15597
|
+
"role": "root"
|
|
15598
|
+
},
|
|
15599
|
+
{
|
|
15600
|
+
"type": "insight",
|
|
15601
|
+
"role": "branch"
|
|
15602
|
+
}
|
|
15603
|
+
],
|
|
15604
|
+
"required_properties": {}
|
|
15605
|
+
},
|
|
15606
|
+
"structure": {
|
|
15607
|
+
"pattern": "tree"
|
|
15608
|
+
},
|
|
15609
|
+
"presentation": {
|
|
15610
|
+
"layout": {
|
|
15611
|
+
"type": "tree",
|
|
15612
|
+
"direction": "TB"
|
|
15613
|
+
},
|
|
15614
|
+
"sort_by": {
|
|
15615
|
+
"property": "title",
|
|
15616
|
+
"direction": "asc"
|
|
15617
|
+
},
|
|
15618
|
+
"card_fields": [
|
|
15619
|
+
"title",
|
|
15620
|
+
"description"
|
|
15621
|
+
]
|
|
15622
|
+
},
|
|
15623
|
+
"education": {
|
|
15624
|
+
"purpose": 'Resist first-order reasoning by chaining "and then what?" until non-obvious downstream consequences come into view.',
|
|
15625
|
+
"core_question": "If we make this move and it works, what does the world look like next, and is that the world we want?",
|
|
15626
|
+
"when_to_use": [
|
|
15627
|
+
"A decision has feedback loops, market reactions, or behavioural ripples",
|
|
15628
|
+
"The first-order case is compelling, which is exactly when downstream effects bite",
|
|
15629
|
+
"Considering an irreversible or large-scale commitment"
|
|
15630
|
+
],
|
|
15631
|
+
"when_not_to_use": [
|
|
15632
|
+
"Routine, reversible, low-blast-radius decisions where deliberation costs more than mistakes",
|
|
15633
|
+
"Higher-order branches diverge into pure speculation with no anchor in evidence",
|
|
15634
|
+
"Time pressure makes a deeper trace expensive and the first-order call is good enough"
|
|
15635
|
+
]
|
|
15636
|
+
}
|
|
15637
|
+
}
|
|
15638
|
+
];
|
|
15639
|
+
var UPG_FRAMEWORKS_BY_ID = Object.fromEntries(
|
|
15640
|
+
UPG_FRAMEWORKS.map((fw) => [fw.id, fw])
|
|
15641
|
+
);
|
|
15642
|
+
var UPG_FRAMEWORKS_BY_CATEGORY = {};
|
|
15643
|
+
for (const fw of UPG_FRAMEWORKS) {
|
|
15644
|
+
if (!UPG_FRAMEWORKS_BY_CATEGORY[fw.category]) UPG_FRAMEWORKS_BY_CATEGORY[fw.category] = [];
|
|
15645
|
+
UPG_FRAMEWORKS_BY_CATEGORY[fw.category].push(fw);
|
|
15646
|
+
}
|
|
15647
|
+
var PRIORITY_LABELS = [
|
|
15648
|
+
// ── need (CONSOLIDATED: replaces pain_point + user_need) ─────────────────────
|
|
15649
|
+
{
|
|
15650
|
+
id: "need",
|
|
15651
|
+
canonical_label: "Need",
|
|
15652
|
+
alt_labels: [
|
|
15653
|
+
"pain point",
|
|
15654
|
+
"pain",
|
|
15655
|
+
"user need",
|
|
15656
|
+
"customer need",
|
|
15657
|
+
"problem",
|
|
15658
|
+
"struggle",
|
|
15659
|
+
"customer pain",
|
|
15660
|
+
"frustration",
|
|
15661
|
+
"gap",
|
|
15662
|
+
"unmet need",
|
|
15663
|
+
"user problem"
|
|
15664
|
+
],
|
|
15665
|
+
framework_labels: {
|
|
15666
|
+
lean_canvas: "Problem",
|
|
15667
|
+
design_thinking: "Pain Point",
|
|
15668
|
+
ost: "Opportunity (need)",
|
|
15669
|
+
jtbd: "Struggle",
|
|
15670
|
+
vpc: "Customer Pain"
|
|
15671
|
+
},
|
|
15672
|
+
designations: {
|
|
15673
|
+
pain: "Pain Point",
|
|
15674
|
+
gap: "Need",
|
|
15675
|
+
desire: "Desire",
|
|
15676
|
+
constraint: "Constraint"
|
|
15677
|
+
}
|
|
15678
|
+
},
|
|
15679
|
+
// ── opportunity ──────────────────────────────────────────────────────────────
|
|
15680
|
+
{
|
|
15681
|
+
id: "opportunity",
|
|
15682
|
+
canonical_label: "Opportunity",
|
|
15683
|
+
alt_labels: ["product opportunity", "market opportunity", "user opportunity"],
|
|
15684
|
+
framework_labels: {
|
|
15685
|
+
ost: "Opportunity"
|
|
15686
|
+
}
|
|
15687
|
+
},
|
|
15688
|
+
// ── solution ─────────────────────────────────────────────────────────────────
|
|
15689
|
+
{
|
|
15690
|
+
id: "solution",
|
|
15691
|
+
canonical_label: "Solution",
|
|
15692
|
+
alt_labels: ["proposed solution", "solution idea", "concept", "approach"],
|
|
15693
|
+
framework_labels: {
|
|
15694
|
+
ost: "Solution",
|
|
15695
|
+
design_thinking: "Solution",
|
|
15696
|
+
lean_canvas: "Solution",
|
|
15697
|
+
rice: "Scored Solution"
|
|
15698
|
+
}
|
|
15699
|
+
},
|
|
15700
|
+
// ── experiment (CONSOLIDATED: absorbs ab_test, growth_experiment, pricing_experiment) ──
|
|
15701
|
+
{
|
|
15702
|
+
id: "experiment",
|
|
15703
|
+
canonical_label: "Experiment",
|
|
15704
|
+
alt_labels: [
|
|
15705
|
+
"test",
|
|
15706
|
+
"validation",
|
|
15707
|
+
"ab test",
|
|
15708
|
+
"a/b test",
|
|
15709
|
+
"split test",
|
|
15710
|
+
"growth experiment",
|
|
15711
|
+
"pricing experiment",
|
|
15712
|
+
"usability test",
|
|
15713
|
+
"discovery experiment"
|
|
15714
|
+
],
|
|
15715
|
+
framework_labels: {
|
|
15716
|
+
ost: "Experiment",
|
|
15717
|
+
design_thinking: "Test",
|
|
15718
|
+
lean_startup: "Experiment"
|
|
15719
|
+
},
|
|
15720
|
+
designations: {
|
|
14754
15721
|
discovery: "Discovery Experiment",
|
|
14755
15722
|
ab_test: "A/B Test",
|
|
14756
15723
|
growth: "Growth Experiment",
|
|
@@ -15614,7 +16581,10 @@ var STRATEGY_OUTCOMES_PLAYBOOK = {
|
|
|
15614
16581
|
region: "strategy_outcomes",
|
|
15615
16582
|
is_canonical: true,
|
|
15616
16583
|
related_framework_ids: ["okr-framework", "three-horizons", "north-star-metric", "metrics-tree", "wardley-map"],
|
|
15617
|
-
|
|
16584
|
+
// DT-PB-3: anchor is `outcome`, not `objective`. The creation_sequence
|
|
16585
|
+
// creates outcome (step 3) before objective (step 4), and outcome is the
|
|
16586
|
+
// strategy region's gravitational centre (objectives translate outcomes).
|
|
16587
|
+
target_anchor_entity: "outcome",
|
|
15618
16588
|
creation_sequence: [
|
|
15619
16589
|
seqStep(
|
|
15620
16590
|
1,
|
|
@@ -15770,8 +16740,12 @@ var DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK = {
|
|
|
15770
16740
|
seqStep(
|
|
15771
16741
|
8,
|
|
15772
16742
|
"Test",
|
|
15773
|
-
|
|
15774
|
-
|
|
16743
|
+
// DT-PB-1: was `experiment` — which resolves to NO canonical edge with
|
|
16744
|
+
// hypothesis or test_plan, forcing an orphan. `experiment_run` is the
|
|
16745
|
+
// hypothesis-linked test unit (experiment_run_validates_hypothesis,
|
|
16746
|
+
// test_plan_ran_as_experiment_run, experiment_run_yields_evidence).
|
|
16747
|
+
["experiment_run", "test_plan", "evidence"],
|
|
16748
|
+
"Validate with targeted experiment runs. Close the loop between research and action."
|
|
15775
16749
|
)
|
|
15776
16750
|
]
|
|
15777
16751
|
};
|
|
@@ -15818,8 +16792,8 @@ var MARKET_COMPETITIVE_PLAYBOOK = {
|
|
|
15818
16792
|
seqStep(
|
|
15819
16793
|
6,
|
|
15820
16794
|
"Moves",
|
|
15821
|
-
["
|
|
15822
|
-
"Look where competitors are weak and trends are strong. That intersection is where your moves live
|
|
16795
|
+
["competitive_battle_card"],
|
|
16796
|
+
"Look where competitors are weak and trends are strong. That intersection is where your moves live: arm the team with battle cards that turn each competitor weakness into a position you can win. (Partnership moves belong in the business & GTM playbook, where `partnership` connects.)"
|
|
15823
16797
|
)
|
|
15824
16798
|
]
|
|
15825
16799
|
};
|
|
@@ -16232,8 +17206,8 @@ var OPERATIONS_QUALITY_PLAYBOOK = {
|
|
|
16232
17206
|
seqStep(
|
|
16233
17207
|
5,
|
|
16234
17208
|
"Quality Gates",
|
|
16235
|
-
["test_suite", "test_case", "regression_test", "qa_session"],
|
|
16236
|
-
"Establish what does not ship until tests pass. Define the test pyramid: unit, integration, end-to-end."
|
|
17209
|
+
["test_suite", "test_case", "regression_test", "qa_session", "feature", "bug"],
|
|
17210
|
+
"Establish what does not ship until tests pass. Define the test pyramid: unit, integration, end-to-end. Quality is a delivery concern: gates guard the features they cover and the bugs they catch, which is also where incidents trace back (a shipped defect becomes a production incident)."
|
|
16237
17211
|
),
|
|
16238
17212
|
seqStep(
|
|
16239
17213
|
6,
|
|
@@ -16941,6 +17915,9 @@ function getVisibleTypes(lens) {
|
|
|
16941
17915
|
}
|
|
16942
17916
|
return [...typeSet];
|
|
16943
17917
|
}
|
|
17918
|
+
function getLensIds() {
|
|
17919
|
+
return UPG_LENSES.map((l) => l.id);
|
|
17920
|
+
}
|
|
16944
17921
|
var UPG_DOMAIN_RINGS = [
|
|
16945
17922
|
{
|
|
16946
17923
|
id: "nucleus",
|
|
@@ -17834,7 +18811,7 @@ var PORTFOLIO_GUIDE = {
|
|
|
17834
18811
|
var WORKSPACE_GUIDE = {
|
|
17835
18812
|
domain_id: "workspace",
|
|
17836
18813
|
anchor_entity: "workspace",
|
|
17837
|
-
creation_sequence: ["workspace"],
|
|
18814
|
+
creation_sequence: ["workspace", "framework_exercise"],
|
|
17838
18815
|
patterns: [],
|
|
17839
18816
|
required_bridges: [],
|
|
17840
18817
|
anti_patterns: [
|
|
@@ -20221,7 +21198,7 @@ var UPG_ANTI_PATTERNS = [
|
|
|
20221
21198
|
]
|
|
20222
21199
|
},
|
|
20223
21200
|
why_it_matters: "Without any chain link, every downstream artefact (need, opportunity, feature) loses its anchor. Features end up addressing demographics instead of struggles.",
|
|
20224
|
-
remediation: "For each persona, connect it into the user chain via at least one of: `persona_pursues_job`, `persona_experiences_need`, `persona_aspires_to_desired_outcome`, or `persona_incurs_switching_cost`. Use `/upg-persona` or the JTBD canvas workflow.",
|
|
21201
|
+
remediation: "For each persona, connect it into the user chain via at least one of: `persona_pursues_job`, `persona_experiences_need`, `persona_aspires_to_desired_outcome`, or `persona_incurs_switching_cost`. Use `/upg-new-persona` or the JTBD canvas workflow.",
|
|
20225
21202
|
stages: ["concept", "validation", "build", "beta", "launch", "growth", "mature"],
|
|
20226
21203
|
severity: "high",
|
|
20227
21204
|
source: { kind: "practitioner", attribution: "Clayton Christensen, Jobs to Be Done" }
|
|
@@ -20361,7 +21338,7 @@ var UPG_ANTI_PATTERNS = [
|
|
|
20361
21338
|
]
|
|
20362
21339
|
},
|
|
20363
21340
|
why_it_matters: "OKRs without measurable key results cannot be tracked, debated, or learned from. The graph carries intent but not accountability.",
|
|
20364
|
-
remediation: "For each `objective`, define 2\u20134 `key_result` entities and link via `objective_achieved_through_key_result`. Use `/upg-okr` to author.",
|
|
21341
|
+
remediation: "For each `objective`, define 2\u20134 `key_result` entities and link via `objective_achieved_through_key_result`. Use `/upg-new-okr` to author.",
|
|
20365
21342
|
stages: ["validation", "build", "beta", "launch", "growth", "mature"],
|
|
20366
21343
|
severity: "high",
|
|
20367
21344
|
source: { kind: "book", citation: "Measure What Matters, John Doerr (2017)" }
|
|
@@ -20421,7 +21398,7 @@ var UPG_ANTI_PATTERNS = [
|
|
|
20421
21398
|
}
|
|
20422
21399
|
},
|
|
20423
21400
|
why_it_matters: 'A graph with one persona past validation is usually carrying an unexamined "everyone is the same user" assumption.',
|
|
20424
|
-
remediation: "Add personas representing the next 1\u20132 most distinct user segments. Use `/upg-persona`.",
|
|
21401
|
+
remediation: "Add personas representing the next 1\u20132 most distinct user segments. Use `/upg-new-persona`.",
|
|
20425
21402
|
stages: ["validation", "build", "beta", "launch", "growth", "mature"],
|
|
20426
21403
|
severity: "medium",
|
|
20427
21404
|
source: { kind: "practitioner", attribution: "Alan Cooper, The Inmates Are Running the Asylum" }
|
|
@@ -20439,7 +21416,7 @@ var UPG_ANTI_PATTERNS = [
|
|
|
20439
21416
|
]
|
|
20440
21417
|
},
|
|
20441
21418
|
why_it_matters: "Build-only graphs commit the team to delivery without a learning loop. Every shipped feature becomes a permanent assumption.",
|
|
20442
|
-
remediation: "Spin up at least one `experiment_plan` or `hypothesis` per quarter's build batch. Use `/upg-
|
|
21419
|
+
remediation: "Spin up at least one `experiment_plan` or `hypothesis` per quarter's build batch. Use `/upg-new-discovery` or `/upg-new-hypothesis`.",
|
|
20443
21420
|
stages: ["build", "beta", "launch", "growth"],
|
|
20444
21421
|
severity: "high",
|
|
20445
21422
|
source: { kind: "practitioner", attribution: "Marty Cagan, Inspired (continuous discovery)" }
|
|
@@ -20677,25 +21654,54 @@ var UPG_PRODUCT_STAGE_COERCION_MAP = Object.freeze({
|
|
|
20677
21654
|
// check happens before the coercion lookup.
|
|
20678
21655
|
});
|
|
20679
21656
|
var UPG_PRODUCT_STAGES_SET = new Set(UPG_PRODUCT_STAGES);
|
|
21657
|
+
function coerceProductStage(value) {
|
|
21658
|
+
if (value === void 0 || value === null) {
|
|
21659
|
+
return { canonical: void 0, originalValue: value, wasCoerced: false, wasUnknown: false };
|
|
21660
|
+
}
|
|
21661
|
+
if (typeof value !== "string") {
|
|
21662
|
+
return { canonical: void 0, originalValue: value, wasCoerced: false, wasUnknown: true };
|
|
21663
|
+
}
|
|
21664
|
+
if (UPG_PRODUCT_STAGES_SET.has(value)) {
|
|
21665
|
+
return { canonical: value, originalValue: value, wasCoerced: false, wasUnknown: false };
|
|
21666
|
+
}
|
|
21667
|
+
const lower = value.toLowerCase();
|
|
21668
|
+
const mapped = UPG_PRODUCT_STAGE_COERCION_MAP[lower];
|
|
21669
|
+
if (mapped) {
|
|
21670
|
+
return { canonical: mapped, originalValue: value, wasCoerced: true, wasUnknown: false };
|
|
21671
|
+
}
|
|
21672
|
+
return { canonical: void 0, originalValue: value, wasCoerced: false, wasUnknown: true };
|
|
21673
|
+
}
|
|
20680
21674
|
var REFLECT_MODES = [
|
|
20681
21675
|
"assumptions",
|
|
20682
21676
|
"alternatives",
|
|
20683
21677
|
"blind-spots",
|
|
20684
21678
|
"load-bearing"
|
|
20685
21679
|
];
|
|
21680
|
+
function deriveFrameworkExamples() {
|
|
21681
|
+
const byApproach = {
|
|
21682
|
+
plan: [],
|
|
21683
|
+
inspect: [],
|
|
21684
|
+
prioritise: [],
|
|
21685
|
+
trace: [],
|
|
21686
|
+
reflect: []
|
|
21687
|
+
};
|
|
21688
|
+
for (const fw of UPG_FRAMEWORKS) {
|
|
21689
|
+
for (const approachId of fw.approach_ids ?? []) {
|
|
21690
|
+
if (approachId in byApproach) {
|
|
21691
|
+
byApproach[approachId].push(fw.id);
|
|
21692
|
+
}
|
|
21693
|
+
}
|
|
21694
|
+
}
|
|
21695
|
+
return byApproach;
|
|
21696
|
+
}
|
|
21697
|
+
var FRAMEWORK_EXAMPLES = deriveFrameworkExamples();
|
|
20686
21698
|
var PLAN = {
|
|
20687
21699
|
id: "plan",
|
|
20688
21700
|
label: "Plan",
|
|
20689
21701
|
description: 'The path of arrival to "what should I build next?". Plan engages a region by surveying its entity coverage against canonical expectations and surfacing the missing scaffolding: the entities a healthy region carries that this graph does not. Cartographic sense: you are walking the coastline of a region and noting where the contour is incomplete, not deciding a strategy. Frameworks like Now/Next/Later, MoSCoW, and Wardley Mapping live within Plan as the named techniques for organising the gap-filling sequence.',
|
|
20690
21702
|
question_answered: "what should I build next?",
|
|
20691
21703
|
signature_hint: "({ region?: UPGRegionId }) \u2192 { missing_entities, coverage_score }",
|
|
20692
|
-
framework_id_examples:
|
|
20693
|
-
"now-next-later",
|
|
20694
|
-
"moscow",
|
|
20695
|
-
"wardley-map",
|
|
20696
|
-
"okr-framework",
|
|
20697
|
-
"three-horizons"
|
|
20698
|
-
]
|
|
21704
|
+
framework_id_examples: FRAMEWORK_EXAMPLES.plan
|
|
20699
21705
|
};
|
|
20700
21706
|
var INSPECT = {
|
|
20701
21707
|
id: "inspect",
|
|
@@ -20703,28 +21709,15 @@ var INSPECT = {
|
|
|
20703
21709
|
description: 'The path of arrival to "what\'s broken?". Inspect engages a region or a set of entities by running canonical health checks (anti-pattern audits, drift reports, lint passes) and emitting a structured violation list with severity, kind, target entity, description, and fix hint. Cartographic sense: you are surveying the coastline for hazards before approach; the violations are the rocks marked on the chart. The named techniques inside Inspect are the audit catalogues themselves (`UPG_ANTI_PATTERNS` and the lint passes built on the structural rules).',
|
|
20704
21710
|
question_answered: "what's broken?",
|
|
20705
21711
|
signature_hint: "({ region?: UPGRegionId, entities?: entity_ids[] }) \u2192 { violations: [{ severity, kind, entity_id, description, fix_hint }] }",
|
|
20706
|
-
framework_id_examples:
|
|
20707
|
-
"heuristic-evaluation",
|
|
20708
|
-
"tech-debt-tracker",
|
|
20709
|
-
"accessibility-maturity-model",
|
|
20710
|
-
"cognitive-walkthrough",
|
|
20711
|
-
"blameless-postmortem"
|
|
20712
|
-
]
|
|
21712
|
+
framework_id_examples: FRAMEWORK_EXAMPLES.inspect
|
|
20713
21713
|
};
|
|
20714
21714
|
var PRIORITISE = {
|
|
20715
21715
|
id: "prioritise",
|
|
20716
21716
|
label: "Prioritise",
|
|
20717
|
-
description: `The path of arrival to "what's most important?". Prioritise engages an explicit candidate set (entity ids the caller passes in) and ranks it by an explicit framework: RICE,
|
|
21717
|
+
description: `The path of arrival to "what's most important?". Prioritise engages an explicit candidate set (entity ids the caller passes in) and ranks it by an explicit framework: RICE, Kano, MoSCoW. The framework_id is required because prioritisation without a declared scoring lens is incoherent. Cartographic sense: you have a set of charted destinations and you are computing the order of arrival from a chosen vantage. Different frameworks weight the same candidate set differently; the approach delegates the actual ranking math to the named technique (the framework definition).`,
|
|
20718
21718
|
question_answered: "what's most important?",
|
|
20719
21719
|
signature_hint: "({ candidates: entity_ids[], framework_id }) \u2192 { ranked: [{ entity_id, score, rationale }], framework_used }",
|
|
20720
|
-
framework_id_examples:
|
|
20721
|
-
"rice-scoring",
|
|
20722
|
-
"ice-scoring",
|
|
20723
|
-
"kano-model",
|
|
20724
|
-
"cost-of-delay",
|
|
20725
|
-
"moscow",
|
|
20726
|
-
"wsjf"
|
|
20727
|
-
]
|
|
21720
|
+
framework_id_examples: FRAMEWORK_EXAMPLES.prioritise
|
|
20728
21721
|
};
|
|
20729
21722
|
var TRACE = {
|
|
20730
21723
|
id: "trace",
|
|
@@ -20732,34 +21725,15 @@ var TRACE = {
|
|
|
20732
21725
|
description: 'The path of arrival to "walk a meaningful path through existing graph". Trace engages an anchor entity and follows a path expressed as a UPGEntityType[] shorthand. Example: `["persona", "job", "feature"]` walks persona\u2192job\u2192feature using the canonical edge for each pair (resolved via `resolve_edge_for_pair`). An optional `edges_override` array selects non-canonical edges per hop when a pair has multiple resolutions. Cartographic sense: you are tracing a route across charted terrain; anchor is the departure, path is the heading sequence, the canonical edges are the roads. No DSL invented; the shorthand IS the path expression.',
|
|
20733
21726
|
question_answered: "walk a meaningful path through existing graph",
|
|
20734
21727
|
signature_hint: "({ anchor: entity_id, path: UPGEntityType[], edges_override?: (string | null)[] }) \u2192 { trail: [{ depth, entity_id, edge_type_in }], reached: entity_id[] }",
|
|
20735
|
-
framework_id_examples:
|
|
20736
|
-
"opportunity-solution-tree",
|
|
20737
|
-
"strategic-cascade",
|
|
20738
|
-
"metrics-tree",
|
|
20739
|
-
"user-journey-map",
|
|
20740
|
-
"impact-map",
|
|
20741
|
-
"dependency-map"
|
|
20742
|
-
]
|
|
21728
|
+
framework_id_examples: FRAMEWORK_EXAMPLES.trace
|
|
20743
21729
|
};
|
|
20744
21730
|
var REFLECT = {
|
|
20745
21731
|
id: "reflect",
|
|
20746
21732
|
label: "Reflect",
|
|
20747
|
-
description: 'The path of arrival to "what should I be questioning?". Reflect engages an optional scope (region, entity, or `null` for the whole graph) and emits structured prompts a thinker should consider: assumptions to test, alternatives to weigh, blind-spots to surface, load-bearing claims to verify. Mode is optional; absence is open reflection. Cartographic sense: before approaching the coastline, you are asking which features of your chart you have not actually verified; the prompts mark the parts of the map that may be conjecture.
|
|
21733
|
+
description: 'The path of arrival to "what should I be questioning?". Reflect engages an optional scope (region, entity, or `null` for the whole graph) and emits structured prompts a thinker should consider: assumptions to test, alternatives to weigh, blind-spots to surface, load-bearing claims to verify. Mode is optional; absence is open reflection. Cartographic sense: before approaching the coastline, you are asking which features of your chart you have not actually verified; the prompts mark the parts of the map that may be conjecture. Retrospective and Build-Measure-Learn are the named reflective techniques in the canonical surface.',
|
|
20748
21734
|
question_answered: "what should I be questioning?",
|
|
20749
21735
|
signature_hint: "({ scope?: UPGRegionId | entity_id | null, mode?: 'assumptions' | 'alternatives' | 'blind-spots' | 'load-bearing' }) \u2192 { prompts: [{ kind, question, target_entities? }] }",
|
|
20750
|
-
framework_id_examples:
|
|
20751
|
-
// Reflection classics: the five canonical reflect frameworks.
|
|
20752
|
-
"five-whys",
|
|
20753
|
-
"pre-mortem",
|
|
20754
|
-
"red-team",
|
|
20755
|
-
"devils-advocate",
|
|
20756
|
-
"second-order-thinking",
|
|
20757
|
-
// Reflective ceremonies + reflective JTBD lens already in the catalog.
|
|
20758
|
-
"retrospective",
|
|
20759
|
-
"four-forces-of-progress",
|
|
20760
|
-
"assumption-canvas",
|
|
20761
|
-
"win-loss-analysis"
|
|
20762
|
-
]
|
|
21736
|
+
framework_id_examples: FRAMEWORK_EXAMPLES.reflect
|
|
20763
21737
|
};
|
|
20764
21738
|
var UPG_APPROACHES = [PLAN, INSPECT, PRIORITISE, TRACE, REFLECT];
|
|
20765
21739
|
var UPG_APPROACHES_BY_ID = Object.fromEntries(
|
|
@@ -22637,7 +23611,7 @@ var NODE_KEY_ORDER = [
|
|
|
22637
23611
|
"sort_order",
|
|
22638
23612
|
"properties"
|
|
22639
23613
|
];
|
|
22640
|
-
var EDGE_KEY_ORDER = ["id", "source", "target", "type", "mapping_confidence"];
|
|
23614
|
+
var EDGE_KEY_ORDER = ["id", "source", "target", "type", "mapping_confidence", "properties"];
|
|
22641
23615
|
var CROSS_EDGE_KEY_ORDER = ["id", "source", "target", "type", "source_product_id", "target_product_id", "mapping_confidence"];
|
|
22642
23616
|
var PRODUCT_KEY_ORDER = ["id", "title", "description", "stage", "properties"];
|
|
22643
23617
|
function canonicalNode(node) {
|
|
@@ -22649,7 +23623,8 @@ function canonicalNode(node) {
|
|
|
22649
23623
|
}
|
|
22650
23624
|
function canonicalEdge(edge) {
|
|
22651
23625
|
return orderedObject(edge, EDGE_KEY_ORDER, {
|
|
22652
|
-
forceKeys: ["id", "source", "target", "type"]
|
|
23626
|
+
forceKeys: ["id", "source", "target", "type"],
|
|
23627
|
+
openKeys: ["properties"]
|
|
22653
23628
|
});
|
|
22654
23629
|
}
|
|
22655
23630
|
function canonicalCrossEdge(edge) {
|
|
@@ -22687,7 +23662,7 @@ function portfolioBody(doc) {
|
|
|
22687
23662
|
function computeBodyChecksum(doc) {
|
|
22688
23663
|
const body = isPortfolio(doc) ? portfolioBody(doc) : singleBody(doc);
|
|
22689
23664
|
const content = JSON.stringify(body);
|
|
22690
|
-
return
|
|
23665
|
+
return createHash(INTEGRITY_HASH_PRIMITIVE).update(content).digest("hex").slice(0, INTEGRITY_DIGEST_HEX);
|
|
22691
23666
|
}
|
|
22692
23667
|
function isPortfolio(doc) {
|
|
22693
23668
|
return doc.type === "portfolio" || "cross_edges" in doc;
|
|
@@ -22753,7 +23728,7 @@ function serializePortfolioWithHeader(doc, opts) {
|
|
|
22753
23728
|
header.integrity = { algorithm: INTEGRITY_ALGORITHM, body: computeBodyChecksum(doc) };
|
|
22754
23729
|
return JSON.stringify({ $upg: header, ...body }, null, 2) + "\n";
|
|
22755
23730
|
}
|
|
22756
|
-
var UPG_VERSION = "0.8.
|
|
23731
|
+
var UPG_VERSION = "0.8.4";
|
|
22757
23732
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
22758
23733
|
var UPG_TYPES = getTypes();
|
|
22759
23734
|
var UPG_TYPES_SET = new Set(UPG_TYPES);
|
|
@@ -22813,6 +23788,52 @@ var UPG_DOMAIN_COUNT = UPG_DOMAINS.length;
|
|
|
22813
23788
|
var UPG_EDGE_COUNT = UPG_EDGE_TYPES.length;
|
|
22814
23789
|
var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
22815
23790
|
|
|
23791
|
+
// src/lib/server-context.ts
|
|
23792
|
+
function text(s) {
|
|
23793
|
+
return { content: [{ type: "text", text: s }] };
|
|
23794
|
+
}
|
|
23795
|
+
function textError(s) {
|
|
23796
|
+
return { content: [{ type: "text", text: s }], isError: true };
|
|
23797
|
+
}
|
|
23798
|
+
var CANONICAL_LENS_IDS = getLensIds();
|
|
23799
|
+
function isCanonicalLens(id) {
|
|
23800
|
+
return typeof id === "string" && CANONICAL_LENS_IDS.includes(id);
|
|
23801
|
+
}
|
|
23802
|
+
function createSessionContext() {
|
|
23803
|
+
return {
|
|
23804
|
+
lens: "product",
|
|
23805
|
+
skills_invoked: [],
|
|
23806
|
+
recommendations_given: [],
|
|
23807
|
+
focus_area: null,
|
|
23808
|
+
custom: {}
|
|
23809
|
+
};
|
|
23810
|
+
}
|
|
23811
|
+
function createQueryCache() {
|
|
23812
|
+
return { entries: /* @__PURE__ */ new Map(), counter: 0 };
|
|
23813
|
+
}
|
|
23814
|
+
function syncFilePath(upgPath) {
|
|
23815
|
+
const dir = path.dirname(upgPath);
|
|
23816
|
+
const base = path.basename(upgPath, ".upg");
|
|
23817
|
+
return path.join(dir, `${base}.upg-sync`);
|
|
23818
|
+
}
|
|
23819
|
+
async function readSyncState(upgPath) {
|
|
23820
|
+
const p = syncFilePath(upgPath);
|
|
23821
|
+
try {
|
|
23822
|
+
const raw = await fsp.readFile(p, "utf-8");
|
|
23823
|
+
return JSON.parse(raw);
|
|
23824
|
+
} catch {
|
|
23825
|
+
return null;
|
|
23826
|
+
}
|
|
23827
|
+
}
|
|
23828
|
+
async function writeSyncState(upgPath, state) {
|
|
23829
|
+
const p = syncFilePath(upgPath);
|
|
23830
|
+
await fsp.writeFile(p, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
23831
|
+
}
|
|
23832
|
+
async function hashFile(filePath) {
|
|
23833
|
+
const content = await fsp.readFile(filePath, "utf-8");
|
|
23834
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
23835
|
+
}
|
|
23836
|
+
|
|
22816
23837
|
// src/tools/context.ts
|
|
22817
23838
|
import { computeGraphDigest } from "@unified-product-graph/sdk";
|
|
22818
23839
|
function lensAwareLabel(entityType, lensId) {
|
|
@@ -22870,7 +23891,7 @@ Lens: ${sessionContext.lens}`
|
|
|
22870
23891
|
lines.push(` - [${sev}] ${b.title}`);
|
|
22871
23892
|
}
|
|
22872
23893
|
}
|
|
22873
|
-
} else if (sessionContext.lens === "
|
|
23894
|
+
} else if (sessionContext.lens === "ux_design") {
|
|
22874
23895
|
const screens = nodes.filter((n) => n.type === "screen");
|
|
22875
23896
|
const components = nodes.filter((n) => n.type === "design_component");
|
|
22876
23897
|
const flows = nodes.filter((n) => n.type === "user_flow");
|
|
@@ -22985,7 +24006,7 @@ var getGraphDigest = (args, ctx) => {
|
|
|
22985
24006
|
const blockedFeatures = allNodes.filter((n) => blockedFeatureIds.has(n.id)).map((n) => n.title);
|
|
22986
24007
|
const openInvestigations = allNodes.filter((n) => n.type === "investigation" && n.status !== "resolved").length;
|
|
22987
24008
|
lensDigest = { open_bugs: openBugs, blockers: blockerEdges.length, in_flight_features: inFlightFeatures, active_debt: activeDebt, blocked_features: blockedFeatures, open_investigations: openInvestigations };
|
|
22988
|
-
} else if (sessionContext.lens === "
|
|
24009
|
+
} else if (sessionContext.lens === "ux_design") {
|
|
22989
24010
|
const screens = allNodes.filter((n) => n.type === "screen").length;
|
|
22990
24011
|
const components = allNodes.filter((n) => n.type === "design_component").length;
|
|
22991
24012
|
const flows = allNodes.filter((n) => n.type === "user_flow").length;
|
|
@@ -23103,7 +24124,12 @@ var updateSessionContext = (args, ctx) => {
|
|
|
23103
24124
|
if (focusArea !== void 0) {
|
|
23104
24125
|
sessionContext.focus_area = focusArea;
|
|
23105
24126
|
}
|
|
23106
|
-
if (lensArg
|
|
24127
|
+
if (lensArg !== void 0) {
|
|
24128
|
+
if (!isCanonicalLens(lensArg)) {
|
|
24129
|
+
return textError(
|
|
24130
|
+
`Invalid lens "${lensArg}". Canonical lenses: ${CANONICAL_LENS_IDS.join(", ")}`
|
|
24131
|
+
);
|
|
24132
|
+
}
|
|
23107
24133
|
sessionContext.lens = lensArg;
|
|
23108
24134
|
if (persistLens && store) {
|
|
23109
24135
|
const doc = store.getDocument();
|
|
@@ -23913,7 +24939,7 @@ var updateNode = (args, ctx) => {
|
|
|
23913
24939
|
const existingNode = store.getNode(nid);
|
|
23914
24940
|
if (existingNode) {
|
|
23915
24941
|
const sw = validateStatusAgainstLifecycle(existingNode.type, args.status);
|
|
23916
|
-
if (sw)
|
|
24942
|
+
if (sw) return textError(sw);
|
|
23917
24943
|
}
|
|
23918
24944
|
}
|
|
23919
24945
|
let unknownProperties = [];
|
|
@@ -23946,10 +24972,18 @@ var updateNode = (args, ctx) => {
|
|
|
23946
24972
|
});
|
|
23947
24973
|
if (lengthWarnings.length > 0) warnings.push(...lengthWarnings);
|
|
23948
24974
|
try {
|
|
23949
|
-
|
|
24975
|
+
let updated = store.updateNode(nid, patch);
|
|
24976
|
+
let removedKeys;
|
|
24977
|
+
const unsetArg = args.unset_properties;
|
|
24978
|
+
if (Array.isArray(unsetArg) && unsetArg.length > 0) {
|
|
24979
|
+
const r = store.unsetNodeProperties(nid, unsetArg);
|
|
24980
|
+
updated = r.node;
|
|
24981
|
+
if (r.removed.length > 0) removedKeys = r.removed;
|
|
24982
|
+
}
|
|
23950
24983
|
const result = { node: updated };
|
|
23951
24984
|
if (warnings.length > 0) result.warning = warnings.join(" | ");
|
|
23952
24985
|
if (unknownProperties.length > 0) result.unknown_properties = unknownProperties;
|
|
24986
|
+
if (removedKeys && removedKeys.length > 0) result.unset = removedKeys;
|
|
23953
24987
|
return text(JSON.stringify(result, null, 2));
|
|
23954
24988
|
} catch (err) {
|
|
23955
24989
|
return textError(err.message);
|
|
@@ -24271,7 +25305,7 @@ var deduplicateNodes = (args, ctx) => {
|
|
|
24271
25305
|
// src/tools/edges.ts
|
|
24272
25306
|
import { edgeId as edgeId2 } from "@unified-product-graph/sdk";
|
|
24273
25307
|
import { inferEdgeTypeWithTier } from "@unified-product-graph/sdk";
|
|
24274
|
-
import {
|
|
25308
|
+
import { validateExplicitEdgeType } from "@unified-product-graph/sdk";
|
|
24275
25309
|
import { buildResolverHints } from "@unified-product-graph/sdk";
|
|
24276
25310
|
import {
|
|
24277
25311
|
createEdge as createEdgeLib,
|
|
@@ -24300,7 +25334,8 @@ var createEdge = (args, ctx) => {
|
|
|
24300
25334
|
target_id: args.target_id,
|
|
24301
25335
|
target_title: args.target_title,
|
|
24302
25336
|
target_type: args.target_type,
|
|
24303
|
-
type: args.type
|
|
25337
|
+
type: args.type,
|
|
25338
|
+
properties: args.properties
|
|
24304
25339
|
});
|
|
24305
25340
|
if ("error" in result) {
|
|
24306
25341
|
if (result.no_canonical_edge_for) {
|
|
@@ -24378,13 +25413,13 @@ var batchCreateEdges = (args, ctx) => {
|
|
|
24378
25413
|
);
|
|
24379
25414
|
}
|
|
24380
25415
|
if (e.type) {
|
|
24381
|
-
const
|
|
25416
|
+
const typeCheck = validateExplicitEdgeType(
|
|
24382
25417
|
e.type,
|
|
24383
25418
|
sourceNode.type,
|
|
24384
25419
|
targetNode.type
|
|
24385
25420
|
);
|
|
24386
|
-
if (
|
|
24387
|
-
return textError(`Edge at index ${i}: ${
|
|
25421
|
+
if (typeCheck.errors.length > 0) {
|
|
25422
|
+
return textError(`Edge at index ${i}: ${typeCheck.errors.join(" ")}`);
|
|
24388
25423
|
}
|
|
24389
25424
|
resolvedEdgeTypes.push(e.type);
|
|
24390
25425
|
} else {
|
|
@@ -24832,10 +25867,11 @@ var listLocalProducts = (_args, _ctx) => {
|
|
|
24832
25867
|
try {
|
|
24833
25868
|
const raw = fs.readFileSync(filePath, "utf-8");
|
|
24834
25869
|
const doc = JSON.parse(raw);
|
|
25870
|
+
const coerced = coerceProductStage(doc.product?.stage);
|
|
24835
25871
|
products.push({
|
|
24836
25872
|
file: path3.relative(cwd, filePath),
|
|
24837
25873
|
title: doc.product?.title ?? "(untitled)",
|
|
24838
|
-
stage:
|
|
25874
|
+
stage: coerced.canonical ?? null,
|
|
24839
25875
|
nodes: Array.isArray(doc.nodes) ? doc.nodes.length : 0,
|
|
24840
25876
|
edges: Array.isArray(doc.edges) ? doc.edges.length : 0
|
|
24841
25877
|
});
|
|
@@ -25198,6 +26234,62 @@ var getEntitySchema = (args, _ctx) => {
|
|
|
25198
26234
|
}
|
|
25199
26235
|
};
|
|
25200
26236
|
|
|
26237
|
+
// src/tools/frameworks.ts
|
|
26238
|
+
import {
|
|
26239
|
+
applyFramework as applyFrameworkLib,
|
|
26240
|
+
scoreEntity as scoreEntityLib
|
|
26241
|
+
} from "@unified-product-graph/sdk";
|
|
26242
|
+
var applyFramework = (args, ctx) => {
|
|
26243
|
+
const { store } = ctx;
|
|
26244
|
+
const frameworkId = args.framework_id;
|
|
26245
|
+
if (!frameworkId) {
|
|
26246
|
+
return textError('Missing required parameter: framework_id (e.g. "moscow", "rice-scoring")');
|
|
26247
|
+
}
|
|
26248
|
+
try {
|
|
26249
|
+
const result = applyFrameworkLib(store, {
|
|
26250
|
+
framework_id: frameworkId,
|
|
26251
|
+
title: args.title,
|
|
26252
|
+
entity_ids: args.entity_ids ?? [],
|
|
26253
|
+
status: args.status
|
|
26254
|
+
});
|
|
26255
|
+
return text(
|
|
26256
|
+
JSON.stringify(
|
|
26257
|
+
{
|
|
26258
|
+
exercise_id: result.exercise.id,
|
|
26259
|
+
exercise: result.exercise,
|
|
26260
|
+
included: result.edges.map((e) => ({ edge_id: e.id, entity_id: e.target })),
|
|
26261
|
+
warnings: result.warnings
|
|
26262
|
+
},
|
|
26263
|
+
null,
|
|
26264
|
+
2
|
|
26265
|
+
)
|
|
26266
|
+
);
|
|
26267
|
+
} catch (err) {
|
|
26268
|
+
return textError(err.message);
|
|
26269
|
+
}
|
|
26270
|
+
};
|
|
26271
|
+
var scoreEntity = (args, ctx) => {
|
|
26272
|
+
const { store } = ctx;
|
|
26273
|
+
const exerciseId = args.exercise_id;
|
|
26274
|
+
const entityId = args.entity_id;
|
|
26275
|
+
const values = args.values;
|
|
26276
|
+
if (!exerciseId) return textError("Missing required parameter: exercise_id");
|
|
26277
|
+
if (!entityId) return textError("Missing required parameter: entity_id");
|
|
26278
|
+
if (!values || typeof values !== "object" || Array.isArray(values)) {
|
|
26279
|
+
return textError(
|
|
26280
|
+
'Missing required parameter: values (object of input \u2192 value, e.g. { "moscow": "must" })'
|
|
26281
|
+
);
|
|
26282
|
+
}
|
|
26283
|
+
const result = scoreEntityLib(store, {
|
|
26284
|
+
exercise_id: exerciseId,
|
|
26285
|
+
entity_id: entityId,
|
|
26286
|
+
values,
|
|
26287
|
+
replace: args.replace
|
|
26288
|
+
});
|
|
26289
|
+
if ("error" in result) return textError(result.error);
|
|
26290
|
+
return text(JSON.stringify({ edge: result.edge, warnings: result.warnings }, null, 2));
|
|
26291
|
+
};
|
|
26292
|
+
|
|
25201
26293
|
// src/tools/spec.ts
|
|
25202
26294
|
import { buildResolverHints as buildResolverHints2 } from "@unified-product-graph/sdk";
|
|
25203
26295
|
import {
|
|
@@ -25211,7 +26303,7 @@ import {
|
|
|
25211
26303
|
// src/tools/validation.ts
|
|
25212
26304
|
import { computeSchemaDriftSummary } from "@unified-product-graph/sdk";
|
|
25213
26305
|
import { collectAntiPatternInputs } from "@unified-product-graph/sdk";
|
|
25214
|
-
import { validateEdgeTypePair
|
|
26306
|
+
import { validateEdgeTypePair } from "@unified-product-graph/sdk";
|
|
25215
26307
|
import { checkPropertyTypes as checkPropertyTypes2 } from "@unified-product-graph/sdk";
|
|
25216
26308
|
var CANONICAL_NODE_FIELDS = /* @__PURE__ */ new Set([
|
|
25217
26309
|
"id",
|
|
@@ -25503,7 +26595,7 @@ var validateGraph = (args, ctx) => {
|
|
|
25503
26595
|
const sourceNode = nodeById.get(edge.source);
|
|
25504
26596
|
const targetNode = nodeById.get(edge.target);
|
|
25505
26597
|
if (!sourceNode || !targetNode) continue;
|
|
25506
|
-
const pairCheck =
|
|
26598
|
+
const pairCheck = validateEdgeTypePair(
|
|
25507
26599
|
edge.type,
|
|
25508
26600
|
sourceNode.type,
|
|
25509
26601
|
targetNode.type
|
|
@@ -25757,10 +26849,13 @@ function approachEnvelope(approachId, scope, payload) {
|
|
|
25757
26849
|
}
|
|
25758
26850
|
var plan = (args, ctx) => {
|
|
25759
26851
|
const region = args.region;
|
|
25760
|
-
const
|
|
26852
|
+
const exhaustive = args.exhaustive;
|
|
26853
|
+
const result = executePlan(ctx.store, { region, exhaustive });
|
|
25761
26854
|
return approachEnvelope("plan", region ?? null, {
|
|
25762
|
-
params: { region: region ?? null },
|
|
26855
|
+
params: { region: region ?? null, exhaustive: exhaustive ?? false },
|
|
25763
26856
|
region: result.region,
|
|
26857
|
+
plan_scope: result.scope,
|
|
26858
|
+
scoped_regions: result.scoped_regions,
|
|
25764
26859
|
missing_entities: result.missing_entities,
|
|
25765
26860
|
coverage_score: result.coverage_score,
|
|
25766
26861
|
expected_count: result.expected_count,
|
|
@@ -25789,34 +26884,38 @@ var inspect = async (args, ctx) => {
|
|
|
25789
26884
|
});
|
|
25790
26885
|
};
|
|
25791
26886
|
var prioritise = (args, ctx) => {
|
|
25792
|
-
const candidates = args.candidates;
|
|
26887
|
+
const candidates = args.candidates ?? [];
|
|
25793
26888
|
const frameworkId = args.framework_id;
|
|
25794
|
-
|
|
25795
|
-
return textError("Missing required parameter: candidates (entity_id[])");
|
|
25796
|
-
}
|
|
26889
|
+
const exerciseId = args.exercise_id;
|
|
25797
26890
|
if (!frameworkId) {
|
|
25798
26891
|
return textError(
|
|
25799
26892
|
'Missing required parameter: framework_id (e.g. "rice-scoring", "ice-scoring", "wsjf")'
|
|
25800
26893
|
);
|
|
25801
26894
|
}
|
|
26895
|
+
if (!exerciseId && (!Array.isArray(candidates) || candidates.length === 0)) {
|
|
26896
|
+
return textError(
|
|
26897
|
+
"Provide candidates (entity_id[]), or an exercise_id whose includes edges supply the candidates and their scoring inputs."
|
|
26898
|
+
);
|
|
26899
|
+
}
|
|
25802
26900
|
const framework = UPG_FRAMEWORKS_BY_ID[frameworkId];
|
|
25803
26901
|
if (!framework) {
|
|
25804
26902
|
return textError(
|
|
25805
26903
|
`Unknown framework_id: "${frameworkId}". See list_frameworks for valid ids.`
|
|
25806
26904
|
);
|
|
25807
26905
|
}
|
|
25808
|
-
const execResult = executePrioritise(framework, candidates, ctx.store);
|
|
26906
|
+
const execResult = executePrioritise(framework, candidates, ctx.store, { exerciseId });
|
|
26907
|
+
const params = { candidates, framework_id: frameworkId, ...exerciseId ? { exercise_id: exerciseId } : {} };
|
|
25809
26908
|
if (execResult.kind === "execution") {
|
|
25810
26909
|
return approachEnvelope("prioritise", candidates, {
|
|
25811
|
-
params
|
|
26910
|
+
params,
|
|
25812
26911
|
framework_resolved: execResult.framework_used,
|
|
25813
26912
|
ranked: execResult.ranked,
|
|
25814
26913
|
required_properties: execResult.required_properties,
|
|
25815
|
-
execution_mode: "execution_v0_4_0"
|
|
26914
|
+
execution_mode: exerciseId ? "exercise_v0_8_4" : "execution_v0_4_0"
|
|
25816
26915
|
});
|
|
25817
26916
|
}
|
|
25818
26917
|
return approachEnvelope("prioritise", candidates, {
|
|
25819
|
-
params
|
|
26918
|
+
params,
|
|
25820
26919
|
framework_resolved: execResult.framework_used,
|
|
25821
26920
|
hint: execResult.hint,
|
|
25822
26921
|
execution_mode: "definition_lookup_v0_4_0"
|
|
@@ -26782,9 +27881,14 @@ function auditOne(name) {
|
|
|
26782
27881
|
};
|
|
26783
27882
|
}
|
|
26784
27883
|
function allSkillNames() {
|
|
26785
|
-
const
|
|
26786
|
-
|
|
26787
|
-
|
|
27884
|
+
const names = /* @__PURE__ */ new Set();
|
|
27885
|
+
for (const dir of [sourceSkillsDir(), deployedSkillsDir()]) {
|
|
27886
|
+
if (!existsSync2(dir)) continue;
|
|
27887
|
+
for (const d of readdirSync2(dir, { withFileTypes: true })) {
|
|
27888
|
+
if (d.isDirectory() || d.isSymbolicLink()) names.add(d.name);
|
|
27889
|
+
}
|
|
27890
|
+
}
|
|
27891
|
+
return [...names].sort();
|
|
26788
27892
|
}
|
|
26789
27893
|
var skillAudit = (args) => {
|
|
26790
27894
|
const filter = typeof args?.name === "string" && args.name.length > 0 ? args.name : null;
|
|
@@ -26974,6 +28078,11 @@ var TOOL_DEFINITIONS = [
|
|
|
26974
28078
|
properties: {
|
|
26975
28079
|
type: "object",
|
|
26976
28080
|
description: "Merged with existing properties"
|
|
28081
|
+
},
|
|
28082
|
+
unset_properties: {
|
|
28083
|
+
type: "array",
|
|
28084
|
+
items: { type: "string" },
|
|
28085
|
+
description: "Property keys to DELETE. Applied after the `properties` merge, so one call can set some keys and drop others. Writing `{ key: null }` only stores a literal null; use this to actually remove a key. Unknown keys are ignored."
|
|
26977
28086
|
}
|
|
26978
28087
|
},
|
|
26979
28088
|
required: ["node_id"]
|
|
@@ -27181,6 +28290,10 @@ var TOOL_DEFINITIONS = [
|
|
|
27181
28290
|
type: {
|
|
27182
28291
|
type: "string",
|
|
27183
28292
|
description: "Edge type. Auto-inferred if omitted."
|
|
28293
|
+
},
|
|
28294
|
+
properties: {
|
|
28295
|
+
type: "object",
|
|
28296
|
+
description: "Edge-scoped properties. Only permitted on edge types that opt in (currently framework_exercise_includes_node); rejected on plain semantic edges."
|
|
27184
28297
|
}
|
|
27185
28298
|
},
|
|
27186
28299
|
required: ["source_id"]
|
|
@@ -27456,17 +28569,18 @@ var TOOL_DEFINITIONS = [
|
|
|
27456
28569
|
},
|
|
27457
28570
|
{
|
|
27458
28571
|
name: "plan",
|
|
27459
|
-
description: 'Plan approach: path of arrival to "what should I build next?". Returns the Plan record + invocation params wrapped in `{ approach_id, scope, generated_at, approach, params }`. The LLM consumes `signature_hint` and synthesises `{ missing_entities, coverage_score }` against the live graph. Optional `region` narrows scope.',
|
|
28572
|
+
description: 'Plan approach: path of arrival to "what should I build next?". Returns the Plan record + invocation params wrapped in `{ approach_id, scope, generated_at, approach, params }`. The LLM consumes `signature_hint` and synthesises `{ missing_entities, coverage_score }` against the live graph. Optional `region` narrows scope; omit `region` to scope to the product\'s ACTIVE regions; pass `exhaustive:true` to score the full type universe (UPG-601).',
|
|
27460
28573
|
inputSchema: {
|
|
27461
28574
|
type: "object",
|
|
27462
28575
|
properties: {
|
|
27463
|
-
region: { type: "string", description:
|
|
28576
|
+
region: { type: "string", description: `Optional UPGRegionId or atomic-domain id. Narrows planning scope to a single region (e.g. "users_needs", "business_gtm_growth"). Omit to scope to the product's active regions.` },
|
|
28577
|
+
exhaustive: { type: "boolean", description: "If true, score against the entire 312-type universe (every domain creation sequence). Off by default; whole-universe gap scoring is noisy for a focused product. Only applies when `region` is omitted." }
|
|
27464
28578
|
}
|
|
27465
28579
|
}
|
|
27466
28580
|
},
|
|
27467
28581
|
{
|
|
27468
28582
|
name: "inspect",
|
|
27469
|
-
description: '[LLM-mediated] This tool returns a routing envelope, not computed results. For user-facing inspection, invoke the /upg-
|
|
28583
|
+
description: '[LLM-mediated] This tool returns a routing envelope, not computed results. For user-facing inspection, invoke the /upg-show-entity skill instead of calling this tool directly. Inspect approach: path of arrival to "what\'s broken?". Returns the Inspect record + invocation params in the family-resemblance envelope. The LLM consumes `signature_hint` and emits `{ violations: [{ severity, kind, entity_id, description, fix_hint }] }` against `UPG_ANTI_PATTERNS` + the live graph. Optional `region` or `entities[]` scope the audit.',
|
|
27470
28584
|
inputSchema: {
|
|
27471
28585
|
type: "object",
|
|
27472
28586
|
properties: {
|
|
@@ -27481,10 +28595,39 @@ var TOOL_DEFINITIONS = [
|
|
|
27481
28595
|
inputSchema: {
|
|
27482
28596
|
type: "object",
|
|
27483
28597
|
properties: {
|
|
27484
|
-
candidates: { type: "array", items: { type: "string" }, description: "
|
|
27485
|
-
framework_id: { type: "string", description: 'Required. UPGFramework.id of the scoring lens (e.g. "rice-scoring", "ice-scoring", "kano-model", "cost-of-delay", "wsjf").' }
|
|
28598
|
+
candidates: { type: "array", items: { type: "string" }, description: "entity_id[] to rank. Optional when exercise_id is given (the exercise supplies them)." },
|
|
28599
|
+
framework_id: { type: "string", description: 'Required. UPGFramework.id of the scoring lens (e.g. "rice-scoring", "ice-scoring", "kano-model", "cost-of-delay", "wsjf").' },
|
|
28600
|
+
exercise_id: { type: "string", description: "Optional (0.8.4). A framework_exercise id: reads each candidate's scoring inputs from its includes-edge properties instead of node.properties, and bypasses the target-type guard so any entity type can be scored." }
|
|
27486
28601
|
},
|
|
27487
|
-
required: ["
|
|
28602
|
+
required: ["framework_id"]
|
|
28603
|
+
}
|
|
28604
|
+
},
|
|
28605
|
+
{
|
|
28606
|
+
name: "apply_framework",
|
|
28607
|
+
description: "Apply a framework (MoSCoW, RICE, Kano, ...) to a set of entities: creates a framework_exercise node and an `includes` edge to each entity. The per-entity result is recorded on the edge via score_entity, never on the entity node, so the same entity can sit in many exercises and any entity type can be scored. Returns { exercise_id, exercise, included, warnings }.",
|
|
28608
|
+
inputSchema: {
|
|
28609
|
+
type: "object",
|
|
28610
|
+
properties: {
|
|
28611
|
+
framework_id: { type: "string", description: 'Required. UPGFramework.id (e.g. "moscow", "rice-scoring").' },
|
|
28612
|
+
title: { type: "string", description: 'Human label for the exercise (default "<Framework> exercise").' },
|
|
28613
|
+
entity_ids: { type: "array", items: { type: "string" }, description: "Entities to pull into the exercise (any type)." },
|
|
28614
|
+
status: { type: "string", description: "Lifecycle phase: draft | active | archived (default draft)." }
|
|
28615
|
+
},
|
|
28616
|
+
required: ["framework_id"]
|
|
28617
|
+
}
|
|
28618
|
+
},
|
|
28619
|
+
{
|
|
28620
|
+
name: "score_entity",
|
|
28621
|
+
description: "Record a framework's result for one entity on the exercise's includes edge (a MoSCoW bucket, a RICE score, a canvas slot). Auto-includes the entity if not already in scope. Merges into existing edge properties unless replace is set. Returns { edge, warnings }.",
|
|
28622
|
+
inputSchema: {
|
|
28623
|
+
type: "object",
|
|
28624
|
+
properties: {
|
|
28625
|
+
exercise_id: { type: "string", description: "Required. The framework_exercise id." },
|
|
28626
|
+
entity_id: { type: "string", description: "Required. The entity being scored." },
|
|
28627
|
+
values: { type: "object", description: 'Required. The result as { input: value }, e.g. { "moscow": "must" } or { "reach": 800, "impact": 3 }.' },
|
|
28628
|
+
replace: { type: "boolean", description: "Replace the edge properties instead of merging (default false)." }
|
|
28629
|
+
},
|
|
28630
|
+
required: ["exercise_id", "entity_id", "values"]
|
|
27488
28631
|
}
|
|
27489
28632
|
},
|
|
27490
28633
|
{
|
|
@@ -27970,10 +29113,10 @@ var TOOL_DEFINITIONS = [
|
|
|
27970
29113
|
inputSchema: {
|
|
27971
29114
|
type: "object",
|
|
27972
29115
|
properties: {
|
|
27973
|
-
skill_invoked: { type: "string", description: 'Register that this skill was just invoked (e.g. "upg-status")' },
|
|
27974
|
-
recommendation: { type: "string", description: 'Record a recommendation given to the user (e.g. "Run /upg-strategy to fill strategy gap")' },
|
|
29116
|
+
skill_invoked: { type: "string", description: 'Register that this skill was just invoked (e.g. "upg-show-status")' },
|
|
29117
|
+
recommendation: { type: "string", description: 'Record a recommendation given to the user (e.g. "Run /upg-new-strategy to fill strategy gap")' },
|
|
27975
29118
|
focus_area: { type: "string", description: 'Set the current focus area (e.g. "strategy", "validation", "user_research")' },
|
|
27976
|
-
lens: { type: "string", enum: [
|
|
29119
|
+
lens: { type: "string", enum: [...CANONICAL_LENS_IDS], description: "Switch the active lens. Changes what context, skills, and gaps are surfaced first. Canonical lens ids (derived from core): product, ux_design, engineering, growth, business, research, marketing, full." },
|
|
27977
29120
|
persist_lens: { type: "boolean", description: "If true, also save the lens to the .upg file so it persists across sessions" },
|
|
27978
29121
|
custom: { type: "object", description: "Arbitrary key-value pairs for cross-skill state" }
|
|
27979
29122
|
}
|
|
@@ -28140,6 +29283,8 @@ var HANDLERS = {
|
|
|
28140
29283
|
batch_delete_nodes: batchDeleteNodes,
|
|
28141
29284
|
batch_create_edges: batchCreateEdges,
|
|
28142
29285
|
batch_delete_edges: batchDeleteEdges,
|
|
29286
|
+
apply_framework: applyFramework,
|
|
29287
|
+
score_entity: scoreEntity,
|
|
28143
29288
|
repair_dangling_edges: repairDanglingEdges,
|
|
28144
29289
|
export_edges: exportEdges,
|
|
28145
29290
|
rename_edge_type: renameEdgeType,
|
|
@@ -28301,7 +29446,7 @@ function createServer(store) {
|
|
|
28301
29446
|
{
|
|
28302
29447
|
const doc = store.getDocument();
|
|
28303
29448
|
const persistedLens = doc.product?.lens;
|
|
28304
|
-
if (persistedLens &&
|
|
29449
|
+
if (persistedLens && isCanonicalLens(persistedLens)) {
|
|
28305
29450
|
sessionContext.lens = persistedLens;
|
|
28306
29451
|
}
|
|
28307
29452
|
}
|
|
@@ -28408,7 +29553,12 @@ async function runMcpServer() {
|
|
|
28408
29553
|
options: {
|
|
28409
29554
|
file: { type: "string", short: "f" },
|
|
28410
29555
|
title: { type: "string", short: "t" }
|
|
28411
|
-
}
|
|
29556
|
+
},
|
|
29557
|
+
// Tolerate stray positionals. When launched via `upg mcp run`, argv carries
|
|
29558
|
+
// the `mcp run` subcommand tokens; without this, parseArgs throws
|
|
29559
|
+
// ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL and the server never starts. The
|
|
29560
|
+
// standalone bin (`upg-mcp-server`) passes none, so this is harmless there.
|
|
29561
|
+
allowPositionals: true
|
|
28412
29562
|
});
|
|
28413
29563
|
let resolvedPath = await discoverUPGFile(values.file);
|
|
28414
29564
|
if (!resolvedPath) {
|
|
@@ -28480,7 +29630,7 @@ async function runMcpServer() {
|
|
|
28480
29630
|
Deprecated types found in your graph:
|
|
28481
29631
|
${lines.join("\n")}
|
|
28482
29632
|
`);
|
|
28483
|
-
process.stderr.write(`Run /upg-
|
|
29633
|
+
process.stderr.write(`Run /upg-fix-types to update them.
|
|
28484
29634
|
|
|
28485
29635
|
`);
|
|
28486
29636
|
}
|