@unified-product-graph/core 0.8.7 → 0.8.8
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 +14 -0
- package/dist/index.cjs +1721 -1388
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -2
- package/dist/index.d.ts +28 -2
- package/dist/index.js +1721 -1388
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11633,293 +11633,6 @@ function getPropertySchema(entityType) {
|
|
|
11633
11633
|
return UPG_PROPERTY_SCHEMA[entityType];
|
|
11634
11634
|
}
|
|
11635
11635
|
|
|
11636
|
-
// src/grammar/validate.ts
|
|
11637
|
-
var crossProductEdgeTypeSet = new Set(UPG_CROSS_EDGE_TYPES);
|
|
11638
|
-
var CONTENT_DEPTH_WARNING_RULES = /* @__PURE__ */ new Set([
|
|
11639
|
-
"property-type",
|
|
11640
|
-
"property-enum",
|
|
11641
|
-
"self-loop"
|
|
11642
|
-
]);
|
|
11643
|
-
function validateUPGDocument(doc) {
|
|
11644
|
-
const errors = [];
|
|
11645
|
-
const warnings = [];
|
|
11646
|
-
if (!doc || typeof doc !== "object") {
|
|
11647
|
-
return { valid: false, errors: [{ path: "$", message: "Document must be an object" }], warnings };
|
|
11648
|
-
}
|
|
11649
|
-
const d = doc;
|
|
11650
|
-
if (!d.upg_version || typeof d.upg_version !== "string") {
|
|
11651
|
-
errors.push({ path: "$.upg_version", message: "upg_version is required and must be a string" });
|
|
11652
|
-
}
|
|
11653
|
-
if (!d.exported_at || typeof d.exported_at !== "string") {
|
|
11654
|
-
errors.push({ path: "$.exported_at", message: "exported_at is required and must be an ISO 8601 string" });
|
|
11655
|
-
}
|
|
11656
|
-
if (!d.source || typeof d.source !== "object") {
|
|
11657
|
-
errors.push({ path: "$.source", message: "source is required and must be an object" });
|
|
11658
|
-
} else {
|
|
11659
|
-
const source = d.source;
|
|
11660
|
-
if (!source.tool || typeof source.tool !== "string") {
|
|
11661
|
-
errors.push({ path: "$.source.tool", message: "source.tool is required and must be a string" });
|
|
11662
|
-
}
|
|
11663
|
-
}
|
|
11664
|
-
if (!d.product || typeof d.product !== "object") {
|
|
11665
|
-
errors.push({ path: "$.product", message: "product is required and must be an object" });
|
|
11666
|
-
} else {
|
|
11667
|
-
const product = d.product;
|
|
11668
|
-
if (!product.id || typeof product.id !== "string") {
|
|
11669
|
-
errors.push({ path: "$.product.id", message: "product.id is required and must be a string" });
|
|
11670
|
-
}
|
|
11671
|
-
if (!product.title || typeof product.title !== "string") {
|
|
11672
|
-
errors.push({ path: "$.product.title", message: "product.title is required and must be a string" });
|
|
11673
|
-
}
|
|
11674
|
-
}
|
|
11675
|
-
const allKnownTypes = new Set(getTypes());
|
|
11676
|
-
const allKnownEdgeTypes = new Set(Object.keys(UPG_EDGE_CATALOG));
|
|
11677
|
-
if (!Array.isArray(d.nodes)) {
|
|
11678
|
-
errors.push({ path: "$.nodes", message: "nodes is required and must be an array" });
|
|
11679
|
-
} else {
|
|
11680
|
-
const nodeIds = /* @__PURE__ */ new Set();
|
|
11681
|
-
const nodesById = /* @__PURE__ */ new Map();
|
|
11682
|
-
d.nodes.forEach((node, i) => {
|
|
11683
|
-
const path = `$.nodes[${i}]`;
|
|
11684
|
-
if (!node || typeof node !== "object") {
|
|
11685
|
-
errors.push({ path, message: "Each node must be an object" });
|
|
11686
|
-
return;
|
|
11687
|
-
}
|
|
11688
|
-
const n = node;
|
|
11689
|
-
if (!n.id || typeof n.id !== "string") {
|
|
11690
|
-
errors.push({ path: `${path}.id`, message: "Node id is required and must be a string" });
|
|
11691
|
-
} else {
|
|
11692
|
-
if (nodeIds.has(n.id)) {
|
|
11693
|
-
errors.push({ path: `${path}.id`, message: `Duplicate node id: ${n.id}` });
|
|
11694
|
-
}
|
|
11695
|
-
nodeIds.add(n.id);
|
|
11696
|
-
nodesById.set(n.id, n);
|
|
11697
|
-
}
|
|
11698
|
-
if (!n.type || typeof n.type !== "string") {
|
|
11699
|
-
errors.push({ path: `${path}.type`, message: "Node type is required and must be a string" });
|
|
11700
|
-
} else if (!allKnownTypes.has(n.type)) {
|
|
11701
|
-
warnings.push({ path: `${path}.type`, message: `Unknown UPG type: "${n.type}". Node will be preserved with its original type.` });
|
|
11702
|
-
}
|
|
11703
|
-
if (!n.title || typeof n.title !== "string") {
|
|
11704
|
-
errors.push({ path: `${path}.title`, message: "Node title is required and must be a string" });
|
|
11705
|
-
} else if (n.title.trim().length === 0) {
|
|
11706
|
-
errors.push({ path: `${path}.title`, message: "Node title must not be blank (whitespace-only)" });
|
|
11707
|
-
}
|
|
11708
|
-
if (typeof n.type === "string") {
|
|
11709
|
-
validateNodeProperties(n, `${path}`, warnings);
|
|
11710
|
-
}
|
|
11711
|
-
});
|
|
11712
|
-
if (!Array.isArray(d.edges)) {
|
|
11713
|
-
errors.push({ path: "$.edges", message: "edges is required and must be an array" });
|
|
11714
|
-
} else {
|
|
11715
|
-
d.edges.forEach((edge, i) => {
|
|
11716
|
-
const path = `$.edges[${i}]`;
|
|
11717
|
-
if (!edge || typeof edge !== "object") {
|
|
11718
|
-
errors.push({ path, message: "Each edge must be an object" });
|
|
11719
|
-
return;
|
|
11720
|
-
}
|
|
11721
|
-
const e = edge;
|
|
11722
|
-
if (!e.id || typeof e.id !== "string") {
|
|
11723
|
-
errors.push({ path: `${path}.id`, message: "Edge id is required and must be a string" });
|
|
11724
|
-
}
|
|
11725
|
-
if (typeof e.type === "string" && crossProductEdgeTypeSet.has(e.type)) {
|
|
11726
|
-
errors.push({
|
|
11727
|
-
path: `${path}.type`,
|
|
11728
|
-
message: `Cross-product edge type "${e.type}" must live in portfolio.cross_edges[], not product edges[].`
|
|
11729
|
-
});
|
|
11730
|
-
return;
|
|
11731
|
-
}
|
|
11732
|
-
if (!e.source || typeof e.source !== "string") {
|
|
11733
|
-
errors.push({ path: `${path}.source`, message: "Edge source is required and must be a string" });
|
|
11734
|
-
} else if (!nodeIds.has(e.source)) {
|
|
11735
|
-
errors.push({ path: `${path}.source`, message: `Edge source references unknown node id: ${e.source}` });
|
|
11736
|
-
}
|
|
11737
|
-
if (!e.target || typeof e.target !== "string") {
|
|
11738
|
-
errors.push({ path: `${path}.target`, message: "Edge target is required and must be a string" });
|
|
11739
|
-
} else if (!nodeIds.has(e.target)) {
|
|
11740
|
-
errors.push({ path: `${path}.target`, message: `Edge target references unknown node id: ${e.target}` });
|
|
11741
|
-
}
|
|
11742
|
-
if (typeof e.source === "string" && typeof e.target === "string" && e.source === e.target) {
|
|
11743
|
-
warnings.push({
|
|
11744
|
-
path: `${path}`,
|
|
11745
|
-
message: `Self-loop edge: source and target are the same node ("${e.source}"). A node related to itself is almost always an error.`,
|
|
11746
|
-
rule: "self-loop"
|
|
11747
|
-
});
|
|
11748
|
-
}
|
|
11749
|
-
if (!e.type || typeof e.type !== "string") {
|
|
11750
|
-
errors.push({ path: `${path}.type`, message: "Edge type is required and must be a string" });
|
|
11751
|
-
} else if (!allKnownEdgeTypes.has(e.type)) {
|
|
11752
|
-
warnings.push({
|
|
11753
|
-
path: `${path}.type`,
|
|
11754
|
-
message: `Unknown edge type: "${e.type}". Edge preserved but not in the edge registry.`
|
|
11755
|
-
});
|
|
11756
|
-
}
|
|
11757
|
-
});
|
|
11758
|
-
const valueToAxis = /* @__PURE__ */ new Map();
|
|
11759
|
-
const edgesArr = d.edges;
|
|
11760
|
-
for (const edge of edgesArr) {
|
|
11761
|
-
if (!edge || typeof edge !== "object") continue;
|
|
11762
|
-
const e = edge;
|
|
11763
|
-
if (e.type === "classification_axis_includes_classification_value" && typeof e.source === "string" && typeof e.target === "string") {
|
|
11764
|
-
valueToAxis.set(e.target, e.source);
|
|
11765
|
-
}
|
|
11766
|
-
}
|
|
11767
|
-
for (const [id, n] of nodesById.entries()) {
|
|
11768
|
-
if (n.type !== "classification_value") continue;
|
|
11769
|
-
if (valueToAxis.has(id)) continue;
|
|
11770
|
-
const pid = n.parent_id;
|
|
11771
|
-
if (typeof pid === "string") {
|
|
11772
|
-
const parent = nodesById.get(pid);
|
|
11773
|
-
if (parent && parent.type === "classification_axis") {
|
|
11774
|
-
valueToAxis.set(id, pid);
|
|
11775
|
-
}
|
|
11776
|
-
}
|
|
11777
|
-
}
|
|
11778
|
-
const axesPerAnalysis = /* @__PURE__ */ new Map();
|
|
11779
|
-
for (const edge of edgesArr) {
|
|
11780
|
-
if (!edge || typeof edge !== "object") continue;
|
|
11781
|
-
const e = edge;
|
|
11782
|
-
if (e.type === "competitive_analysis_dimensioned_by_classification_axis" && typeof e.source === "string") {
|
|
11783
|
-
axesPerAnalysis.set(e.source, (axesPerAnalysis.get(e.source) ?? 0) + 1);
|
|
11784
|
-
}
|
|
11785
|
-
}
|
|
11786
|
-
d.nodes.forEach((node, i) => {
|
|
11787
|
-
if (!node || typeof node !== "object") return;
|
|
11788
|
-
const n = node;
|
|
11789
|
-
if (n.type !== "competitive_analysis") return;
|
|
11790
|
-
const props = n.properties;
|
|
11791
|
-
if (!props || typeof props !== "object") return;
|
|
11792
|
-
const empty = props.empty_cells;
|
|
11793
|
-
if (!Array.isArray(empty)) return;
|
|
11794
|
-
const axisCount = axesPerAnalysis.get(n.id ?? "") ?? 0;
|
|
11795
|
-
if (axisCount !== 2) {
|
|
11796
|
-
warnings.push({
|
|
11797
|
-
path: `$.nodes[${i}].properties.empty_cells`,
|
|
11798
|
-
message: `empty_cells is meaningful only on a 2-axis competitive_analysis; this node is dimensioned by ${axisCount} classification_axis ${axisCount === 1 ? "child" : "children"}.`
|
|
11799
|
-
});
|
|
11800
|
-
}
|
|
11801
|
-
empty.forEach((cell, j) => {
|
|
11802
|
-
const cpath = `$.nodes[${i}].properties.empty_cells[${j}]`;
|
|
11803
|
-
if (!cell || typeof cell !== "object") {
|
|
11804
|
-
errors.push({ path: cpath, message: "empty_cells entry must be an object" });
|
|
11805
|
-
return;
|
|
11806
|
-
}
|
|
11807
|
-
const c = cell;
|
|
11808
|
-
const aRef = c.axis_a_value_ref;
|
|
11809
|
-
const bRef = c.axis_b_value_ref;
|
|
11810
|
-
if (typeof aRef !== "string") {
|
|
11811
|
-
errors.push({ path: `${cpath}.axis_a_value_ref`, message: "axis_a_value_ref is required and must be a string" });
|
|
11812
|
-
}
|
|
11813
|
-
if (typeof bRef !== "string") {
|
|
11814
|
-
errors.push({ path: `${cpath}.axis_b_value_ref`, message: "axis_b_value_ref is required and must be a string" });
|
|
11815
|
-
}
|
|
11816
|
-
if (typeof aRef !== "string" || typeof bRef !== "string") return;
|
|
11817
|
-
const aNode = nodesById.get(aRef);
|
|
11818
|
-
const bNode = nodesById.get(bRef);
|
|
11819
|
-
if (!aNode) {
|
|
11820
|
-
errors.push({ path: `${cpath}.axis_a_value_ref`, message: `Unknown node id: ${aRef}` });
|
|
11821
|
-
} else if (aNode.type !== "classification_value") {
|
|
11822
|
-
errors.push({ path: `${cpath}.axis_a_value_ref`, message: `Expected classification_value, found "${String(aNode.type)}" at ${aRef}` });
|
|
11823
|
-
}
|
|
11824
|
-
if (!bNode) {
|
|
11825
|
-
errors.push({ path: `${cpath}.axis_b_value_ref`, message: `Unknown node id: ${bRef}` });
|
|
11826
|
-
} else if (bNode.type !== "classification_value") {
|
|
11827
|
-
errors.push({ path: `${cpath}.axis_b_value_ref`, message: `Expected classification_value, found "${String(bNode.type)}" at ${bRef}` });
|
|
11828
|
-
}
|
|
11829
|
-
if (aNode && bNode && aNode.type === "classification_value" && bNode.type === "classification_value") {
|
|
11830
|
-
const aAxis = valueToAxis.get(aRef);
|
|
11831
|
-
const bAxis = valueToAxis.get(bRef);
|
|
11832
|
-
if (aAxis && bAxis && aAxis === bAxis) {
|
|
11833
|
-
errors.push({
|
|
11834
|
-
path: cpath,
|
|
11835
|
-
message: `empty_cells references two classification_values from the same axis (${aAxis}); axis_a and axis_b must come from distinct classification_axis parents.`
|
|
11836
|
-
});
|
|
11837
|
-
}
|
|
11838
|
-
}
|
|
11839
|
-
const kind = c.rationale_kind;
|
|
11840
|
-
if (kind !== void 0 && kind !== "structural" && kind !== "ideological" && kind !== "opportunity") {
|
|
11841
|
-
errors.push({
|
|
11842
|
-
path: `${cpath}.rationale_kind`,
|
|
11843
|
-
message: `rationale_kind must be one of 'structural' | 'ideological' | 'opportunity'; got "${String(kind)}".`
|
|
11844
|
-
});
|
|
11845
|
-
}
|
|
11846
|
-
});
|
|
11847
|
-
});
|
|
11848
|
-
}
|
|
11849
|
-
}
|
|
11850
|
-
return { valid: errors.length === 0, errors, warnings };
|
|
11851
|
-
}
|
|
11852
|
-
function runtimeKind(val) {
|
|
11853
|
-
if (val === null || val === void 0) return null;
|
|
11854
|
-
if (Array.isArray(val)) return "string[]";
|
|
11855
|
-
switch (typeof val) {
|
|
11856
|
-
case "string":
|
|
11857
|
-
return "string";
|
|
11858
|
-
case "number":
|
|
11859
|
-
return "number";
|
|
11860
|
-
case "boolean":
|
|
11861
|
-
return "boolean";
|
|
11862
|
-
case "object":
|
|
11863
|
-
return "object";
|
|
11864
|
-
default:
|
|
11865
|
-
return null;
|
|
11866
|
-
}
|
|
11867
|
-
}
|
|
11868
|
-
function valueMatchesType(defType, val) {
|
|
11869
|
-
const kind = runtimeKind(val);
|
|
11870
|
-
if (kind === null) return true;
|
|
11871
|
-
switch (defType) {
|
|
11872
|
-
case "string":
|
|
11873
|
-
return kind === "string";
|
|
11874
|
-
case "number":
|
|
11875
|
-
return kind === "number";
|
|
11876
|
-
case "boolean":
|
|
11877
|
-
return kind === "boolean";
|
|
11878
|
-
case "string[]":
|
|
11879
|
-
return kind === "string[]";
|
|
11880
|
-
case "object":
|
|
11881
|
-
case "assessment":
|
|
11882
|
-
return kind === "object";
|
|
11883
|
-
default:
|
|
11884
|
-
return true;
|
|
11885
|
-
}
|
|
11886
|
-
}
|
|
11887
|
-
function describeKind(val) {
|
|
11888
|
-
if (Array.isArray(val)) return "array";
|
|
11889
|
-
return typeof val;
|
|
11890
|
-
}
|
|
11891
|
-
function validateNodeProperties(node, basePath, warnings) {
|
|
11892
|
-
const type = node.type;
|
|
11893
|
-
if (typeof type !== "string") return;
|
|
11894
|
-
const schema = getPropertySchema(type);
|
|
11895
|
-
if (!schema) return;
|
|
11896
|
-
const props = node.properties;
|
|
11897
|
-
if (!props || typeof props !== "object" || Array.isArray(props)) return;
|
|
11898
|
-
for (const [key, value] of Object.entries(props)) {
|
|
11899
|
-
const def = schema[key];
|
|
11900
|
-
if (!def) continue;
|
|
11901
|
-
if (value === null || value === void 0) continue;
|
|
11902
|
-
if (!valueMatchesType(def.type, value)) {
|
|
11903
|
-
warnings.push({
|
|
11904
|
-
path: `${basePath}.properties.${key}`,
|
|
11905
|
-
message: `Property "${key}" on ${type} should be ${def.type}, got ${describeKind(value)}.`,
|
|
11906
|
-
rule: "property-type"
|
|
11907
|
-
});
|
|
11908
|
-
continue;
|
|
11909
|
-
}
|
|
11910
|
-
if (def.enum && typeof value === "string" && !def.enum.includes(value)) {
|
|
11911
|
-
warnings.push({
|
|
11912
|
-
path: `${basePath}.properties.${key}`,
|
|
11913
|
-
message: `Property "${key}" on ${type} has value "${value}" outside its allowed set: ${def.enum.join(", ")}.`,
|
|
11914
|
-
rule: "property-enum"
|
|
11915
|
-
});
|
|
11916
|
-
}
|
|
11917
|
-
}
|
|
11918
|
-
}
|
|
11919
|
-
function isUPGDocument(doc) {
|
|
11920
|
-
return validateUPGDocument(doc).valid;
|
|
11921
|
-
}
|
|
11922
|
-
|
|
11923
11636
|
// src/frameworks/canonical.ts
|
|
11924
11637
|
var UPG_FRAMEWORKS = [
|
|
11925
11638
|
{
|
|
@@ -12138,31 +11851,37 @@ var UPG_FRAMEWORKS = [
|
|
|
12138
11851
|
"slots": [
|
|
12139
11852
|
{
|
|
12140
11853
|
"label": "Customer Jobs",
|
|
11854
|
+
"role": "customer_job",
|
|
12141
11855
|
"entityTypeId": "job",
|
|
12142
11856
|
"description": "Place job entities in the Customer Jobs position of the matrix"
|
|
12143
11857
|
},
|
|
12144
11858
|
{
|
|
12145
11859
|
"label": "Pains",
|
|
11860
|
+
"role": "pain",
|
|
12146
11861
|
"entityTypeId": "need",
|
|
12147
11862
|
"description": "Place need entities in the Pains position of the matrix"
|
|
12148
11863
|
},
|
|
12149
11864
|
{
|
|
12150
11865
|
"label": "Gains",
|
|
11866
|
+
"role": "gain",
|
|
12151
11867
|
"entityTypeId": "desired_outcome",
|
|
12152
11868
|
"description": "Place desired outcome entities in the Gains position of the matrix"
|
|
12153
11869
|
},
|
|
12154
11870
|
{
|
|
12155
11871
|
"label": "Products & Services",
|
|
11872
|
+
"role": "product_or_service",
|
|
12156
11873
|
"entityTypeId": "feature",
|
|
12157
11874
|
"description": "Place feature entities in the Products & Services position of the matrix"
|
|
12158
11875
|
},
|
|
12159
11876
|
{
|
|
12160
11877
|
"label": "Pain Relievers",
|
|
11878
|
+
"role": "pain_reliever",
|
|
12161
11879
|
"entityTypeId": "feature",
|
|
12162
11880
|
"description": "Place feature entities in the Pain Relievers position of the matrix"
|
|
12163
11881
|
},
|
|
12164
11882
|
{
|
|
12165
11883
|
"label": "Gain Creators",
|
|
11884
|
+
"role": "gain_creator",
|
|
12166
11885
|
"entityTypeId": "feature",
|
|
12167
11886
|
"description": "Place feature entities in the Gain Creators position of the matrix"
|
|
12168
11887
|
}
|
|
@@ -12791,26 +12510,31 @@ var UPG_FRAMEWORKS = [
|
|
|
12791
12510
|
"slots": [
|
|
12792
12511
|
{
|
|
12793
12512
|
"label": "Competitive Rivalry",
|
|
12513
|
+
"role": "competitive_rivalry",
|
|
12794
12514
|
"entityTypeId": "competitor",
|
|
12795
12515
|
"description": "Intensity of competition among existing players"
|
|
12796
12516
|
},
|
|
12797
12517
|
{
|
|
12798
12518
|
"label": "Threat of New Entrants",
|
|
12519
|
+
"role": "new_entrants",
|
|
12799
12520
|
"entityTypeId": "competitor",
|
|
12800
12521
|
"description": "How easy is it for new competitors to enter?"
|
|
12801
12522
|
},
|
|
12802
12523
|
{
|
|
12803
12524
|
"label": "Threat of Substitutes",
|
|
12525
|
+
"role": "substitutes",
|
|
12804
12526
|
"entityTypeId": "competitor",
|
|
12805
12527
|
"description": "Can customers switch to alternatives?"
|
|
12806
12528
|
},
|
|
12807
12529
|
{
|
|
12808
12530
|
"label": "Buyer Power",
|
|
12531
|
+
"role": "buyer_power",
|
|
12809
12532
|
"entityTypeId": "persona",
|
|
12810
12533
|
"description": "How much leverage do buyers have?"
|
|
12811
12534
|
},
|
|
12812
12535
|
{
|
|
12813
12536
|
"label": "Supplier Power",
|
|
12537
|
+
"role": "supplier_power",
|
|
12814
12538
|
"entityTypeId": "persona",
|
|
12815
12539
|
"description": "How much leverage do suppliers have?"
|
|
12816
12540
|
}
|
|
@@ -12978,31 +12702,37 @@ var UPG_FRAMEWORKS = [
|
|
|
12978
12702
|
"slots": [
|
|
12979
12703
|
{
|
|
12980
12704
|
"label": "Inbound Logistics",
|
|
12705
|
+
"role": "inbound_logistics",
|
|
12981
12706
|
"entityTypeId": "key_activity",
|
|
12982
12707
|
"description": "Inbound Logistics phase: key activity entities move through this stage"
|
|
12983
12708
|
},
|
|
12984
12709
|
{
|
|
12985
12710
|
"label": "Operations",
|
|
12711
|
+
"role": "operations",
|
|
12986
12712
|
"entityTypeId": "key_activity",
|
|
12987
12713
|
"description": "Operations phase: key activity entities move through this stage"
|
|
12988
12714
|
},
|
|
12989
12715
|
{
|
|
12990
12716
|
"label": "Outbound Logistics",
|
|
12717
|
+
"role": "outbound_logistics",
|
|
12991
12718
|
"entityTypeId": "key_activity",
|
|
12992
12719
|
"description": "Outbound Logistics phase: key activity entities move through this stage"
|
|
12993
12720
|
},
|
|
12994
12721
|
{
|
|
12995
12722
|
"label": "Marketing & Sales",
|
|
12723
|
+
"role": "marketing_and_sales",
|
|
12996
12724
|
"entityTypeId": "key_activity",
|
|
12997
12725
|
"description": "Marketing & Sales phase: key activity entities move through this stage"
|
|
12998
12726
|
},
|
|
12999
12727
|
{
|
|
13000
12728
|
"label": "Service",
|
|
12729
|
+
"role": "service",
|
|
13001
12730
|
"entityTypeId": "key_activity",
|
|
13002
12731
|
"description": "Service phase: key activity entities move through this stage"
|
|
13003
12732
|
},
|
|
13004
12733
|
{
|
|
13005
12734
|
"label": "Support Activities",
|
|
12735
|
+
"role": "support_activity",
|
|
13006
12736
|
"entityTypeId": "capability",
|
|
13007
12737
|
"description": "Support Activities phase: capability entities move through this stage"
|
|
13008
12738
|
}
|
|
@@ -13073,26 +12803,31 @@ var UPG_FRAMEWORKS = [
|
|
|
13073
12803
|
"slots": [
|
|
13074
12804
|
{
|
|
13075
12805
|
"label": "Portfolio",
|
|
12806
|
+
"role": "portfolio",
|
|
13076
12807
|
"entityTypeId": "portfolio",
|
|
13077
12808
|
"description": "Innovation portfolio balanced across three horizons: core performance, emerging growth, and future creation"
|
|
13078
12809
|
},
|
|
13079
12810
|
{
|
|
13080
12811
|
"label": "Product",
|
|
12812
|
+
"role": "product",
|
|
13081
12813
|
"entityTypeId": "product",
|
|
13082
12814
|
"description": "Product or business unit assigned to a horizon: H1 (defend and extend), H2 (build), or H3 (seed and explore)"
|
|
13083
12815
|
},
|
|
13084
12816
|
{
|
|
13085
12817
|
"label": "Initiative",
|
|
12818
|
+
"role": "initiative",
|
|
13086
12819
|
"entityTypeId": "portfolio",
|
|
13087
12820
|
"description": "Growth initiative positioned in a horizon, with appropriate funding model, governance, and success metrics"
|
|
13088
12821
|
},
|
|
13089
12822
|
{
|
|
13090
12823
|
"label": "Product Area",
|
|
12824
|
+
"role": "product_area",
|
|
13091
12825
|
"entityTypeId": "product_area",
|
|
13092
12826
|
"description": "Product area representing a horizon: core products (H1), adjacent expansions (H2), or experimental ventures (H3)"
|
|
13093
12827
|
},
|
|
13094
12828
|
{
|
|
13095
12829
|
"label": "Capability",
|
|
12830
|
+
"role": "capability",
|
|
13096
12831
|
"entityTypeId": "capability",
|
|
13097
12832
|
"description": "Capability needed to execute across horizons: H1 needs efficiency, H2 needs scaling, H3 needs discovery"
|
|
13098
12833
|
}
|
|
@@ -13572,21 +13307,25 @@ var UPG_FRAMEWORKS = [
|
|
|
13572
13307
|
"slots": [
|
|
13573
13308
|
{
|
|
13574
13309
|
"label": "Must-haves",
|
|
13310
|
+
"role": "must_have",
|
|
13575
13311
|
"entityTypeId": "feature",
|
|
13576
13312
|
"description": "Expected features: absence causes dissatisfaction"
|
|
13577
13313
|
},
|
|
13578
13314
|
{
|
|
13579
13315
|
"label": "Performance",
|
|
13316
|
+
"role": "performance",
|
|
13580
13317
|
"entityTypeId": "feature",
|
|
13581
13318
|
"description": "More is better: linear satisfaction increase"
|
|
13582
13319
|
},
|
|
13583
13320
|
{
|
|
13584
13321
|
"label": "Delighters",
|
|
13322
|
+
"role": "delighter",
|
|
13585
13323
|
"entityTypeId": "feature",
|
|
13586
13324
|
"description": "Unexpected features: presence creates delight"
|
|
13587
13325
|
},
|
|
13588
13326
|
{
|
|
13589
13327
|
"label": "Indifferent",
|
|
13328
|
+
"role": "indifferent",
|
|
13590
13329
|
"entityTypeId": "feature",
|
|
13591
13330
|
"description": "Features users don't care about either way"
|
|
13592
13331
|
}
|
|
@@ -13594,12 +13333,97 @@ var UPG_FRAMEWORKS = [
|
|
|
13594
13333
|
"data": {
|
|
13595
13334
|
"entity_types": [
|
|
13596
13335
|
{
|
|
13597
|
-
"type": "feature",
|
|
13598
|
-
"role": "
|
|
13336
|
+
"type": "feature",
|
|
13337
|
+
"role": "scored_item"
|
|
13338
|
+
}
|
|
13339
|
+
],
|
|
13340
|
+
"required_properties": {
|
|
13341
|
+
"feature": [
|
|
13342
|
+
{
|
|
13343
|
+
"property": "functional_response",
|
|
13344
|
+
"type": "enum",
|
|
13345
|
+
"required": true,
|
|
13346
|
+
"scope": "framework",
|
|
13347
|
+
"label": "Functional Response",
|
|
13348
|
+
"description": "How users feel when the feature IS present",
|
|
13349
|
+
"enum_values": [
|
|
13350
|
+
"i_like_it",
|
|
13351
|
+
"i_expect_it",
|
|
13352
|
+
"i_am_neutral",
|
|
13353
|
+
"i_can_tolerate_it",
|
|
13354
|
+
"i_dislike_it"
|
|
13355
|
+
]
|
|
13356
|
+
},
|
|
13357
|
+
{
|
|
13358
|
+
"property": "dysfunctional_response",
|
|
13359
|
+
"type": "enum",
|
|
13360
|
+
"required": true,
|
|
13361
|
+
"scope": "framework",
|
|
13362
|
+
"label": "Dysfunctional Response",
|
|
13363
|
+
"description": "How users feel when the feature IS NOT present",
|
|
13364
|
+
"enum_values": [
|
|
13365
|
+
"i_like_it",
|
|
13366
|
+
"i_expect_it",
|
|
13367
|
+
"i_am_neutral",
|
|
13368
|
+
"i_can_tolerate_it",
|
|
13369
|
+
"i_dislike_it"
|
|
13370
|
+
]
|
|
13371
|
+
},
|
|
13372
|
+
{
|
|
13373
|
+
"property": "delighter_count",
|
|
13374
|
+
"type": "number",
|
|
13375
|
+
"required": false,
|
|
13376
|
+
"scope": "framework",
|
|
13377
|
+
"label": "Delighter classifications",
|
|
13378
|
+
"description": "Count of survey responses classifying this feature as a delighter (attractive)"
|
|
13379
|
+
},
|
|
13380
|
+
{
|
|
13381
|
+
"property": "performance_count",
|
|
13382
|
+
"type": "number",
|
|
13383
|
+
"required": false,
|
|
13384
|
+
"scope": "framework",
|
|
13385
|
+
"label": "Performance classifications",
|
|
13386
|
+
"description": "Count of survey responses classifying this feature as performance (one-dimensional)"
|
|
13387
|
+
},
|
|
13388
|
+
{
|
|
13389
|
+
"property": "must_be_count",
|
|
13390
|
+
"type": "number",
|
|
13391
|
+
"required": false,
|
|
13392
|
+
"scope": "framework",
|
|
13393
|
+
"label": "Must-be classifications",
|
|
13394
|
+
"description": "Count of survey responses classifying this feature as must-be (basic)"
|
|
13395
|
+
},
|
|
13396
|
+
{
|
|
13397
|
+
"property": "indifferent_count",
|
|
13398
|
+
"type": "number",
|
|
13399
|
+
"required": false,
|
|
13400
|
+
"scope": "framework",
|
|
13401
|
+
"label": "Indifferent classifications",
|
|
13402
|
+
"description": "Count of survey responses classifying this feature as indifferent"
|
|
13403
|
+
}
|
|
13404
|
+
]
|
|
13405
|
+
},
|
|
13406
|
+
"computed_properties": [
|
|
13407
|
+
{
|
|
13408
|
+
"property": "satisfaction_coefficient",
|
|
13409
|
+
"expression": "(delighter_count + performance_count) / (delighter_count + performance_count + must_be_count + indifferent_count)",
|
|
13410
|
+
"entity_type": "feature",
|
|
13411
|
+
"label": "Satisfaction Coefficient",
|
|
13412
|
+
"format": "number"
|
|
13413
|
+
},
|
|
13414
|
+
{
|
|
13415
|
+
"property": "dissatisfaction_coefficient",
|
|
13416
|
+
"expression": "(must_be_count + performance_count) / (delighter_count + performance_count + must_be_count + indifferent_count) * -1",
|
|
13417
|
+
"entity_type": "feature",
|
|
13418
|
+
"label": "Dissatisfaction Coefficient",
|
|
13419
|
+
"format": "number"
|
|
13599
13420
|
}
|
|
13600
13421
|
],
|
|
13601
|
-
"
|
|
13602
|
-
"
|
|
13422
|
+
"scoring_lens": {
|
|
13423
|
+
"applies_to": [
|
|
13424
|
+
"feature"
|
|
13425
|
+
],
|
|
13426
|
+
"inputs": [
|
|
13603
13427
|
{
|
|
13604
13428
|
"property": "functional_response",
|
|
13605
13429
|
"type": "enum",
|
|
@@ -13662,24 +13486,22 @@ var UPG_FRAMEWORKS = [
|
|
|
13662
13486
|
"label": "Indifferent classifications",
|
|
13663
13487
|
"description": "Count of survey responses classifying this feature as indifferent"
|
|
13664
13488
|
}
|
|
13489
|
+
],
|
|
13490
|
+
"computed": [
|
|
13491
|
+
{
|
|
13492
|
+
"property": "satisfaction_coefficient",
|
|
13493
|
+
"expression": "(delighter_count + performance_count) / (delighter_count + performance_count + must_be_count + indifferent_count)",
|
|
13494
|
+
"label": "Satisfaction Coefficient",
|
|
13495
|
+
"format": "number"
|
|
13496
|
+
},
|
|
13497
|
+
{
|
|
13498
|
+
"property": "dissatisfaction_coefficient",
|
|
13499
|
+
"expression": "(must_be_count + performance_count) / (delighter_count + performance_count + must_be_count + indifferent_count) * -1",
|
|
13500
|
+
"label": "Dissatisfaction Coefficient",
|
|
13501
|
+
"format": "number"
|
|
13502
|
+
}
|
|
13665
13503
|
]
|
|
13666
|
-
}
|
|
13667
|
-
"computed_properties": [
|
|
13668
|
-
{
|
|
13669
|
-
"property": "satisfaction_coefficient",
|
|
13670
|
-
"expression": "(delighter_count + performance_count) / (delighter_count + performance_count + must_be_count + indifferent_count)",
|
|
13671
|
-
"entity_type": "feature",
|
|
13672
|
-
"label": "Satisfaction Coefficient",
|
|
13673
|
-
"format": "number"
|
|
13674
|
-
},
|
|
13675
|
-
{
|
|
13676
|
-
"property": "dissatisfaction_coefficient",
|
|
13677
|
-
"expression": "(must_be_count + performance_count) / (delighter_count + performance_count + must_be_count + indifferent_count) * -1",
|
|
13678
|
-
"entity_type": "feature",
|
|
13679
|
-
"label": "Dissatisfaction Coefficient",
|
|
13680
|
-
"format": "number"
|
|
13681
|
-
}
|
|
13682
|
-
]
|
|
13504
|
+
}
|
|
13683
13505
|
},
|
|
13684
13506
|
"structure": {
|
|
13685
13507
|
"pattern": "matrix"
|
|
@@ -13734,16 +13556,19 @@ var UPG_FRAMEWORKS = [
|
|
|
13734
13556
|
"slots": [
|
|
13735
13557
|
{
|
|
13736
13558
|
"label": "Now",
|
|
13559
|
+
"role": "now",
|
|
13737
13560
|
"entityTypeId": "feature",
|
|
13738
13561
|
"description": "Committed work in progress"
|
|
13739
13562
|
},
|
|
13740
13563
|
{
|
|
13741
13564
|
"label": "Next",
|
|
13565
|
+
"role": "next",
|
|
13742
13566
|
"entityTypeId": "feature",
|
|
13743
13567
|
"description": "High-confidence upcoming work"
|
|
13744
13568
|
},
|
|
13745
13569
|
{
|
|
13746
13570
|
"label": "Later",
|
|
13571
|
+
"role": "later",
|
|
13747
13572
|
"entityTypeId": "initiative",
|
|
13748
13573
|
"description": "Exploratory, needs more discovery"
|
|
13749
13574
|
}
|
|
@@ -14259,26 +14084,31 @@ var UPG_FRAMEWORKS = [
|
|
|
14259
14084
|
"slots": [
|
|
14260
14085
|
{
|
|
14261
14086
|
"label": "Atoms",
|
|
14087
|
+
"role": "atom",
|
|
14262
14088
|
"entityTypeId": "design_component",
|
|
14263
14089
|
"description": "Atoms: design component entities for this dimension of the framework"
|
|
14264
14090
|
},
|
|
14265
14091
|
{
|
|
14266
14092
|
"label": "Molecules",
|
|
14093
|
+
"role": "molecule",
|
|
14267
14094
|
"entityTypeId": "design_component",
|
|
14268
14095
|
"description": "Molecules: design token entities for this dimension of the framework"
|
|
14269
14096
|
},
|
|
14270
14097
|
{
|
|
14271
14098
|
"label": "Organisms",
|
|
14099
|
+
"role": "organism",
|
|
14272
14100
|
"entityTypeId": "design_pattern",
|
|
14273
14101
|
"description": "Organisms: design pattern entities for this dimension of the framework"
|
|
14274
14102
|
},
|
|
14275
14103
|
{
|
|
14276
14104
|
"label": "Templates",
|
|
14105
|
+
"role": "template",
|
|
14277
14106
|
"entityTypeId": "wireframe",
|
|
14278
14107
|
"description": "Templates: design system entities for this dimension of the framework"
|
|
14279
14108
|
},
|
|
14280
14109
|
{
|
|
14281
14110
|
"label": "Pages",
|
|
14111
|
+
"role": "page",
|
|
14282
14112
|
"entityTypeId": "screen",
|
|
14283
14113
|
"description": "Pages: screen entities for this dimension of the framework"
|
|
14284
14114
|
}
|
|
@@ -14943,26 +14773,31 @@ var UPG_FRAMEWORKS = [
|
|
|
14943
14773
|
"slots": [
|
|
14944
14774
|
{
|
|
14945
14775
|
"label": "Outer Ring",
|
|
14776
|
+
"role": "outer_ring",
|
|
14946
14777
|
"entityTypeId": "acquisition_channel",
|
|
14947
14778
|
"description": "All 19 traction channels brainstormed"
|
|
14948
14779
|
},
|
|
14949
14780
|
{
|
|
14950
14781
|
"label": "Middle Ring",
|
|
14782
|
+
"role": "middle_ring",
|
|
14951
14783
|
"entityTypeId": "acquisition_channel",
|
|
14952
14784
|
"description": "Top 6 channels worth testing"
|
|
14953
14785
|
},
|
|
14954
14786
|
{
|
|
14955
14787
|
"label": "Inner Ring",
|
|
14788
|
+
"role": "inner_ring",
|
|
14956
14789
|
"entityTypeId": "acquisition_channel",
|
|
14957
14790
|
"description": "Top 3 channels to focus on"
|
|
14958
14791
|
},
|
|
14959
14792
|
{
|
|
14960
14793
|
"label": "Traction Test",
|
|
14794
|
+
"role": "traction_test",
|
|
14961
14795
|
"entityTypeId": "growth_campaign",
|
|
14962
14796
|
"description": "Cheap test for each channel"
|
|
14963
14797
|
},
|
|
14964
14798
|
{
|
|
14965
14799
|
"label": "Bullseye Channel",
|
|
14800
|
+
"role": "bullseye_channel",
|
|
14966
14801
|
"entityTypeId": "acquisition_channel",
|
|
14967
14802
|
"description": "The single best-performing channel"
|
|
14968
14803
|
}
|
|
@@ -15032,26 +14867,31 @@ var UPG_FRAMEWORKS = [
|
|
|
15032
14867
|
"slots": [
|
|
15033
14868
|
{
|
|
15034
14869
|
"label": "Free Entry",
|
|
14870
|
+
"role": "free_entry",
|
|
15035
14871
|
"entityTypeId": "gtm_strategy",
|
|
15036
14872
|
"description": "How users access the product for free"
|
|
15037
14873
|
},
|
|
15038
14874
|
{
|
|
15039
14875
|
"label": "Aha Moment",
|
|
14876
|
+
"role": "aha_moment",
|
|
15040
14877
|
"entityTypeId": "gtm_strategy",
|
|
15041
14878
|
"description": "First experience of core value"
|
|
15042
14879
|
},
|
|
15043
14880
|
{
|
|
15044
14881
|
"label": "Expansion Motion",
|
|
14882
|
+
"role": "expansion_motion",
|
|
15045
14883
|
"entityTypeId": "sales_motion",
|
|
15046
14884
|
"description": "How usage spreads within accounts"
|
|
15047
14885
|
},
|
|
15048
14886
|
{
|
|
15049
14887
|
"label": "Monetisation Trigger",
|
|
14888
|
+
"role": "monetisation_trigger",
|
|
15050
14889
|
"entityTypeId": "gtm_strategy",
|
|
15051
14890
|
"description": "When and how to convert to paid"
|
|
15052
14891
|
},
|
|
15053
14892
|
{
|
|
15054
14893
|
"label": "Metric",
|
|
14894
|
+
"role": "metric",
|
|
15055
14895
|
"entityTypeId": "metric",
|
|
15056
14896
|
"description": "Key metric driving the PLG motion"
|
|
15057
14897
|
}
|
|
@@ -15126,21 +14966,25 @@ var UPG_FRAMEWORKS = [
|
|
|
15126
14966
|
"slots": [
|
|
15127
14967
|
{
|
|
15128
14968
|
"label": "Objective",
|
|
14969
|
+
"role": "objective",
|
|
15129
14970
|
"entityTypeId": "objective",
|
|
15130
14971
|
"description": "Qualitative, inspiring goal"
|
|
15131
14972
|
},
|
|
15132
14973
|
{
|
|
15133
14974
|
"label": "Key Result 1",
|
|
14975
|
+
"role": "key_result",
|
|
15134
14976
|
"entityTypeId": "key_result",
|
|
15135
14977
|
"description": "Measurable outcome proving progress"
|
|
15136
14978
|
},
|
|
15137
14979
|
{
|
|
15138
14980
|
"label": "Key Result 2",
|
|
14981
|
+
"role": "key_result",
|
|
15139
14982
|
"entityTypeId": "key_result",
|
|
15140
14983
|
"description": "Measurable outcome proving progress"
|
|
15141
14984
|
},
|
|
15142
14985
|
{
|
|
15143
14986
|
"label": "Initiatives",
|
|
14987
|
+
"role": "initiative",
|
|
15144
14988
|
"entityTypeId": "initiative",
|
|
15145
14989
|
"description": "Work streams that drive key results"
|
|
15146
14990
|
}
|
|
@@ -15214,26 +15058,31 @@ var UPG_FRAMEWORKS = [
|
|
|
15214
15058
|
"slots": [
|
|
15215
15059
|
{
|
|
15216
15060
|
"label": "Activity",
|
|
15061
|
+
"role": "activity",
|
|
15217
15062
|
"entityTypeId": "key_activity",
|
|
15218
15063
|
"description": "Place persona entities in the Activity position of the matrix"
|
|
15219
15064
|
},
|
|
15220
15065
|
{
|
|
15221
15066
|
"label": "Responsible",
|
|
15067
|
+
"role": "responsible",
|
|
15222
15068
|
"entityTypeId": "role",
|
|
15223
15069
|
"description": "Does the work"
|
|
15224
15070
|
},
|
|
15225
15071
|
{
|
|
15226
15072
|
"label": "Accountable",
|
|
15073
|
+
"role": "accountable",
|
|
15227
15074
|
"entityTypeId": "role",
|
|
15228
15075
|
"description": "Makes the final call"
|
|
15229
15076
|
},
|
|
15230
15077
|
{
|
|
15231
15078
|
"label": "Consulted",
|
|
15079
|
+
"role": "consulted",
|
|
15232
15080
|
"entityTypeId": "role",
|
|
15233
15081
|
"description": "Gives input"
|
|
15234
15082
|
},
|
|
15235
15083
|
{
|
|
15236
15084
|
"label": "Informed",
|
|
15085
|
+
"role": "informed",
|
|
15237
15086
|
"entityTypeId": "role",
|
|
15238
15087
|
"description": "Kept in the loop"
|
|
15239
15088
|
}
|
|
@@ -15307,21 +15156,25 @@ var UPG_FRAMEWORKS = [
|
|
|
15307
15156
|
"slots": [
|
|
15308
15157
|
{
|
|
15309
15158
|
"label": "What Went Well",
|
|
15159
|
+
"role": "went_well",
|
|
15310
15160
|
"entityTypeId": "outcome",
|
|
15311
15161
|
"description": "Practices to continue"
|
|
15312
15162
|
},
|
|
15313
15163
|
{
|
|
15314
15164
|
"label": "What Didn't Go Well",
|
|
15165
|
+
"role": "went_poorly",
|
|
15315
15166
|
"entityTypeId": "need",
|
|
15316
15167
|
"description": "Issues to address"
|
|
15317
15168
|
},
|
|
15318
15169
|
{
|
|
15319
15170
|
"label": "Action Items",
|
|
15171
|
+
"role": "action_item",
|
|
15320
15172
|
"entityTypeId": "learning",
|
|
15321
15173
|
"description": "Changes for next iteration"
|
|
15322
15174
|
},
|
|
15323
15175
|
{
|
|
15324
15176
|
"label": "Learnings",
|
|
15177
|
+
"role": "learning",
|
|
15325
15178
|
"entityTypeId": "learning",
|
|
15326
15179
|
"description": "Insights to carry forward"
|
|
15327
15180
|
}
|
|
@@ -15409,48 +15262,341 @@ var UPG_FRAMEWORKS = [
|
|
|
15409
15262
|
"description": "The business or product outcome the north-star metric is designed to represent"
|
|
15410
15263
|
},
|
|
15411
15264
|
{
|
|
15412
|
-
"label": "Dashboard",
|
|
15413
|
-
"entityTypeId": "dashboard",
|
|
15414
|
-
"description": "Dashboard visualising the metrics tree and showing real-time progress across levels"
|
|
15265
|
+
"label": "Dashboard",
|
|
15266
|
+
"entityTypeId": "dashboard",
|
|
15267
|
+
"description": "Dashboard visualising the metrics tree and showing real-time progress across levels"
|
|
15268
|
+
},
|
|
15269
|
+
{
|
|
15270
|
+
"label": "Report",
|
|
15271
|
+
"entityTypeId": "report",
|
|
15272
|
+
"description": "Periodic report analysing movement across the tree and attributing changes to specific inputs"
|
|
15273
|
+
}
|
|
15274
|
+
],
|
|
15275
|
+
"data": {
|
|
15276
|
+
"entity_types": [
|
|
15277
|
+
{
|
|
15278
|
+
"type": "metric",
|
|
15279
|
+
"role": "item"
|
|
15280
|
+
},
|
|
15281
|
+
{
|
|
15282
|
+
"type": "outcome",
|
|
15283
|
+
"role": "root"
|
|
15284
|
+
},
|
|
15285
|
+
{
|
|
15286
|
+
"type": "dashboard",
|
|
15287
|
+
"role": "item"
|
|
15288
|
+
},
|
|
15289
|
+
{
|
|
15290
|
+
"type": "report",
|
|
15291
|
+
"role": "item"
|
|
15292
|
+
}
|
|
15293
|
+
],
|
|
15294
|
+
"required_properties": {}
|
|
15295
|
+
},
|
|
15296
|
+
"structure": {
|
|
15297
|
+
"pattern": "tree"
|
|
15298
|
+
},
|
|
15299
|
+
"presentation": {
|
|
15300
|
+
"layout": {
|
|
15301
|
+
"type": "tree",
|
|
15302
|
+
"direction": "TB",
|
|
15303
|
+
"engine": "dagre"
|
|
15304
|
+
},
|
|
15305
|
+
"colour_by": "type",
|
|
15306
|
+
"collapsible": true,
|
|
15307
|
+
"card_fields": [
|
|
15308
|
+
"title",
|
|
15309
|
+
"description",
|
|
15310
|
+
"status"
|
|
15311
|
+
]
|
|
15312
|
+
},
|
|
15313
|
+
"education": {
|
|
15314
|
+
"purpose": "Decompose a top-level business metric into its component parts and drivers, creating a tree that shows exactly which levers move the number.",
|
|
15315
|
+
"core_question": "What are the component metrics that drive our north star, and which lever has the most headroom for improvement?",
|
|
15316
|
+
"when_to_use": [
|
|
15317
|
+
"You need to structure your data architecture or analytics practice",
|
|
15318
|
+
"Data quality, governance, or accessibility is a problem",
|
|
15319
|
+
"You want to move from ad-hoc analysis to systematic data practices"
|
|
15320
|
+
],
|
|
15321
|
+
"when_not_to_use": [
|
|
15322
|
+
"The product generates minimal data that does not warrant formal practices",
|
|
15323
|
+
"You are in very early stage where data infrastructure is premature"
|
|
15324
|
+
]
|
|
15325
|
+
}
|
|
15326
|
+
},
|
|
15327
|
+
{
|
|
15328
|
+
"id": "team-health-check",
|
|
15329
|
+
"approach_ids": [
|
|
15330
|
+
"inspect"
|
|
15331
|
+
],
|
|
15332
|
+
"name": "Team Health Check",
|
|
15333
|
+
"version": "1.0.0",
|
|
15334
|
+
"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.",
|
|
15335
|
+
"category": "team_process",
|
|
15336
|
+
"origin": {
|
|
15337
|
+
"type": "practitioner",
|
|
15338
|
+
"attribution": "Spotify / Kniberg",
|
|
15339
|
+
"description": "Popularised by Spotify and Henrik Kniberg's Squad Health Check model (2014). Teams self-assess health across dimensions like mission clarity, psychological safety, speed, and learning.",
|
|
15340
|
+
"url": "https://engineering.atspotify.com/2014/09/squad-health-check-model/",
|
|
15341
|
+
"year": 2014,
|
|
15342
|
+
"license": "open_attribution"
|
|
15343
|
+
},
|
|
15344
|
+
"tags": [
|
|
15345
|
+
"team_process",
|
|
15346
|
+
"table"
|
|
15347
|
+
],
|
|
15348
|
+
"slots": [
|
|
15349
|
+
{
|
|
15350
|
+
"label": "Team",
|
|
15351
|
+
"entityTypeId": "team",
|
|
15352
|
+
"description": "Team conducting the health check; all members vote anonymously on each dimension"
|
|
15353
|
+
},
|
|
15354
|
+
{
|
|
15355
|
+
"label": "Retrospective",
|
|
15356
|
+
"entityTypeId": "retrospective",
|
|
15357
|
+
"description": "Health check session results feeding into the retrospective discussion and action items"
|
|
15358
|
+
},
|
|
15359
|
+
{
|
|
15360
|
+
"label": "Metric",
|
|
15361
|
+
"entityTypeId": "metric",
|
|
15362
|
+
"description": "Health dimension scored, e.g. delivering value, teamwork, fun, learning, mission clarity, speed"
|
|
15363
|
+
},
|
|
15364
|
+
{
|
|
15365
|
+
"label": "Team OKR",
|
|
15366
|
+
"entityTypeId": "team_okr",
|
|
15367
|
+
"description": "Team OKR or improvement goal derived from consistently red health check dimensions"
|
|
15368
|
+
}
|
|
15369
|
+
],
|
|
15370
|
+
"data": {
|
|
15371
|
+
"entity_types": [
|
|
15372
|
+
{
|
|
15373
|
+
"type": "team",
|
|
15374
|
+
"role": "item"
|
|
15375
|
+
},
|
|
15376
|
+
{
|
|
15377
|
+
"type": "retrospective",
|
|
15378
|
+
"role": "item"
|
|
15379
|
+
},
|
|
15380
|
+
{
|
|
15381
|
+
"type": "metric",
|
|
15382
|
+
"role": "item"
|
|
15383
|
+
},
|
|
15384
|
+
{
|
|
15385
|
+
"type": "team_okr",
|
|
15386
|
+
"role": "item"
|
|
15387
|
+
}
|
|
15388
|
+
],
|
|
15389
|
+
"required_properties": {}
|
|
15390
|
+
},
|
|
15391
|
+
"structure": {
|
|
15392
|
+
"pattern": "table"
|
|
15393
|
+
},
|
|
15394
|
+
"presentation": {
|
|
15395
|
+
"layout": {
|
|
15396
|
+
"type": "table",
|
|
15397
|
+
"columns": [
|
|
15398
|
+
{
|
|
15399
|
+
"property": "title",
|
|
15400
|
+
"label": "Team",
|
|
15401
|
+
"sortable": true
|
|
15402
|
+
},
|
|
15403
|
+
{
|
|
15404
|
+
"property": "title",
|
|
15405
|
+
"label": "Retrospective",
|
|
15406
|
+
"sortable": true
|
|
15407
|
+
},
|
|
15408
|
+
{
|
|
15409
|
+
"property": "description",
|
|
15410
|
+
"label": "Metric"
|
|
15411
|
+
},
|
|
15412
|
+
{
|
|
15413
|
+
"property": "title",
|
|
15414
|
+
"label": "Team OKR",
|
|
15415
|
+
"sortable": true
|
|
15416
|
+
}
|
|
15417
|
+
]
|
|
15418
|
+
},
|
|
15419
|
+
"sort_by": {
|
|
15420
|
+
"property": "title",
|
|
15421
|
+
"direction": "asc"
|
|
15422
|
+
},
|
|
15423
|
+
"colour_by": "type",
|
|
15424
|
+
"card_fields": [
|
|
15425
|
+
"title",
|
|
15426
|
+
"description",
|
|
15427
|
+
"status"
|
|
15428
|
+
]
|
|
15429
|
+
},
|
|
15430
|
+
"education": {
|
|
15431
|
+
"purpose": "Regularly assess team health across dimensions (psychological safety, autonomy, mission clarity, fun, speed, learning) to catch problems early and celebrate strengths.",
|
|
15432
|
+
"core_question": "How is the team really doing: where do they feel strong, where do they feel stuck, and what has changed since last check?",
|
|
15433
|
+
"when_to_use": [
|
|
15434
|
+
"You need to improve team collaboration, clarity, or effectiveness",
|
|
15435
|
+
"Roles and responsibilities are unclear or causing friction",
|
|
15436
|
+
"You want to establish or improve team processes and ceremonies"
|
|
15437
|
+
],
|
|
15438
|
+
"when_not_to_use": [
|
|
15439
|
+
"The team is small and informal coordination works well",
|
|
15440
|
+
"Process overhead would slow down a team that needs speed"
|
|
15441
|
+
]
|
|
15442
|
+
}
|
|
15443
|
+
},
|
|
15444
|
+
{
|
|
15445
|
+
"id": "raid-log",
|
|
15446
|
+
"approach_ids": [
|
|
15447
|
+
"inspect"
|
|
15448
|
+
],
|
|
15449
|
+
"name": "RAID Log",
|
|
15450
|
+
"version": "1.0.0",
|
|
15451
|
+
"description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies, the four categories most likely to derail a project if left unmanaged.",
|
|
15452
|
+
"category": "program_mgmt",
|
|
15453
|
+
"origin": {
|
|
15454
|
+
"type": "practitioner",
|
|
15455
|
+
"description": "Standard project management tool used across methodologies. The RAID log (Risks, Assumptions, Issues, Dependencies) provides a single living document for tracking all factors that could derail a programme.",
|
|
15456
|
+
"license": "cc_by"
|
|
15457
|
+
},
|
|
15458
|
+
"tags": [
|
|
15459
|
+
"program_mgmt",
|
|
15460
|
+
"table"
|
|
15461
|
+
],
|
|
15462
|
+
"slots": [
|
|
15463
|
+
{
|
|
15464
|
+
"label": "Risk Register",
|
|
15465
|
+
"entityTypeId": "risk_register",
|
|
15466
|
+
"description": "Risk or assumption logged with likelihood, impact, owner, and mitigation strategy"
|
|
15467
|
+
},
|
|
15468
|
+
{
|
|
15469
|
+
"label": "Risk",
|
|
15470
|
+
"entityTypeId": "risk",
|
|
15471
|
+
"description": "Individual risk scored by probability and impact; severity = probability * impact"
|
|
15472
|
+
},
|
|
15473
|
+
{
|
|
15474
|
+
"label": "Dependency",
|
|
15475
|
+
"entityTypeId": "dependency",
|
|
15476
|
+
"description": "Dependency on another team, system, or deliverable that could block progress if not resolved"
|
|
15415
15477
|
},
|
|
15416
15478
|
{
|
|
15417
|
-
"label": "Report",
|
|
15418
|
-
"entityTypeId": "
|
|
15419
|
-
"description": "
|
|
15479
|
+
"label": "Status Report",
|
|
15480
|
+
"entityTypeId": "status_report",
|
|
15481
|
+
"description": "Status update summarising RAID log changes: new risks, resolved issues, and dependency updates"
|
|
15482
|
+
},
|
|
15483
|
+
{
|
|
15484
|
+
"label": "Deliverable",
|
|
15485
|
+
"entityTypeId": "deliverable",
|
|
15486
|
+
"description": "Deliverable affected by logged risks, issues, or dependencies, linking RAID items to project scope"
|
|
15420
15487
|
}
|
|
15421
15488
|
],
|
|
15422
15489
|
"data": {
|
|
15423
15490
|
"entity_types": [
|
|
15424
15491
|
{
|
|
15425
|
-
"type": "
|
|
15492
|
+
"type": "risk_register",
|
|
15426
15493
|
"role": "item"
|
|
15427
15494
|
},
|
|
15428
15495
|
{
|
|
15429
|
-
"type": "
|
|
15430
|
-
"role": "
|
|
15496
|
+
"type": "risk",
|
|
15497
|
+
"role": "scored_item"
|
|
15431
15498
|
},
|
|
15432
15499
|
{
|
|
15433
|
-
"type": "
|
|
15500
|
+
"type": "dependency",
|
|
15434
15501
|
"role": "item"
|
|
15435
15502
|
},
|
|
15436
15503
|
{
|
|
15437
|
-
"type": "
|
|
15504
|
+
"type": "status_report",
|
|
15505
|
+
"role": "item"
|
|
15506
|
+
},
|
|
15507
|
+
{
|
|
15508
|
+
"type": "deliverable",
|
|
15438
15509
|
"role": "item"
|
|
15439
15510
|
}
|
|
15440
15511
|
],
|
|
15441
|
-
"required_properties": {
|
|
15512
|
+
"required_properties": {
|
|
15513
|
+
"risk": [
|
|
15514
|
+
{
|
|
15515
|
+
"property": "probability",
|
|
15516
|
+
"type": "number",
|
|
15517
|
+
"required": true,
|
|
15518
|
+
"label": "Probability",
|
|
15519
|
+
"description": "Likelihood the risk materialises (risk.probability assessment)"
|
|
15520
|
+
},
|
|
15521
|
+
{
|
|
15522
|
+
"property": "impact",
|
|
15523
|
+
"type": "number",
|
|
15524
|
+
"required": true,
|
|
15525
|
+
"label": "Impact",
|
|
15526
|
+
"description": "Consequence severity if the risk materialises (risk.impact assessment)"
|
|
15527
|
+
}
|
|
15528
|
+
]
|
|
15529
|
+
},
|
|
15530
|
+
"computed_properties": [
|
|
15531
|
+
{
|
|
15532
|
+
"property": "severity",
|
|
15533
|
+
"expression": "probability * impact",
|
|
15534
|
+
"entity_type": "risk",
|
|
15535
|
+
"label": "Severity",
|
|
15536
|
+
"format": "number"
|
|
15537
|
+
}
|
|
15538
|
+
],
|
|
15539
|
+
"scoring_lens": {
|
|
15540
|
+
"applies_to": [
|
|
15541
|
+
"risk"
|
|
15542
|
+
],
|
|
15543
|
+
"inputs": [
|
|
15544
|
+
{
|
|
15545
|
+
"property": "probability",
|
|
15546
|
+
"type": "number",
|
|
15547
|
+
"required": true,
|
|
15548
|
+
"label": "Probability",
|
|
15549
|
+
"description": "Likelihood the risk materialises (risk.probability assessment)"
|
|
15550
|
+
},
|
|
15551
|
+
{
|
|
15552
|
+
"property": "impact",
|
|
15553
|
+
"type": "number",
|
|
15554
|
+
"required": true,
|
|
15555
|
+
"label": "Impact",
|
|
15556
|
+
"description": "Consequence severity if the risk materialises (risk.impact assessment)"
|
|
15557
|
+
}
|
|
15558
|
+
],
|
|
15559
|
+
"computed": [
|
|
15560
|
+
{
|
|
15561
|
+
"property": "severity",
|
|
15562
|
+
"expression": "probability * impact",
|
|
15563
|
+
"label": "Severity",
|
|
15564
|
+
"format": "number"
|
|
15565
|
+
}
|
|
15566
|
+
]
|
|
15567
|
+
}
|
|
15442
15568
|
},
|
|
15443
15569
|
"structure": {
|
|
15444
|
-
"pattern": "
|
|
15570
|
+
"pattern": "table"
|
|
15445
15571
|
},
|
|
15446
15572
|
"presentation": {
|
|
15447
15573
|
"layout": {
|
|
15448
|
-
"type": "
|
|
15449
|
-
"
|
|
15450
|
-
|
|
15574
|
+
"type": "table",
|
|
15575
|
+
"columns": [
|
|
15576
|
+
{
|
|
15577
|
+
"property": "title",
|
|
15578
|
+
"label": "Item",
|
|
15579
|
+
"sortable": true
|
|
15580
|
+
},
|
|
15581
|
+
{
|
|
15582
|
+
"property": "description",
|
|
15583
|
+
"label": "Category"
|
|
15584
|
+
},
|
|
15585
|
+
{
|
|
15586
|
+
"property": "status",
|
|
15587
|
+
"label": "Status"
|
|
15588
|
+
},
|
|
15589
|
+
{
|
|
15590
|
+
"property": "description",
|
|
15591
|
+
"label": "Owner / Action"
|
|
15592
|
+
}
|
|
15593
|
+
]
|
|
15594
|
+
},
|
|
15595
|
+
"sort_by": {
|
|
15596
|
+
"property": "title",
|
|
15597
|
+
"direction": "asc"
|
|
15451
15598
|
},
|
|
15452
15599
|
"colour_by": "type",
|
|
15453
|
-
"collapsible": true,
|
|
15454
15600
|
"card_fields": [
|
|
15455
15601
|
"title",
|
|
15456
15602
|
"description",
|
|
@@ -15458,82 +15604,244 @@ var UPG_FRAMEWORKS = [
|
|
|
15458
15604
|
]
|
|
15459
15605
|
},
|
|
15460
15606
|
"education": {
|
|
15461
|
-
"purpose": "
|
|
15462
|
-
"core_question": "What are the
|
|
15607
|
+
"purpose": "Track Risks, Assumptions, Issues, and Dependencies in a single living document so the project manager sees all potential blockers in one place.",
|
|
15608
|
+
"core_question": "What are the current risks, untested assumptions, open issues, and external dependencies that could derail this programme?",
|
|
15463
15609
|
"when_to_use": [
|
|
15464
|
-
"You
|
|
15465
|
-
"
|
|
15466
|
-
"
|
|
15610
|
+
"You are managing complex, multi-team initiatives with dependencies",
|
|
15611
|
+
"You need to track risks, decisions, and milestones across workstreams",
|
|
15612
|
+
"Stakeholders need visibility into programme-level progress"
|
|
15467
15613
|
],
|
|
15468
15614
|
"when_not_to_use": [
|
|
15469
|
-
"The
|
|
15470
|
-
"
|
|
15615
|
+
"The project is small enough for a single team to manage",
|
|
15616
|
+
"Formal programme management would create overhead without value"
|
|
15471
15617
|
]
|
|
15472
15618
|
}
|
|
15473
15619
|
},
|
|
15474
15620
|
{
|
|
15475
|
-
"id": "
|
|
15621
|
+
"id": "ice-scoring",
|
|
15476
15622
|
"approach_ids": [
|
|
15477
|
-
"
|
|
15623
|
+
"prioritise"
|
|
15478
15624
|
],
|
|
15479
|
-
"name": "
|
|
15625
|
+
"name": "ICE Scoring",
|
|
15480
15626
|
"version": "1.0.0",
|
|
15481
|
-
"description": "
|
|
15482
|
-
"category": "
|
|
15627
|
+
"description": "Rate ideas by Impact, Confidence, and Ease on a 1-10 scale. Multiply for a composite score. Fast and lightweight.",
|
|
15628
|
+
"category": "prioritization",
|
|
15483
15629
|
"origin": {
|
|
15484
15630
|
"type": "practitioner",
|
|
15485
|
-
"attribution": "
|
|
15486
|
-
"description": "
|
|
15487
|
-
"
|
|
15488
|
-
"year": 2014,
|
|
15631
|
+
"attribution": "Sean Ellis",
|
|
15632
|
+
"description": "Created by Sean Ellis as a lightweight growth experiment scoring method. Widely adopted in growth teams.",
|
|
15633
|
+
"year": 2010,
|
|
15489
15634
|
"license": "open_attribution"
|
|
15490
15635
|
},
|
|
15491
15636
|
"tags": [
|
|
15492
|
-
"
|
|
15637
|
+
"prioritization",
|
|
15493
15638
|
"table"
|
|
15494
15639
|
],
|
|
15495
15640
|
"slots": [
|
|
15496
15641
|
{
|
|
15497
|
-
"label": "
|
|
15498
|
-
"
|
|
15499
|
-
"
|
|
15642
|
+
"label": "Items to score",
|
|
15643
|
+
"role": "candidate",
|
|
15644
|
+
"entityTypeId": "feature",
|
|
15645
|
+
"description": "Features or experiments being evaluated"
|
|
15500
15646
|
},
|
|
15501
15647
|
{
|
|
15502
|
-
"label": "
|
|
15503
|
-
"
|
|
15504
|
-
"
|
|
15648
|
+
"label": "Impact",
|
|
15649
|
+
"role": "impact",
|
|
15650
|
+
"entityTypeId": "outcome",
|
|
15651
|
+
"description": "How much will this move the needle?"
|
|
15505
15652
|
},
|
|
15506
15653
|
{
|
|
15507
|
-
"label": "
|
|
15508
|
-
"
|
|
15509
|
-
"
|
|
15654
|
+
"label": "Confidence",
|
|
15655
|
+
"role": "confidence",
|
|
15656
|
+
"entityTypeId": "assumption",
|
|
15657
|
+
"description": "How sure are we about the impact?"
|
|
15658
|
+
},
|
|
15659
|
+
{
|
|
15660
|
+
"label": "Ease",
|
|
15661
|
+
"role": "ease",
|
|
15662
|
+
"entityTypeId": "feature",
|
|
15663
|
+
"description": "How easy is this to implement?"
|
|
15664
|
+
},
|
|
15665
|
+
{
|
|
15666
|
+
"label": "Opportunities to score",
|
|
15667
|
+
"role": "candidate",
|
|
15668
|
+
"entityTypeId": "opportunity",
|
|
15669
|
+
"description": "Opportunities scored on the same ICE Scoring inputs as features."
|
|
15670
|
+
},
|
|
15671
|
+
{
|
|
15672
|
+
"label": "Needs to score",
|
|
15673
|
+
"role": "candidate",
|
|
15674
|
+
"entityTypeId": "need",
|
|
15675
|
+
"description": "Needs scored on the same ICE Scoring inputs as features."
|
|
15676
|
+
}
|
|
15677
|
+
],
|
|
15678
|
+
"data": {
|
|
15679
|
+
"entity_types": [
|
|
15680
|
+
{
|
|
15681
|
+
"type": "feature",
|
|
15682
|
+
"role": "scored_item"
|
|
15683
|
+
},
|
|
15684
|
+
{
|
|
15685
|
+
"type": "outcome",
|
|
15686
|
+
"role": "item"
|
|
15687
|
+
},
|
|
15688
|
+
{
|
|
15689
|
+
"type": "assumption",
|
|
15690
|
+
"role": "item"
|
|
15691
|
+
},
|
|
15692
|
+
{
|
|
15693
|
+
"type": "opportunity",
|
|
15694
|
+
"role": "scored_item"
|
|
15695
|
+
},
|
|
15696
|
+
{
|
|
15697
|
+
"type": "need",
|
|
15698
|
+
"role": "scored_item"
|
|
15699
|
+
}
|
|
15700
|
+
],
|
|
15701
|
+
"required_properties": {
|
|
15702
|
+
"feature": [
|
|
15703
|
+
{
|
|
15704
|
+
"property": "impact",
|
|
15705
|
+
"type": "number",
|
|
15706
|
+
"required": true,
|
|
15707
|
+
"scope": "framework",
|
|
15708
|
+
"label": "Impact",
|
|
15709
|
+
"description": "Expected impact on the target metric (1-10)"
|
|
15710
|
+
},
|
|
15711
|
+
{
|
|
15712
|
+
"property": "confidence",
|
|
15713
|
+
"type": "number",
|
|
15714
|
+
"required": true,
|
|
15715
|
+
"scope": "framework",
|
|
15716
|
+
"label": "Confidence",
|
|
15717
|
+
"description": "Confidence in the impact estimate (1-10)"
|
|
15718
|
+
},
|
|
15719
|
+
{
|
|
15720
|
+
"property": "ease",
|
|
15721
|
+
"type": "number",
|
|
15722
|
+
"required": true,
|
|
15723
|
+
"scope": "framework",
|
|
15724
|
+
"label": "Ease",
|
|
15725
|
+
"description": "Ease of implementation (1-10)"
|
|
15726
|
+
}
|
|
15727
|
+
],
|
|
15728
|
+
"opportunity": [
|
|
15729
|
+
{
|
|
15730
|
+
"property": "impact",
|
|
15731
|
+
"type": "number",
|
|
15732
|
+
"required": true,
|
|
15733
|
+
"scope": "framework",
|
|
15734
|
+
"label": "Impact",
|
|
15735
|
+
"description": "Expected impact on the target metric (1-10)"
|
|
15736
|
+
},
|
|
15737
|
+
{
|
|
15738
|
+
"property": "confidence",
|
|
15739
|
+
"type": "number",
|
|
15740
|
+
"required": true,
|
|
15741
|
+
"scope": "framework",
|
|
15742
|
+
"label": "Confidence",
|
|
15743
|
+
"description": "Confidence in the impact estimate (1-10)"
|
|
15744
|
+
},
|
|
15745
|
+
{
|
|
15746
|
+
"property": "ease",
|
|
15747
|
+
"type": "number",
|
|
15748
|
+
"required": true,
|
|
15749
|
+
"scope": "framework",
|
|
15750
|
+
"label": "Ease",
|
|
15751
|
+
"description": "Ease of implementation (1-10)"
|
|
15752
|
+
}
|
|
15753
|
+
],
|
|
15754
|
+
"need": [
|
|
15755
|
+
{
|
|
15756
|
+
"property": "impact",
|
|
15757
|
+
"type": "number",
|
|
15758
|
+
"required": true,
|
|
15759
|
+
"scope": "framework",
|
|
15760
|
+
"label": "Impact",
|
|
15761
|
+
"description": "Expected impact on the target metric (1-10)"
|
|
15762
|
+
},
|
|
15763
|
+
{
|
|
15764
|
+
"property": "confidence",
|
|
15765
|
+
"type": "number",
|
|
15766
|
+
"required": true,
|
|
15767
|
+
"scope": "framework",
|
|
15768
|
+
"label": "Confidence",
|
|
15769
|
+
"description": "Confidence in the impact estimate (1-10)"
|
|
15770
|
+
},
|
|
15771
|
+
{
|
|
15772
|
+
"property": "ease",
|
|
15773
|
+
"type": "number",
|
|
15774
|
+
"required": true,
|
|
15775
|
+
"scope": "framework",
|
|
15776
|
+
"label": "Ease",
|
|
15777
|
+
"description": "Ease of implementation (1-10)"
|
|
15778
|
+
}
|
|
15779
|
+
]
|
|
15510
15780
|
},
|
|
15511
|
-
|
|
15512
|
-
"label": "Team OKR",
|
|
15513
|
-
"entityTypeId": "team_okr",
|
|
15514
|
-
"description": "Team OKR or improvement goal derived from consistently red health check dimensions"
|
|
15515
|
-
}
|
|
15516
|
-
],
|
|
15517
|
-
"data": {
|
|
15518
|
-
"entity_types": [
|
|
15519
|
-
{
|
|
15520
|
-
"type": "team",
|
|
15521
|
-
"role": "item"
|
|
15522
|
-
},
|
|
15781
|
+
"computed_properties": [
|
|
15523
15782
|
{
|
|
15524
|
-
"
|
|
15525
|
-
"
|
|
15783
|
+
"property": "ice_score",
|
|
15784
|
+
"expression": "impact * confidence * ease",
|
|
15785
|
+
"entity_type": "feature",
|
|
15786
|
+
"label": "ICE Score",
|
|
15787
|
+
"format": "number"
|
|
15526
15788
|
},
|
|
15527
15789
|
{
|
|
15528
|
-
"
|
|
15529
|
-
"
|
|
15790
|
+
"property": "ice_score",
|
|
15791
|
+
"expression": "impact * confidence * ease",
|
|
15792
|
+
"entity_type": "opportunity",
|
|
15793
|
+
"label": "ICE Score",
|
|
15794
|
+
"format": "number"
|
|
15530
15795
|
},
|
|
15531
15796
|
{
|
|
15532
|
-
"
|
|
15533
|
-
"
|
|
15797
|
+
"property": "ice_score",
|
|
15798
|
+
"expression": "impact * confidence * ease",
|
|
15799
|
+
"entity_type": "need",
|
|
15800
|
+
"label": "ICE Score",
|
|
15801
|
+
"format": "number"
|
|
15534
15802
|
}
|
|
15535
15803
|
],
|
|
15536
|
-
"
|
|
15804
|
+
"scoring_lens": {
|
|
15805
|
+
"applies_to": [
|
|
15806
|
+
"feature",
|
|
15807
|
+
"opportunity",
|
|
15808
|
+
"need"
|
|
15809
|
+
],
|
|
15810
|
+
"inputs": [
|
|
15811
|
+
{
|
|
15812
|
+
"property": "impact",
|
|
15813
|
+
"type": "number",
|
|
15814
|
+
"required": true,
|
|
15815
|
+
"scope": "framework",
|
|
15816
|
+
"label": "Impact",
|
|
15817
|
+
"description": "Expected impact on the target metric (1-10)"
|
|
15818
|
+
},
|
|
15819
|
+
{
|
|
15820
|
+
"property": "confidence",
|
|
15821
|
+
"type": "number",
|
|
15822
|
+
"required": true,
|
|
15823
|
+
"scope": "framework",
|
|
15824
|
+
"label": "Confidence",
|
|
15825
|
+
"description": "Confidence in the impact estimate (1-10)"
|
|
15826
|
+
},
|
|
15827
|
+
{
|
|
15828
|
+
"property": "ease",
|
|
15829
|
+
"type": "number",
|
|
15830
|
+
"required": true,
|
|
15831
|
+
"scope": "framework",
|
|
15832
|
+
"label": "Ease",
|
|
15833
|
+
"description": "Ease of implementation (1-10)"
|
|
15834
|
+
}
|
|
15835
|
+
],
|
|
15836
|
+
"computed": [
|
|
15837
|
+
{
|
|
15838
|
+
"property": "ice_score",
|
|
15839
|
+
"expression": "impact * confidence * ease",
|
|
15840
|
+
"label": "ICE Score",
|
|
15841
|
+
"format": "number"
|
|
15842
|
+
}
|
|
15843
|
+
]
|
|
15844
|
+
}
|
|
15537
15845
|
},
|
|
15538
15846
|
"structure": {
|
|
15539
15847
|
"pattern": "table"
|
|
@@ -15544,21 +15852,22 @@ var UPG_FRAMEWORKS = [
|
|
|
15544
15852
|
"columns": [
|
|
15545
15853
|
{
|
|
15546
15854
|
"property": "title",
|
|
15547
|
-
"label": "
|
|
15855
|
+
"label": "Items to score",
|
|
15548
15856
|
"sortable": true
|
|
15549
15857
|
},
|
|
15550
15858
|
{
|
|
15551
|
-
"property": "
|
|
15552
|
-
"label": "
|
|
15859
|
+
"property": "impact",
|
|
15860
|
+
"label": "Impact",
|
|
15553
15861
|
"sortable": true
|
|
15554
15862
|
},
|
|
15555
15863
|
{
|
|
15556
|
-
"property": "
|
|
15557
|
-
"label": "
|
|
15864
|
+
"property": "confidence",
|
|
15865
|
+
"label": "Confidence",
|
|
15866
|
+
"sortable": true
|
|
15558
15867
|
},
|
|
15559
15868
|
{
|
|
15560
|
-
"property": "
|
|
15561
|
-
"label": "
|
|
15869
|
+
"property": "ease",
|
|
15870
|
+
"label": "Ease",
|
|
15562
15871
|
"sortable": true
|
|
15563
15872
|
}
|
|
15564
15873
|
]
|
|
@@ -15575,114 +15884,227 @@ var UPG_FRAMEWORKS = [
|
|
|
15575
15884
|
]
|
|
15576
15885
|
},
|
|
15577
15886
|
"education": {
|
|
15578
|
-
"purpose": "
|
|
15579
|
-
"core_question": "
|
|
15887
|
+
"purpose": "Provide a lightweight scoring model for early-stage ideas when detailed effort estimates are unavailable. Faster than RICE and useful for brainstorm triage.",
|
|
15888
|
+
"core_question": "Which ideas should we investigate further based on their potential impact, confidence in our assumptions, and implementation ease?",
|
|
15580
15889
|
"when_to_use": [
|
|
15581
|
-
"You
|
|
15582
|
-
"
|
|
15583
|
-
"You
|
|
15890
|
+
"You have more ideas or features than capacity to build them",
|
|
15891
|
+
"Stakeholders disagree on what to build next",
|
|
15892
|
+
"You need a transparent, defensible prioritisation process"
|
|
15584
15893
|
],
|
|
15585
15894
|
"when_not_to_use": [
|
|
15586
|
-
"
|
|
15587
|
-
"
|
|
15895
|
+
"You have a single obvious next step with no contention",
|
|
15896
|
+
"The backlog is small enough to sequence intuitively"
|
|
15588
15897
|
]
|
|
15589
15898
|
}
|
|
15590
15899
|
},
|
|
15591
15900
|
{
|
|
15592
|
-
"id": "
|
|
15901
|
+
"id": "wsjf",
|
|
15593
15902
|
"approach_ids": [
|
|
15594
|
-
"
|
|
15903
|
+
"prioritise"
|
|
15595
15904
|
],
|
|
15596
|
-
"name": "
|
|
15905
|
+
"name": "WSJF (Weighted Shortest Job First)",
|
|
15597
15906
|
"version": "1.0.0",
|
|
15598
|
-
"description": "
|
|
15599
|
-
"category": "
|
|
15907
|
+
"description": "Prioritise work by dividing Cost of Delay (user value + time criticality + risk reduction) by job duration to maximise economic throughput.",
|
|
15908
|
+
"category": "planning",
|
|
15600
15909
|
"origin": {
|
|
15601
15910
|
"type": "practitioner",
|
|
15602
|
-
"
|
|
15603
|
-
"
|
|
15911
|
+
"attribution": "Reinertsen / SAFe",
|
|
15912
|
+
"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.",
|
|
15913
|
+
"url": "https://www.scaledagileframework.com/wsjf/",
|
|
15914
|
+
"year": 2011,
|
|
15915
|
+
"license": "open_attribution"
|
|
15604
15916
|
},
|
|
15605
15917
|
"tags": [
|
|
15606
|
-
"
|
|
15918
|
+
"planning",
|
|
15607
15919
|
"table"
|
|
15608
15920
|
],
|
|
15609
15921
|
"slots": [
|
|
15610
15922
|
{
|
|
15611
|
-
"label": "
|
|
15612
|
-
"
|
|
15613
|
-
"
|
|
15923
|
+
"label": "Backlog Items",
|
|
15924
|
+
"role": "candidate",
|
|
15925
|
+
"entityTypeId": "feature",
|
|
15926
|
+
"description": "Backlog Items: feature entries to evaluate"
|
|
15614
15927
|
},
|
|
15615
15928
|
{
|
|
15616
|
-
"label": "
|
|
15617
|
-
"
|
|
15618
|
-
"
|
|
15929
|
+
"label": "User/Business Value",
|
|
15930
|
+
"role": "user_business_value",
|
|
15931
|
+
"entityTypeId": "metric",
|
|
15932
|
+
"description": "User/Business Value: metric entries to evaluate"
|
|
15619
15933
|
},
|
|
15620
15934
|
{
|
|
15621
|
-
"label": "
|
|
15622
|
-
"
|
|
15623
|
-
"
|
|
15935
|
+
"label": "Time Criticality",
|
|
15936
|
+
"role": "time_criticality",
|
|
15937
|
+
"entityTypeId": "metric",
|
|
15938
|
+
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
15624
15939
|
},
|
|
15625
15940
|
{
|
|
15626
|
-
"label": "
|
|
15627
|
-
"
|
|
15628
|
-
"
|
|
15941
|
+
"label": "Risk Reduction / Opportunity Enablement",
|
|
15942
|
+
"role": "risk_reduction",
|
|
15943
|
+
"entityTypeId": "metric",
|
|
15944
|
+
"description": "Risk Reduction / Opportunity Enablement: metric entries to evaluate"
|
|
15945
|
+
},
|
|
15946
|
+
{
|
|
15947
|
+
"label": "Job Size",
|
|
15948
|
+
"role": "job_size",
|
|
15949
|
+
"entityTypeId": "metric",
|
|
15950
|
+
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
15951
|
+
},
|
|
15952
|
+
{
|
|
15953
|
+
"label": "Opportunities to score",
|
|
15954
|
+
"role": "candidate",
|
|
15955
|
+
"entityTypeId": "opportunity",
|
|
15956
|
+
"description": "Opportunities scored on the same WSJF (Weighted Shortest Job First) inputs as features."
|
|
15957
|
+
}
|
|
15958
|
+
],
|
|
15959
|
+
"data": {
|
|
15960
|
+
"entity_types": [
|
|
15961
|
+
{
|
|
15962
|
+
"type": "feature",
|
|
15963
|
+
"role": "scored_item"
|
|
15964
|
+
},
|
|
15965
|
+
{
|
|
15966
|
+
"type": "metric",
|
|
15967
|
+
"role": "item"
|
|
15968
|
+
},
|
|
15969
|
+
{
|
|
15970
|
+
"type": "opportunity",
|
|
15971
|
+
"role": "scored_item"
|
|
15972
|
+
}
|
|
15973
|
+
],
|
|
15974
|
+
"required_properties": {
|
|
15975
|
+
"feature": [
|
|
15976
|
+
{
|
|
15977
|
+
"property": "user_value",
|
|
15978
|
+
"type": "number",
|
|
15979
|
+
"required": true,
|
|
15980
|
+
"scope": "framework",
|
|
15981
|
+
"label": "User/Business Value",
|
|
15982
|
+
"description": "Relative value to users and the business if delivered"
|
|
15983
|
+
},
|
|
15984
|
+
{
|
|
15985
|
+
"property": "time_criticality",
|
|
15986
|
+
"type": "number",
|
|
15987
|
+
"required": true,
|
|
15988
|
+
"scope": "framework",
|
|
15989
|
+
"label": "Time Criticality",
|
|
15990
|
+
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
15991
|
+
},
|
|
15992
|
+
{
|
|
15993
|
+
"property": "risk_reduction",
|
|
15994
|
+
"type": "number",
|
|
15995
|
+
"required": true,
|
|
15996
|
+
"scope": "framework",
|
|
15997
|
+
"label": "Risk Reduction / Opportunity Enablement",
|
|
15998
|
+
"description": "Value from reducing risk or enabling future opportunities"
|
|
15999
|
+
},
|
|
16000
|
+
{
|
|
16001
|
+
"property": "job_size",
|
|
16002
|
+
"type": "number",
|
|
16003
|
+
"required": true,
|
|
16004
|
+
"scope": "framework",
|
|
16005
|
+
"label": "Job Size",
|
|
16006
|
+
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
16007
|
+
}
|
|
16008
|
+
],
|
|
16009
|
+
"opportunity": [
|
|
16010
|
+
{
|
|
16011
|
+
"property": "user_value",
|
|
16012
|
+
"type": "number",
|
|
16013
|
+
"required": true,
|
|
16014
|
+
"scope": "framework",
|
|
16015
|
+
"label": "User/Business Value",
|
|
16016
|
+
"description": "Relative value to users and the business if delivered"
|
|
16017
|
+
},
|
|
16018
|
+
{
|
|
16019
|
+
"property": "time_criticality",
|
|
16020
|
+
"type": "number",
|
|
16021
|
+
"required": true,
|
|
16022
|
+
"scope": "framework",
|
|
16023
|
+
"label": "Time Criticality",
|
|
16024
|
+
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
16025
|
+
},
|
|
16026
|
+
{
|
|
16027
|
+
"property": "risk_reduction",
|
|
16028
|
+
"type": "number",
|
|
16029
|
+
"required": true,
|
|
16030
|
+
"scope": "framework",
|
|
16031
|
+
"label": "Risk Reduction / Opportunity Enablement",
|
|
16032
|
+
"description": "Value from reducing risk or enabling future opportunities"
|
|
16033
|
+
},
|
|
16034
|
+
{
|
|
16035
|
+
"property": "job_size",
|
|
16036
|
+
"type": "number",
|
|
16037
|
+
"required": true,
|
|
16038
|
+
"scope": "framework",
|
|
16039
|
+
"label": "Job Size",
|
|
16040
|
+
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
16041
|
+
}
|
|
16042
|
+
]
|
|
15629
16043
|
},
|
|
15630
|
-
|
|
15631
|
-
"label": "Deliverable",
|
|
15632
|
-
"entityTypeId": "deliverable",
|
|
15633
|
-
"description": "Deliverable affected by logged risks, issues, or dependencies, linking RAID items to project scope"
|
|
15634
|
-
}
|
|
15635
|
-
],
|
|
15636
|
-
"data": {
|
|
15637
|
-
"entity_types": [
|
|
15638
|
-
{
|
|
15639
|
-
"type": "risk_register",
|
|
15640
|
-
"role": "item"
|
|
15641
|
-
},
|
|
15642
|
-
{
|
|
15643
|
-
"type": "risk",
|
|
15644
|
-
"role": "scored_item"
|
|
15645
|
-
},
|
|
15646
|
-
{
|
|
15647
|
-
"type": "dependency",
|
|
15648
|
-
"role": "item"
|
|
15649
|
-
},
|
|
16044
|
+
"computed_properties": [
|
|
15650
16045
|
{
|
|
15651
|
-
"
|
|
15652
|
-
"
|
|
16046
|
+
"property": "wsjf_score",
|
|
16047
|
+
"expression": "(user_value + time_criticality + risk_reduction) / job_size",
|
|
16048
|
+
"entity_type": "feature",
|
|
16049
|
+
"label": "WSJF Score",
|
|
16050
|
+
"format": "number"
|
|
15653
16051
|
},
|
|
15654
16052
|
{
|
|
15655
|
-
"
|
|
15656
|
-
"
|
|
16053
|
+
"property": "wsjf_score",
|
|
16054
|
+
"expression": "(user_value + time_criticality + risk_reduction) / job_size",
|
|
16055
|
+
"entity_type": "opportunity",
|
|
16056
|
+
"label": "WSJF Score",
|
|
16057
|
+
"format": "number"
|
|
15657
16058
|
}
|
|
15658
16059
|
],
|
|
15659
|
-
"
|
|
15660
|
-
"
|
|
16060
|
+
"scoring_lens": {
|
|
16061
|
+
"applies_to": [
|
|
16062
|
+
"feature",
|
|
16063
|
+
"opportunity"
|
|
16064
|
+
],
|
|
16065
|
+
"inputs": [
|
|
15661
16066
|
{
|
|
15662
|
-
"property": "
|
|
16067
|
+
"property": "user_value",
|
|
15663
16068
|
"type": "number",
|
|
15664
16069
|
"required": true,
|
|
15665
|
-
"
|
|
15666
|
-
"
|
|
16070
|
+
"scope": "framework",
|
|
16071
|
+
"label": "User/Business Value",
|
|
16072
|
+
"description": "Relative value to users and the business if delivered"
|
|
15667
16073
|
},
|
|
15668
16074
|
{
|
|
15669
|
-
"property": "
|
|
16075
|
+
"property": "time_criticality",
|
|
15670
16076
|
"type": "number",
|
|
15671
16077
|
"required": true,
|
|
15672
|
-
"
|
|
15673
|
-
"
|
|
16078
|
+
"scope": "framework",
|
|
16079
|
+
"label": "Time Criticality",
|
|
16080
|
+
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
16081
|
+
},
|
|
16082
|
+
{
|
|
16083
|
+
"property": "risk_reduction",
|
|
16084
|
+
"type": "number",
|
|
16085
|
+
"required": true,
|
|
16086
|
+
"scope": "framework",
|
|
16087
|
+
"label": "Risk Reduction / Opportunity Enablement",
|
|
16088
|
+
"description": "Value from reducing risk or enabling future opportunities"
|
|
16089
|
+
},
|
|
16090
|
+
{
|
|
16091
|
+
"property": "job_size",
|
|
16092
|
+
"type": "number",
|
|
16093
|
+
"required": true,
|
|
16094
|
+
"scope": "framework",
|
|
16095
|
+
"label": "Job Size",
|
|
16096
|
+
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
16097
|
+
}
|
|
16098
|
+
],
|
|
16099
|
+
"computed": [
|
|
16100
|
+
{
|
|
16101
|
+
"property": "wsjf_score",
|
|
16102
|
+
"expression": "(user_value + time_criticality + risk_reduction) / job_size",
|
|
16103
|
+
"label": "WSJF Score",
|
|
16104
|
+
"format": "number"
|
|
15674
16105
|
}
|
|
15675
16106
|
]
|
|
15676
|
-
}
|
|
15677
|
-
"computed_properties": [
|
|
15678
|
-
{
|
|
15679
|
-
"property": "severity",
|
|
15680
|
-
"expression": "probability * impact",
|
|
15681
|
-
"entity_type": "risk",
|
|
15682
|
-
"label": "Severity",
|
|
15683
|
-
"format": "number"
|
|
15684
|
-
}
|
|
15685
|
-
]
|
|
16107
|
+
}
|
|
15686
16108
|
},
|
|
15687
16109
|
"structure": {
|
|
15688
16110
|
"pattern": "table"
|
|
@@ -15693,20 +16115,33 @@ var UPG_FRAMEWORKS = [
|
|
|
15693
16115
|
"columns": [
|
|
15694
16116
|
{
|
|
15695
16117
|
"property": "title",
|
|
15696
|
-
"label": "
|
|
16118
|
+
"label": "Backlog Items",
|
|
15697
16119
|
"sortable": true
|
|
15698
16120
|
},
|
|
15699
16121
|
{
|
|
15700
|
-
"property": "
|
|
15701
|
-
"label": "
|
|
16122
|
+
"property": "user_value",
|
|
16123
|
+
"label": "User/Business Value",
|
|
16124
|
+
"sortable": true
|
|
15702
16125
|
},
|
|
15703
16126
|
{
|
|
15704
|
-
"property": "
|
|
15705
|
-
"label": "
|
|
16127
|
+
"property": "time_criticality",
|
|
16128
|
+
"label": "Time Criticality",
|
|
16129
|
+
"sortable": true
|
|
15706
16130
|
},
|
|
15707
16131
|
{
|
|
15708
|
-
"property": "
|
|
15709
|
-
"label": "
|
|
16132
|
+
"property": "risk_reduction",
|
|
16133
|
+
"label": "Risk Reduction / Opportunity Enablement",
|
|
16134
|
+
"sortable": true
|
|
16135
|
+
},
|
|
16136
|
+
{
|
|
16137
|
+
"property": "job_size",
|
|
16138
|
+
"label": "Job Size",
|
|
16139
|
+
"sortable": true
|
|
16140
|
+
},
|
|
16141
|
+
{
|
|
16142
|
+
"property": "wsjf_score",
|
|
16143
|
+
"label": "WSJF Score",
|
|
16144
|
+
"sortable": true
|
|
15710
16145
|
}
|
|
15711
16146
|
]
|
|
15712
16147
|
},
|
|
@@ -15722,34 +16157,34 @@ var UPG_FRAMEWORKS = [
|
|
|
15722
16157
|
]
|
|
15723
16158
|
},
|
|
15724
16159
|
"education": {
|
|
15725
|
-
"purpose": "
|
|
15726
|
-
"core_question": "
|
|
16160
|
+
"purpose": "Prioritise work by dividing the Cost of Delay by job duration, ensuring the most time-sensitive, valuable items are done first.",
|
|
16161
|
+
"core_question": "Considering the cost of waiting, which items should we start now to maximise economic benefit?",
|
|
15727
16162
|
"when_to_use": [
|
|
15728
|
-
"You
|
|
15729
|
-
"
|
|
15730
|
-
"
|
|
16163
|
+
"You need to coordinate work across multiple teams or time horizons",
|
|
16164
|
+
"Stakeholders need visibility into what is coming and when",
|
|
16165
|
+
"You want to balance commitments with flexibility"
|
|
15731
16166
|
],
|
|
15732
16167
|
"when_not_to_use": [
|
|
15733
|
-
"The
|
|
15734
|
-
"
|
|
16168
|
+
"The team is small enough that informal coordination works",
|
|
16169
|
+
"Plans would create false precision about uncertain outcomes"
|
|
15735
16170
|
]
|
|
15736
16171
|
}
|
|
15737
16172
|
},
|
|
15738
16173
|
{
|
|
15739
|
-
"id": "
|
|
16174
|
+
"id": "cost-of-delay",
|
|
15740
16175
|
"approach_ids": [
|
|
15741
16176
|
"prioritise"
|
|
15742
16177
|
],
|
|
15743
|
-
"name": "
|
|
16178
|
+
"name": "Cost of Delay",
|
|
15744
16179
|
"version": "1.0.0",
|
|
15745
|
-
"description": "
|
|
16180
|
+
"description": "Quantify the economic cost of not shipping a feature or opportunity to drive priority decisions. Combines urgency with value.",
|
|
15746
16181
|
"category": "prioritization",
|
|
15747
16182
|
"origin": {
|
|
15748
16183
|
"type": "practitioner",
|
|
15749
|
-
"attribution": "
|
|
15750
|
-
"description": "
|
|
15751
|
-
"year":
|
|
15752
|
-
"license": "
|
|
16184
|
+
"attribution": "Don Reinertsen",
|
|
16185
|
+
"description": "Formalised in The Principles of Product Development Flow (Celeritas Publishing). Foundational to lean product economics.",
|
|
16186
|
+
"year": 2009,
|
|
16187
|
+
"license": "public_domain"
|
|
15753
16188
|
},
|
|
15754
16189
|
"tags": [
|
|
15755
16190
|
"prioritization",
|
|
@@ -15757,34 +16192,29 @@ var UPG_FRAMEWORKS = [
|
|
|
15757
16192
|
],
|
|
15758
16193
|
"slots": [
|
|
15759
16194
|
{
|
|
15760
|
-
"label": "Items to
|
|
16195
|
+
"label": "Items to evaluate",
|
|
15761
16196
|
"entityTypeId": "feature",
|
|
15762
|
-
"description": "Features or
|
|
16197
|
+
"description": "Features or initiatives being assessed"
|
|
15763
16198
|
},
|
|
15764
16199
|
{
|
|
15765
|
-
"label": "
|
|
16200
|
+
"label": "User-Business Value",
|
|
15766
16201
|
"entityTypeId": "outcome",
|
|
15767
|
-
"description": "
|
|
16202
|
+
"description": "Revenue, retention, or strategic value"
|
|
15768
16203
|
},
|
|
15769
16204
|
{
|
|
15770
|
-
"label": "
|
|
15771
|
-
"entityTypeId": "
|
|
15772
|
-
"description": "How
|
|
16205
|
+
"label": "Time Criticality",
|
|
16206
|
+
"entityTypeId": "metric",
|
|
16207
|
+
"description": "How much value decays with delay"
|
|
15773
16208
|
},
|
|
15774
16209
|
{
|
|
15775
|
-
"label": "
|
|
15776
|
-
"entityTypeId": "
|
|
15777
|
-
"description": "
|
|
16210
|
+
"label": "Risk Reduction",
|
|
16211
|
+
"entityTypeId": "risk",
|
|
16212
|
+
"description": "What risk does this mitigate?"
|
|
15778
16213
|
},
|
|
15779
16214
|
{
|
|
15780
16215
|
"label": "Opportunities to score",
|
|
15781
16216
|
"entityTypeId": "opportunity",
|
|
15782
|
-
"description": "Opportunities scored on the same
|
|
15783
|
-
},
|
|
15784
|
-
{
|
|
15785
|
-
"label": "Needs to score",
|
|
15786
|
-
"entityTypeId": "need",
|
|
15787
|
-
"description": "Needs scored on the same ICE Scoring inputs as features."
|
|
16217
|
+
"description": "Opportunities scored on the same Cost of Delay inputs as features."
|
|
15788
16218
|
}
|
|
15789
16219
|
],
|
|
15790
16220
|
"data": {
|
|
@@ -15794,743 +16224,567 @@ var UPG_FRAMEWORKS = [
|
|
|
15794
16224
|
"role": "scored_item"
|
|
15795
16225
|
},
|
|
15796
16226
|
{
|
|
15797
|
-
"type": "
|
|
16227
|
+
"type": "metric",
|
|
15798
16228
|
"role": "item"
|
|
15799
16229
|
},
|
|
15800
16230
|
{
|
|
15801
|
-
"type": "
|
|
16231
|
+
"type": "outcome",
|
|
15802
16232
|
"role": "item"
|
|
15803
16233
|
},
|
|
15804
16234
|
{
|
|
15805
|
-
"type": "
|
|
15806
|
-
"role": "
|
|
16235
|
+
"type": "risk",
|
|
16236
|
+
"role": "item"
|
|
15807
16237
|
},
|
|
15808
16238
|
{
|
|
15809
|
-
"type": "
|
|
16239
|
+
"type": "opportunity",
|
|
15810
16240
|
"role": "scored_item"
|
|
15811
16241
|
}
|
|
15812
16242
|
],
|
|
15813
16243
|
"required_properties": {
|
|
15814
16244
|
"feature": [
|
|
15815
16245
|
{
|
|
15816
|
-
"property": "
|
|
15817
|
-
"type": "number",
|
|
15818
|
-
"required": true,
|
|
15819
|
-
"scope": "framework",
|
|
15820
|
-
"label": "Impact",
|
|
15821
|
-
"description": "Expected impact on the target metric (1-10)"
|
|
15822
|
-
},
|
|
15823
|
-
{
|
|
15824
|
-
"property": "confidence",
|
|
15825
|
-
"type": "number",
|
|
15826
|
-
"required": true,
|
|
15827
|
-
"scope": "framework",
|
|
15828
|
-
"label": "Confidence",
|
|
15829
|
-
"description": "Confidence in the impact estimate (1-10)"
|
|
15830
|
-
},
|
|
15831
|
-
{
|
|
15832
|
-
"property": "ease",
|
|
15833
|
-
"type": "number",
|
|
15834
|
-
"required": true,
|
|
15835
|
-
"scope": "framework",
|
|
15836
|
-
"label": "Ease",
|
|
15837
|
-
"description": "Ease of implementation (1-10)"
|
|
15838
|
-
}
|
|
15839
|
-
],
|
|
15840
|
-
"opportunity": [
|
|
15841
|
-
{
|
|
15842
|
-
"property": "impact",
|
|
15843
|
-
"type": "number",
|
|
15844
|
-
"required": true,
|
|
15845
|
-
"scope": "framework",
|
|
15846
|
-
"label": "Impact",
|
|
15847
|
-
"description": "Expected impact on the target metric (1-10)"
|
|
15848
|
-
},
|
|
15849
|
-
{
|
|
15850
|
-
"property": "confidence",
|
|
16246
|
+
"property": "cost_of_delay",
|
|
15851
16247
|
"type": "number",
|
|
15852
16248
|
"required": true,
|
|
15853
16249
|
"scope": "framework",
|
|
15854
|
-
"label": "
|
|
15855
|
-
"description": "
|
|
16250
|
+
"label": "Cost of Delay",
|
|
16251
|
+
"description": "Weekly revenue impact of not shipping"
|
|
15856
16252
|
},
|
|
15857
16253
|
{
|
|
15858
|
-
"property": "
|
|
15859
|
-
"type": "number",
|
|
15860
|
-
"required": true,
|
|
15861
|
-
"scope": "framework",
|
|
15862
|
-
"label": "Ease",
|
|
15863
|
-
"description": "Ease of implementation (1-10)"
|
|
15864
|
-
}
|
|
15865
|
-
],
|
|
15866
|
-
"need": [
|
|
15867
|
-
{
|
|
15868
|
-
"property": "impact",
|
|
16254
|
+
"property": "job_size",
|
|
15869
16255
|
"type": "number",
|
|
15870
16256
|
"required": true,
|
|
15871
16257
|
"scope": "framework",
|
|
15872
|
-
"label": "
|
|
15873
|
-
"description": "
|
|
15874
|
-
}
|
|
16258
|
+
"label": "Job Size",
|
|
16259
|
+
"description": "Weeks of development effort"
|
|
16260
|
+
}
|
|
16261
|
+
],
|
|
16262
|
+
"opportunity": [
|
|
15875
16263
|
{
|
|
15876
|
-
"property": "
|
|
16264
|
+
"property": "cost_of_delay",
|
|
15877
16265
|
"type": "number",
|
|
15878
16266
|
"required": true,
|
|
15879
16267
|
"scope": "framework",
|
|
15880
|
-
"label": "
|
|
15881
|
-
"description": "
|
|
16268
|
+
"label": "Cost of Delay",
|
|
16269
|
+
"description": "Weekly revenue impact of not shipping"
|
|
15882
16270
|
},
|
|
15883
16271
|
{
|
|
15884
|
-
"property": "
|
|
16272
|
+
"property": "job_size",
|
|
15885
16273
|
"type": "number",
|
|
15886
16274
|
"required": true,
|
|
15887
16275
|
"scope": "framework",
|
|
15888
|
-
"label": "
|
|
15889
|
-
"description": "
|
|
16276
|
+
"label": "Job Size",
|
|
16277
|
+
"description": "Weeks of development effort"
|
|
15890
16278
|
}
|
|
15891
16279
|
]
|
|
15892
16280
|
},
|
|
15893
16281
|
"computed_properties": [
|
|
15894
16282
|
{
|
|
15895
|
-
"property": "
|
|
15896
|
-
"expression": "
|
|
16283
|
+
"property": "wsjf_score",
|
|
16284
|
+
"expression": "cost_of_delay / job_size",
|
|
15897
16285
|
"entity_type": "feature",
|
|
15898
|
-
"label": "
|
|
16286
|
+
"label": "WSJF Score",
|
|
15899
16287
|
"format": "number"
|
|
15900
16288
|
},
|
|
15901
16289
|
{
|
|
15902
|
-
"property": "
|
|
15903
|
-
"expression": "
|
|
16290
|
+
"property": "wsjf_score",
|
|
16291
|
+
"expression": "cost_of_delay / job_size",
|
|
15904
16292
|
"entity_type": "opportunity",
|
|
15905
|
-
"label": "
|
|
15906
|
-
"format": "number"
|
|
15907
|
-
},
|
|
15908
|
-
{
|
|
15909
|
-
"property": "ice_score",
|
|
15910
|
-
"expression": "impact * confidence * ease",
|
|
15911
|
-
"entity_type": "need",
|
|
15912
|
-
"label": "ICE Score",
|
|
16293
|
+
"label": "WSJF Score",
|
|
15913
16294
|
"format": "number"
|
|
15914
16295
|
}
|
|
15915
16296
|
],
|
|
15916
16297
|
"scoring_lens": {
|
|
15917
16298
|
"applies_to": [
|
|
15918
16299
|
"feature",
|
|
15919
|
-
"opportunity"
|
|
15920
|
-
"need"
|
|
16300
|
+
"opportunity"
|
|
15921
16301
|
],
|
|
15922
16302
|
"inputs": [
|
|
15923
16303
|
{
|
|
15924
|
-
"property": "
|
|
15925
|
-
"type": "number",
|
|
15926
|
-
"required": true,
|
|
15927
|
-
"scope": "framework",
|
|
15928
|
-
"label": "Impact",
|
|
15929
|
-
"description": "Expected impact on the target metric (1-10)"
|
|
15930
|
-
},
|
|
15931
|
-
{
|
|
15932
|
-
"property": "confidence",
|
|
16304
|
+
"property": "cost_of_delay",
|
|
15933
16305
|
"type": "number",
|
|
15934
16306
|
"required": true,
|
|
15935
16307
|
"scope": "framework",
|
|
15936
|
-
"label": "
|
|
15937
|
-
"description": "
|
|
16308
|
+
"label": "Cost of Delay",
|
|
16309
|
+
"description": "Weekly revenue impact of not shipping"
|
|
15938
16310
|
},
|
|
15939
16311
|
{
|
|
15940
|
-
"property": "
|
|
16312
|
+
"property": "job_size",
|
|
15941
16313
|
"type": "number",
|
|
15942
16314
|
"required": true,
|
|
15943
16315
|
"scope": "framework",
|
|
15944
|
-
"label": "
|
|
15945
|
-
"description": "
|
|
16316
|
+
"label": "Job Size",
|
|
16317
|
+
"description": "Weeks of development effort"
|
|
15946
16318
|
}
|
|
15947
16319
|
],
|
|
15948
16320
|
"computed": [
|
|
15949
16321
|
{
|
|
15950
|
-
"property": "
|
|
15951
|
-
"expression": "
|
|
15952
|
-
"label": "
|
|
16322
|
+
"property": "wsjf_score",
|
|
16323
|
+
"expression": "cost_of_delay / job_size",
|
|
16324
|
+
"label": "WSJF Score",
|
|
15953
16325
|
"format": "number"
|
|
15954
16326
|
}
|
|
15955
16327
|
]
|
|
15956
16328
|
}
|
|
15957
16329
|
},
|
|
15958
16330
|
"structure": {
|
|
15959
|
-
"pattern": "table"
|
|
16331
|
+
"pattern": "table"
|
|
16332
|
+
},
|
|
16333
|
+
"presentation": {
|
|
16334
|
+
"layout": {
|
|
16335
|
+
"type": "table",
|
|
16336
|
+
"columns": [
|
|
16337
|
+
{
|
|
16338
|
+
"property": "title",
|
|
16339
|
+
"label": "Items to evaluate",
|
|
16340
|
+
"sortable": true
|
|
16341
|
+
},
|
|
16342
|
+
{
|
|
16343
|
+
"property": "cost_of_delay",
|
|
16344
|
+
"label": "User-Business Value",
|
|
16345
|
+
"sortable": true
|
|
16346
|
+
},
|
|
16347
|
+
{
|
|
16348
|
+
"property": "job_size",
|
|
16349
|
+
"label": "Job Size",
|
|
16350
|
+
"sortable": true
|
|
16351
|
+
},
|
|
16352
|
+
{
|
|
16353
|
+
"property": "wsjf_score",
|
|
16354
|
+
"label": "CoD Score",
|
|
16355
|
+
"sortable": true
|
|
16356
|
+
}
|
|
16357
|
+
]
|
|
16358
|
+
},
|
|
16359
|
+
"sort_by": {
|
|
16360
|
+
"property": "title",
|
|
16361
|
+
"direction": "asc"
|
|
16362
|
+
},
|
|
16363
|
+
"colour_by": "type",
|
|
16364
|
+
"card_fields": [
|
|
16365
|
+
"title",
|
|
16366
|
+
"description",
|
|
16367
|
+
"status"
|
|
16368
|
+
]
|
|
16369
|
+
},
|
|
16370
|
+
"education": {
|
|
16371
|
+
"purpose": "Quantify the economic impact of not delivering a feature by a given date, making urgency visible and enabling time-sensitive prioritisation.",
|
|
16372
|
+
"core_question": "How much value are we losing every week this feature is not in production, and does that urgency justify fast-tracking it?",
|
|
16373
|
+
"when_to_use": [
|
|
16374
|
+
"You have more ideas or features than capacity to build them",
|
|
16375
|
+
"Stakeholders disagree on what to build next",
|
|
16376
|
+
"You need a transparent, defensible prioritisation process"
|
|
16377
|
+
],
|
|
16378
|
+
"when_not_to_use": [
|
|
16379
|
+
"You have a single obvious next step with no contention",
|
|
16380
|
+
"The backlog is small enough to sequence intuitively"
|
|
16381
|
+
]
|
|
16382
|
+
}
|
|
16383
|
+
},
|
|
16384
|
+
{
|
|
16385
|
+
"id": "five-whys",
|
|
16386
|
+
"approach_ids": [
|
|
16387
|
+
"reflect",
|
|
16388
|
+
"inspect"
|
|
16389
|
+
],
|
|
16390
|
+
"name": "Five Whys",
|
|
16391
|
+
"version": "1.0.0",
|
|
16392
|
+
"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.',
|
|
16393
|
+
"category": "team_process",
|
|
16394
|
+
"origin": {
|
|
16395
|
+
"type": "practitioner",
|
|
16396
|
+
"attribution": "Sakichi Toyoda / Toyota Production System",
|
|
16397
|
+
"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.",
|
|
16398
|
+
"year": 1930,
|
|
16399
|
+
"license": "public_domain"
|
|
16400
|
+
},
|
|
16401
|
+
"tags": [
|
|
16402
|
+
"team_process",
|
|
16403
|
+
"reflection",
|
|
16404
|
+
"root_cause",
|
|
16405
|
+
"tree"
|
|
16406
|
+
],
|
|
16407
|
+
"slots": [
|
|
16408
|
+
{
|
|
16409
|
+
"label": "Symptom",
|
|
16410
|
+
"role": "symptom",
|
|
16411
|
+
"entityTypeId": "need",
|
|
16412
|
+
"description": "The observed problem the analysis starts from."
|
|
16413
|
+
},
|
|
16414
|
+
{
|
|
16415
|
+
"label": "Why chain",
|
|
16416
|
+
"role": "why",
|
|
16417
|
+
"entityTypeId": "insight",
|
|
16418
|
+
"description": 'Each "why?" answer along the chain, typically five iterations deep.'
|
|
16419
|
+
},
|
|
16420
|
+
{
|
|
16421
|
+
"label": "Root cause",
|
|
16422
|
+
"role": "root_cause",
|
|
16423
|
+
"entityTypeId": "insight",
|
|
16424
|
+
"description": "The terminal answer at the bottom of the chain: the underlying cause to address."
|
|
16425
|
+
}
|
|
16426
|
+
],
|
|
16427
|
+
"data": {
|
|
16428
|
+
"entity_types": [
|
|
16429
|
+
{
|
|
16430
|
+
"type": "need",
|
|
16431
|
+
"role": "root"
|
|
16432
|
+
},
|
|
16433
|
+
{
|
|
16434
|
+
"type": "insight",
|
|
16435
|
+
"role": "branch"
|
|
16436
|
+
}
|
|
16437
|
+
],
|
|
16438
|
+
"required_properties": {}
|
|
16439
|
+
},
|
|
16440
|
+
"structure": {
|
|
16441
|
+
"pattern": "tree"
|
|
16442
|
+
},
|
|
16443
|
+
"presentation": {
|
|
16444
|
+
"layout": {
|
|
16445
|
+
"type": "tree",
|
|
16446
|
+
"direction": "TB"
|
|
16447
|
+
},
|
|
16448
|
+
"sort_by": {
|
|
16449
|
+
"property": "title",
|
|
16450
|
+
"direction": "asc"
|
|
16451
|
+
},
|
|
16452
|
+
"card_fields": [
|
|
16453
|
+
"title",
|
|
16454
|
+
"description"
|
|
16455
|
+
]
|
|
16456
|
+
},
|
|
16457
|
+
"education": {
|
|
16458
|
+
"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.',
|
|
16459
|
+
"core_question": "Why is this happening, and why is THAT happening, until we reach a cause we can act on?",
|
|
16460
|
+
"when_to_use": [
|
|
16461
|
+
"A problem keeps recurring after surface fixes",
|
|
16462
|
+
"Post-incident review where the obvious cause feels too obvious",
|
|
16463
|
+
"Designing a fix and you want to confirm you understand the actual driver"
|
|
16464
|
+
],
|
|
16465
|
+
"when_not_to_use": [
|
|
16466
|
+
"The problem has multiple independent root causes (use a fishbone or richer RCA tool)",
|
|
16467
|
+
"You need quantitative attribution rather than a single-thread narrative",
|
|
16468
|
+
'Five linear "whys" oversimplify a systems problem with feedback loops'
|
|
16469
|
+
]
|
|
16470
|
+
}
|
|
16471
|
+
},
|
|
16472
|
+
{
|
|
16473
|
+
"id": "pre-mortem",
|
|
16474
|
+
"approach_ids": [
|
|
16475
|
+
"reflect"
|
|
16476
|
+
],
|
|
16477
|
+
"name": "Pre-mortem",
|
|
16478
|
+
"version": "1.0.0",
|
|
16479
|
+
"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.",
|
|
16480
|
+
"category": "team_process",
|
|
16481
|
+
"origin": {
|
|
16482
|
+
"type": "practitioner",
|
|
16483
|
+
"attribution": "Gary Klein",
|
|
16484
|
+
"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.",
|
|
16485
|
+
"url": "https://hbr.org/2007/09/performing-a-project-premortem",
|
|
16486
|
+
"year": 2007,
|
|
16487
|
+
"license": "published_methodology"
|
|
16488
|
+
},
|
|
16489
|
+
"tags": [
|
|
16490
|
+
"team_process",
|
|
16491
|
+
"reflection",
|
|
16492
|
+
"risk",
|
|
16493
|
+
"collection"
|
|
16494
|
+
],
|
|
16495
|
+
"slots": [
|
|
16496
|
+
{
|
|
16497
|
+
"label": "Imagined failures",
|
|
16498
|
+
"entityTypeId": "risk",
|
|
16499
|
+
"description": "Plausible failure modes named as if they had already occurred."
|
|
16500
|
+
},
|
|
16501
|
+
{
|
|
16502
|
+
"label": "Causes",
|
|
16503
|
+
"entityTypeId": "insight",
|
|
16504
|
+
"description": "For each imagined failure, the contributing causes the team can foresee."
|
|
16505
|
+
},
|
|
16506
|
+
{
|
|
16507
|
+
"label": "Mitigations",
|
|
16508
|
+
"entityTypeId": "initiative",
|
|
16509
|
+
"description": "Mitigation actions the team will take before failure can occur."
|
|
16510
|
+
}
|
|
16511
|
+
],
|
|
16512
|
+
"data": {
|
|
16513
|
+
"entity_types": [
|
|
16514
|
+
{
|
|
16515
|
+
"type": "risk",
|
|
16516
|
+
"role": "bucket"
|
|
16517
|
+
},
|
|
16518
|
+
{
|
|
16519
|
+
"type": "insight",
|
|
16520
|
+
"role": "bucket"
|
|
16521
|
+
},
|
|
16522
|
+
{
|
|
16523
|
+
"type": "initiative",
|
|
16524
|
+
"role": "bucket"
|
|
16525
|
+
}
|
|
16526
|
+
],
|
|
16527
|
+
"required_properties": {}
|
|
16528
|
+
},
|
|
16529
|
+
"structure": {
|
|
16530
|
+
"pattern": "collection"
|
|
15960
16531
|
},
|
|
15961
16532
|
"presentation": {
|
|
15962
16533
|
"layout": {
|
|
15963
|
-
"type": "
|
|
15964
|
-
"
|
|
15965
|
-
{
|
|
15966
|
-
"property": "title",
|
|
15967
|
-
"label": "Items to score",
|
|
15968
|
-
"sortable": true
|
|
15969
|
-
},
|
|
15970
|
-
{
|
|
15971
|
-
"property": "impact",
|
|
15972
|
-
"label": "Impact",
|
|
15973
|
-
"sortable": true
|
|
15974
|
-
},
|
|
15975
|
-
{
|
|
15976
|
-
"property": "confidence",
|
|
15977
|
-
"label": "Confidence",
|
|
15978
|
-
"sortable": true
|
|
15979
|
-
},
|
|
15980
|
-
{
|
|
15981
|
-
"property": "ease",
|
|
15982
|
-
"label": "Ease",
|
|
15983
|
-
"sortable": true
|
|
15984
|
-
}
|
|
15985
|
-
]
|
|
16534
|
+
"type": "grid",
|
|
16535
|
+
"groupBy": "type"
|
|
15986
16536
|
},
|
|
15987
16537
|
"sort_by": {
|
|
15988
16538
|
"property": "title",
|
|
15989
16539
|
"direction": "asc"
|
|
15990
16540
|
},
|
|
15991
|
-
"colour_by": "
|
|
16541
|
+
"colour_by": "group",
|
|
15992
16542
|
"card_fields": [
|
|
15993
16543
|
"title",
|
|
15994
|
-
"description"
|
|
15995
|
-
"status"
|
|
16544
|
+
"description"
|
|
15996
16545
|
]
|
|
15997
16546
|
},
|
|
15998
16547
|
"education": {
|
|
15999
|
-
"purpose": "
|
|
16000
|
-
"core_question": "
|
|
16548
|
+
"purpose": "Surface project risks early by inverting hindsight: imagine the project has already failed and ask why, while there is still time to mitigate.",
|
|
16549
|
+
"core_question": "It is six months from now and the project has failed catastrophically. What happened, and why?",
|
|
16001
16550
|
"when_to_use": [
|
|
16002
|
-
"
|
|
16003
|
-
"
|
|
16004
|
-
"
|
|
16551
|
+
"Kicking off a project with significant downside or irreversible commitment",
|
|
16552
|
+
"A plan looks too clean and the team senses unspoken concerns",
|
|
16553
|
+
"Stakeholders disagree on risk; the exercise externalises and ranks them"
|
|
16005
16554
|
],
|
|
16006
16555
|
"when_not_to_use": [
|
|
16007
|
-
"
|
|
16008
|
-
"The
|
|
16556
|
+
"The work is small, reversible, and cheap to course-correct",
|
|
16557
|
+
"The team is in execution mode and reflective ceremonies will derail momentum",
|
|
16558
|
+
"Risk surfacing has become performative: the team names risks but never mitigates them"
|
|
16009
16559
|
]
|
|
16010
16560
|
}
|
|
16011
16561
|
},
|
|
16012
16562
|
{
|
|
16013
|
-
"id": "
|
|
16563
|
+
"id": "red-team",
|
|
16014
16564
|
"approach_ids": [
|
|
16015
|
-
"
|
|
16565
|
+
"reflect",
|
|
16566
|
+
"inspect"
|
|
16016
16567
|
],
|
|
16017
|
-
"name": "
|
|
16568
|
+
"name": "Red Team",
|
|
16018
16569
|
"version": "1.0.0",
|
|
16019
|
-
"description": "
|
|
16020
|
-
"category": "
|
|
16570
|
+
"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.",
|
|
16571
|
+
"category": "team_process",
|
|
16021
16572
|
"origin": {
|
|
16022
16573
|
"type": "practitioner",
|
|
16023
|
-
"attribution": "
|
|
16024
|
-
"description":
|
|
16025
|
-
"
|
|
16026
|
-
"
|
|
16027
|
-
"license": "open_attribution"
|
|
16574
|
+
"attribution": "US Department of Defense (Cold War era); broadened by security and intelligence practice",
|
|
16575
|
+
"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.`,
|
|
16576
|
+
"year": 1960,
|
|
16577
|
+
"license": "public_domain"
|
|
16028
16578
|
},
|
|
16029
16579
|
"tags": [
|
|
16030
|
-
"
|
|
16031
|
-
"
|
|
16580
|
+
"team_process",
|
|
16581
|
+
"reflection",
|
|
16582
|
+
"adversarial",
|
|
16583
|
+
"collection"
|
|
16032
16584
|
],
|
|
16033
16585
|
"slots": [
|
|
16034
16586
|
{
|
|
16035
|
-
"label": "
|
|
16036
|
-
"entityTypeId": "
|
|
16037
|
-
"description": "
|
|
16038
|
-
},
|
|
16039
|
-
{
|
|
16040
|
-
"label": "User/Business Value",
|
|
16041
|
-
"entityTypeId": "metric",
|
|
16042
|
-
"description": "User/Business Value: metric entries to evaluate"
|
|
16043
|
-
},
|
|
16044
|
-
{
|
|
16045
|
-
"label": "Time Criticality",
|
|
16046
|
-
"entityTypeId": "metric",
|
|
16047
|
-
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
16048
|
-
},
|
|
16049
|
-
{
|
|
16050
|
-
"label": "Risk Reduction / Opportunity Enablement",
|
|
16051
|
-
"entityTypeId": "metric",
|
|
16052
|
-
"description": "Risk Reduction / Opportunity Enablement: metric entries to evaluate"
|
|
16587
|
+
"label": "Target",
|
|
16588
|
+
"entityTypeId": "initiative",
|
|
16589
|
+
"description": "The plan, design, or proposal under adversarial review."
|
|
16053
16590
|
},
|
|
16054
16591
|
{
|
|
16055
|
-
"label": "
|
|
16056
|
-
"entityTypeId": "
|
|
16057
|
-
"description": "
|
|
16592
|
+
"label": "Attack vectors",
|
|
16593
|
+
"entityTypeId": "risk",
|
|
16594
|
+
"description": "The angles the red team uses to probe weaknesses."
|
|
16058
16595
|
},
|
|
16059
16596
|
{
|
|
16060
|
-
"label": "
|
|
16061
|
-
"entityTypeId": "
|
|
16062
|
-
"description": "
|
|
16597
|
+
"label": "Findings",
|
|
16598
|
+
"entityTypeId": "insight",
|
|
16599
|
+
"description": "Weaknesses, blind spots, or unstated assumptions surfaced by the review."
|
|
16063
16600
|
}
|
|
16064
16601
|
],
|
|
16065
16602
|
"data": {
|
|
16066
16603
|
"entity_types": [
|
|
16067
16604
|
{
|
|
16068
|
-
"type": "
|
|
16069
|
-
"role": "
|
|
16070
|
-
},
|
|
16071
|
-
{
|
|
16072
|
-
"type": "
|
|
16073
|
-
"role": "
|
|
16074
|
-
},
|
|
16075
|
-
{
|
|
16076
|
-
"type": "
|
|
16077
|
-
"role": "
|
|
16078
|
-
}
|
|
16079
|
-
],
|
|
16080
|
-
"required_properties": {
|
|
16081
|
-
"feature": [
|
|
16082
|
-
{
|
|
16083
|
-
"property": "user_value",
|
|
16084
|
-
"type": "number",
|
|
16085
|
-
"required": true,
|
|
16086
|
-
"scope": "framework",
|
|
16087
|
-
"label": "User/Business Value",
|
|
16088
|
-
"description": "Relative value to users and the business if delivered"
|
|
16089
|
-
},
|
|
16090
|
-
{
|
|
16091
|
-
"property": "time_criticality",
|
|
16092
|
-
"type": "number",
|
|
16093
|
-
"required": true,
|
|
16094
|
-
"scope": "framework",
|
|
16095
|
-
"label": "Time Criticality",
|
|
16096
|
-
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
16097
|
-
},
|
|
16098
|
-
{
|
|
16099
|
-
"property": "risk_reduction",
|
|
16100
|
-
"type": "number",
|
|
16101
|
-
"required": true,
|
|
16102
|
-
"scope": "framework",
|
|
16103
|
-
"label": "Risk Reduction / Opportunity Enablement",
|
|
16104
|
-
"description": "Value from reducing risk or enabling future opportunities"
|
|
16105
|
-
},
|
|
16106
|
-
{
|
|
16107
|
-
"property": "job_size",
|
|
16108
|
-
"type": "number",
|
|
16109
|
-
"required": true,
|
|
16110
|
-
"scope": "framework",
|
|
16111
|
-
"label": "Job Size",
|
|
16112
|
-
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
16113
|
-
}
|
|
16114
|
-
],
|
|
16115
|
-
"opportunity": [
|
|
16116
|
-
{
|
|
16117
|
-
"property": "user_value",
|
|
16118
|
-
"type": "number",
|
|
16119
|
-
"required": true,
|
|
16120
|
-
"scope": "framework",
|
|
16121
|
-
"label": "User/Business Value",
|
|
16122
|
-
"description": "Relative value to users and the business if delivered"
|
|
16123
|
-
},
|
|
16124
|
-
{
|
|
16125
|
-
"property": "time_criticality",
|
|
16126
|
-
"type": "number",
|
|
16127
|
-
"required": true,
|
|
16128
|
-
"scope": "framework",
|
|
16129
|
-
"label": "Time Criticality",
|
|
16130
|
-
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
16131
|
-
},
|
|
16132
|
-
{
|
|
16133
|
-
"property": "risk_reduction",
|
|
16134
|
-
"type": "number",
|
|
16135
|
-
"required": true,
|
|
16136
|
-
"scope": "framework",
|
|
16137
|
-
"label": "Risk Reduction / Opportunity Enablement",
|
|
16138
|
-
"description": "Value from reducing risk or enabling future opportunities"
|
|
16139
|
-
},
|
|
16140
|
-
{
|
|
16141
|
-
"property": "job_size",
|
|
16142
|
-
"type": "number",
|
|
16143
|
-
"required": true,
|
|
16144
|
-
"scope": "framework",
|
|
16145
|
-
"label": "Job Size",
|
|
16146
|
-
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
16147
|
-
}
|
|
16148
|
-
]
|
|
16149
|
-
},
|
|
16150
|
-
"computed_properties": [
|
|
16151
|
-
{
|
|
16152
|
-
"property": "wsjf_score",
|
|
16153
|
-
"expression": "(user_value + time_criticality + risk_reduction) / job_size",
|
|
16154
|
-
"entity_type": "feature",
|
|
16155
|
-
"label": "WSJF Score",
|
|
16156
|
-
"format": "number"
|
|
16157
|
-
},
|
|
16158
|
-
{
|
|
16159
|
-
"property": "wsjf_score",
|
|
16160
|
-
"expression": "(user_value + time_criticality + risk_reduction) / job_size",
|
|
16161
|
-
"entity_type": "opportunity",
|
|
16162
|
-
"label": "WSJF Score",
|
|
16163
|
-
"format": "number"
|
|
16164
|
-
}
|
|
16165
|
-
],
|
|
16166
|
-
"scoring_lens": {
|
|
16167
|
-
"applies_to": [
|
|
16168
|
-
"feature",
|
|
16169
|
-
"opportunity"
|
|
16170
|
-
],
|
|
16171
|
-
"inputs": [
|
|
16172
|
-
{
|
|
16173
|
-
"property": "user_value",
|
|
16174
|
-
"type": "number",
|
|
16175
|
-
"required": true,
|
|
16176
|
-
"scope": "framework",
|
|
16177
|
-
"label": "User/Business Value",
|
|
16178
|
-
"description": "Relative value to users and the business if delivered"
|
|
16179
|
-
},
|
|
16180
|
-
{
|
|
16181
|
-
"property": "time_criticality",
|
|
16182
|
-
"type": "number",
|
|
16183
|
-
"required": true,
|
|
16184
|
-
"scope": "framework",
|
|
16185
|
-
"label": "Time Criticality",
|
|
16186
|
-
"description": "How much value decays if delivery is delayed (deadlines, competition, seasonal windows)"
|
|
16187
|
-
},
|
|
16188
|
-
{
|
|
16189
|
-
"property": "risk_reduction",
|
|
16190
|
-
"type": "number",
|
|
16191
|
-
"required": true,
|
|
16192
|
-
"scope": "framework",
|
|
16193
|
-
"label": "Risk Reduction / Opportunity Enablement",
|
|
16194
|
-
"description": "Value from reducing risk or enabling future opportunities"
|
|
16195
|
-
},
|
|
16196
|
-
{
|
|
16197
|
-
"property": "job_size",
|
|
16198
|
-
"type": "number",
|
|
16199
|
-
"required": true,
|
|
16200
|
-
"scope": "framework",
|
|
16201
|
-
"label": "Job Size",
|
|
16202
|
-
"description": "Estimated effort (story points, t-shirt size, or person-weeks)"
|
|
16203
|
-
}
|
|
16204
|
-
],
|
|
16205
|
-
"computed": [
|
|
16206
|
-
{
|
|
16207
|
-
"property": "wsjf_score",
|
|
16208
|
-
"expression": "(user_value + time_criticality + risk_reduction) / job_size",
|
|
16209
|
-
"label": "WSJF Score",
|
|
16210
|
-
"format": "number"
|
|
16211
|
-
}
|
|
16212
|
-
]
|
|
16213
|
-
}
|
|
16605
|
+
"type": "initiative",
|
|
16606
|
+
"role": "root"
|
|
16607
|
+
},
|
|
16608
|
+
{
|
|
16609
|
+
"type": "risk",
|
|
16610
|
+
"role": "bucket"
|
|
16611
|
+
},
|
|
16612
|
+
{
|
|
16613
|
+
"type": "insight",
|
|
16614
|
+
"role": "bucket"
|
|
16615
|
+
}
|
|
16616
|
+
],
|
|
16617
|
+
"required_properties": {}
|
|
16214
16618
|
},
|
|
16215
16619
|
"structure": {
|
|
16216
|
-
"pattern": "
|
|
16620
|
+
"pattern": "collection"
|
|
16217
16621
|
},
|
|
16218
16622
|
"presentation": {
|
|
16219
16623
|
"layout": {
|
|
16220
|
-
"type": "
|
|
16221
|
-
"
|
|
16222
|
-
{
|
|
16223
|
-
"property": "title",
|
|
16224
|
-
"label": "Backlog Items",
|
|
16225
|
-
"sortable": true
|
|
16226
|
-
},
|
|
16227
|
-
{
|
|
16228
|
-
"property": "user_value",
|
|
16229
|
-
"label": "User/Business Value",
|
|
16230
|
-
"sortable": true
|
|
16231
|
-
},
|
|
16232
|
-
{
|
|
16233
|
-
"property": "time_criticality",
|
|
16234
|
-
"label": "Time Criticality",
|
|
16235
|
-
"sortable": true
|
|
16236
|
-
},
|
|
16237
|
-
{
|
|
16238
|
-
"property": "risk_reduction",
|
|
16239
|
-
"label": "Risk Reduction / Opportunity Enablement",
|
|
16240
|
-
"sortable": true
|
|
16241
|
-
},
|
|
16242
|
-
{
|
|
16243
|
-
"property": "job_size",
|
|
16244
|
-
"label": "Job Size",
|
|
16245
|
-
"sortable": true
|
|
16246
|
-
},
|
|
16247
|
-
{
|
|
16248
|
-
"property": "wsjf_score",
|
|
16249
|
-
"label": "WSJF Score",
|
|
16250
|
-
"sortable": true
|
|
16251
|
-
}
|
|
16252
|
-
]
|
|
16624
|
+
"type": "grid",
|
|
16625
|
+
"groupBy": "type"
|
|
16253
16626
|
},
|
|
16254
16627
|
"sort_by": {
|
|
16255
16628
|
"property": "title",
|
|
16256
16629
|
"direction": "asc"
|
|
16257
16630
|
},
|
|
16258
|
-
"colour_by": "
|
|
16631
|
+
"colour_by": "group",
|
|
16259
16632
|
"card_fields": [
|
|
16260
16633
|
"title",
|
|
16261
|
-
"description"
|
|
16262
|
-
"status"
|
|
16634
|
+
"description"
|
|
16263
16635
|
]
|
|
16264
16636
|
},
|
|
16265
16637
|
"education": {
|
|
16266
|
-
"purpose": "
|
|
16267
|
-
"core_question": "
|
|
16638
|
+
"purpose": "Stress-test a plan against an explicit adversary by assigning reviewers to attack rather than agree, so weaknesses surface before reality finds them.",
|
|
16639
|
+
"core_question": "If a competent adversary wanted this to fail, where would they push first, and would we hold?",
|
|
16268
16640
|
"when_to_use": [
|
|
16269
|
-
"
|
|
16270
|
-
"
|
|
16271
|
-
"
|
|
16641
|
+
"A high-stakes decision, launch, or security posture needs hardening",
|
|
16642
|
+
"Inside-out thinking is dominant and dissent has gone quiet",
|
|
16643
|
+
"Risk register is suspiciously short for the size of the bet"
|
|
16272
16644
|
],
|
|
16273
16645
|
"when_not_to_use": [
|
|
16274
|
-
"
|
|
16275
|
-
"
|
|
16646
|
+
"Early-stage exploration where adversarial framing would crush a fragile idea prematurely",
|
|
16647
|
+
"Team trust is too low: red-teaming will read as personal attack rather than role-play",
|
|
16648
|
+
"The work is small enough that a lightweight devil's-advocate pass is sufficient"
|
|
16276
16649
|
]
|
|
16277
16650
|
}
|
|
16278
16651
|
},
|
|
16279
16652
|
{
|
|
16280
|
-
"id": "
|
|
16653
|
+
"id": "devils-advocate",
|
|
16281
16654
|
"approach_ids": [
|
|
16282
|
-
"
|
|
16655
|
+
"reflect"
|
|
16283
16656
|
],
|
|
16284
|
-
"name": "
|
|
16657
|
+
"name": "Devil's Advocate",
|
|
16285
16658
|
"version": "1.0.0",
|
|
16286
|
-
"description": "
|
|
16287
|
-
"category": "
|
|
16659
|
+
"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.",
|
|
16660
|
+
"category": "team_process",
|
|
16288
16661
|
"origin": {
|
|
16289
16662
|
"type": "practitioner",
|
|
16290
|
-
"attribution": "
|
|
16291
|
-
"description": "
|
|
16292
|
-
"year":
|
|
16663
|
+
"attribution": "Roman Catholic Church (advocatus diaboli); broadened by decision-quality practice",
|
|
16664
|
+
"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.",
|
|
16665
|
+
"year": 1587,
|
|
16293
16666
|
"license": "public_domain"
|
|
16294
16667
|
},
|
|
16295
16668
|
"tags": [
|
|
16296
|
-
"
|
|
16297
|
-
"
|
|
16669
|
+
"team_process",
|
|
16670
|
+
"reflection",
|
|
16671
|
+
"decision_quality",
|
|
16672
|
+
"collection"
|
|
16298
16673
|
],
|
|
16299
16674
|
"slots": [
|
|
16300
16675
|
{
|
|
16301
|
-
"label": "
|
|
16302
|
-
"entityTypeId": "
|
|
16303
|
-
"description": "
|
|
16304
|
-
},
|
|
16305
|
-
{
|
|
16306
|
-
"label": "User-Business Value",
|
|
16307
|
-
"entityTypeId": "outcome",
|
|
16308
|
-
"description": "Revenue, retention, or strategic value"
|
|
16309
|
-
},
|
|
16310
|
-
{
|
|
16311
|
-
"label": "Time Criticality",
|
|
16312
|
-
"entityTypeId": "metric",
|
|
16313
|
-
"description": "How much value decays with delay"
|
|
16676
|
+
"label": "Proposal",
|
|
16677
|
+
"entityTypeId": "initiative",
|
|
16678
|
+
"description": "The plan or recommendation under consideration."
|
|
16314
16679
|
},
|
|
16315
16680
|
{
|
|
16316
|
-
"label": "
|
|
16317
|
-
"entityTypeId": "
|
|
16318
|
-
"description": "
|
|
16681
|
+
"label": "Opposing arguments",
|
|
16682
|
+
"entityTypeId": "insight",
|
|
16683
|
+
"description": "The case against the proposal, voiced by the assigned contrarian regardless of personal view."
|
|
16319
16684
|
},
|
|
16320
16685
|
{
|
|
16321
|
-
"label": "
|
|
16322
|
-
"entityTypeId": "
|
|
16323
|
-
"description": "
|
|
16686
|
+
"label": "Counter-evidence",
|
|
16687
|
+
"entityTypeId": "evidence",
|
|
16688
|
+
"description": "Data points the contrarian raises that the proposal does not yet account for."
|
|
16324
16689
|
}
|
|
16325
16690
|
],
|
|
16326
16691
|
"data": {
|
|
16327
16692
|
"entity_types": [
|
|
16328
16693
|
{
|
|
16329
|
-
"type": "
|
|
16330
|
-
"role": "
|
|
16331
|
-
},
|
|
16332
|
-
{
|
|
16333
|
-
"type": "metric",
|
|
16334
|
-
"role": "item"
|
|
16335
|
-
},
|
|
16336
|
-
{
|
|
16337
|
-
"type": "outcome",
|
|
16338
|
-
"role": "item"
|
|
16339
|
-
},
|
|
16340
|
-
{
|
|
16341
|
-
"type": "risk",
|
|
16342
|
-
"role": "item"
|
|
16343
|
-
},
|
|
16344
|
-
{
|
|
16345
|
-
"type": "opportunity",
|
|
16346
|
-
"role": "scored_item"
|
|
16347
|
-
}
|
|
16348
|
-
],
|
|
16349
|
-
"required_properties": {
|
|
16350
|
-
"feature": [
|
|
16351
|
-
{
|
|
16352
|
-
"property": "cost_of_delay",
|
|
16353
|
-
"type": "number",
|
|
16354
|
-
"required": true,
|
|
16355
|
-
"scope": "framework",
|
|
16356
|
-
"label": "Cost of Delay",
|
|
16357
|
-
"description": "Weekly revenue impact of not shipping"
|
|
16358
|
-
},
|
|
16359
|
-
{
|
|
16360
|
-
"property": "job_size",
|
|
16361
|
-
"type": "number",
|
|
16362
|
-
"required": true,
|
|
16363
|
-
"scope": "framework",
|
|
16364
|
-
"label": "Job Size",
|
|
16365
|
-
"description": "Weeks of development effort"
|
|
16366
|
-
}
|
|
16367
|
-
],
|
|
16368
|
-
"opportunity": [
|
|
16369
|
-
{
|
|
16370
|
-
"property": "cost_of_delay",
|
|
16371
|
-
"type": "number",
|
|
16372
|
-
"required": true,
|
|
16373
|
-
"scope": "framework",
|
|
16374
|
-
"label": "Cost of Delay",
|
|
16375
|
-
"description": "Weekly revenue impact of not shipping"
|
|
16376
|
-
},
|
|
16377
|
-
{
|
|
16378
|
-
"property": "job_size",
|
|
16379
|
-
"type": "number",
|
|
16380
|
-
"required": true,
|
|
16381
|
-
"scope": "framework",
|
|
16382
|
-
"label": "Job Size",
|
|
16383
|
-
"description": "Weeks of development effort"
|
|
16384
|
-
}
|
|
16385
|
-
]
|
|
16386
|
-
},
|
|
16387
|
-
"computed_properties": [
|
|
16388
|
-
{
|
|
16389
|
-
"property": "wsjf_score",
|
|
16390
|
-
"expression": "cost_of_delay / job_size",
|
|
16391
|
-
"entity_type": "feature",
|
|
16392
|
-
"label": "WSJF Score",
|
|
16393
|
-
"format": "number"
|
|
16694
|
+
"type": "initiative",
|
|
16695
|
+
"role": "root"
|
|
16394
16696
|
},
|
|
16395
16697
|
{
|
|
16396
|
-
"
|
|
16397
|
-
"
|
|
16398
|
-
|
|
16399
|
-
|
|
16400
|
-
"
|
|
16401
|
-
|
|
16402
|
-
|
|
16403
|
-
|
|
16404
|
-
|
|
16405
|
-
|
|
16406
|
-
|
|
16407
|
-
|
|
16408
|
-
|
|
16409
|
-
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
|
|
16413
|
-
"scope": "framework",
|
|
16414
|
-
"label": "Cost of Delay",
|
|
16415
|
-
"description": "Weekly revenue impact of not shipping"
|
|
16416
|
-
},
|
|
16417
|
-
{
|
|
16418
|
-
"property": "job_size",
|
|
16419
|
-
"type": "number",
|
|
16420
|
-
"required": true,
|
|
16421
|
-
"scope": "framework",
|
|
16422
|
-
"label": "Job Size",
|
|
16423
|
-
"description": "Weeks of development effort"
|
|
16424
|
-
}
|
|
16425
|
-
],
|
|
16426
|
-
"computed": [
|
|
16427
|
-
{
|
|
16428
|
-
"property": "wsjf_score",
|
|
16429
|
-
"expression": "cost_of_delay / job_size",
|
|
16430
|
-
"label": "WSJF Score",
|
|
16431
|
-
"format": "number"
|
|
16432
|
-
}
|
|
16433
|
-
]
|
|
16434
|
-
}
|
|
16435
|
-
},
|
|
16436
|
-
"structure": {
|
|
16437
|
-
"pattern": "table"
|
|
16438
|
-
},
|
|
16439
|
-
"presentation": {
|
|
16440
|
-
"layout": {
|
|
16441
|
-
"type": "table",
|
|
16442
|
-
"columns": [
|
|
16443
|
-
{
|
|
16444
|
-
"property": "title",
|
|
16445
|
-
"label": "Items to evaluate",
|
|
16446
|
-
"sortable": true
|
|
16447
|
-
},
|
|
16448
|
-
{
|
|
16449
|
-
"property": "cost_of_delay",
|
|
16450
|
-
"label": "User-Business Value",
|
|
16451
|
-
"sortable": true
|
|
16452
|
-
},
|
|
16453
|
-
{
|
|
16454
|
-
"property": "job_size",
|
|
16455
|
-
"label": "Job Size",
|
|
16456
|
-
"sortable": true
|
|
16457
|
-
},
|
|
16458
|
-
{
|
|
16459
|
-
"property": "wsjf_score",
|
|
16460
|
-
"label": "CoD Score",
|
|
16461
|
-
"sortable": true
|
|
16462
|
-
}
|
|
16463
|
-
]
|
|
16698
|
+
"type": "insight",
|
|
16699
|
+
"role": "bucket"
|
|
16700
|
+
},
|
|
16701
|
+
{
|
|
16702
|
+
"type": "evidence",
|
|
16703
|
+
"role": "bucket"
|
|
16704
|
+
}
|
|
16705
|
+
],
|
|
16706
|
+
"required_properties": {}
|
|
16707
|
+
},
|
|
16708
|
+
"structure": {
|
|
16709
|
+
"pattern": "collection"
|
|
16710
|
+
},
|
|
16711
|
+
"presentation": {
|
|
16712
|
+
"layout": {
|
|
16713
|
+
"type": "grid",
|
|
16714
|
+
"groupBy": "type"
|
|
16464
16715
|
},
|
|
16465
16716
|
"sort_by": {
|
|
16466
16717
|
"property": "title",
|
|
16467
16718
|
"direction": "asc"
|
|
16468
16719
|
},
|
|
16469
|
-
"colour_by": "
|
|
16720
|
+
"colour_by": "group",
|
|
16470
16721
|
"card_fields": [
|
|
16471
16722
|
"title",
|
|
16472
|
-
"description"
|
|
16473
|
-
"status"
|
|
16723
|
+
"description"
|
|
16474
16724
|
]
|
|
16475
16725
|
},
|
|
16476
16726
|
"education": {
|
|
16477
|
-
"purpose": "
|
|
16478
|
-
"core_question": "
|
|
16727
|
+
"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.",
|
|
16728
|
+
"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?",
|
|
16479
16729
|
"when_to_use": [
|
|
16480
|
-
"
|
|
16481
|
-
"
|
|
16482
|
-
"
|
|
16730
|
+
"A decision is heading toward consensus and you suspect groupthink",
|
|
16731
|
+
"Stakes are high and the team has not heard a serious counter-argument",
|
|
16732
|
+
"Cultural norms make raw dissent costly; assigning the role lowers the social cost"
|
|
16483
16733
|
],
|
|
16484
16734
|
"when_not_to_use": [
|
|
16485
|
-
"
|
|
16486
|
-
"The
|
|
16735
|
+
"Genuine disagreement already exists in the room (let it surface; do not theatricalise it)",
|
|
16736
|
+
"The decision is small enough that the ceremony costs more than the insight returned",
|
|
16737
|
+
"The assigned contrarian will be punished socially for the role; set the norms first or skip"
|
|
16487
16738
|
]
|
|
16488
16739
|
}
|
|
16489
16740
|
},
|
|
16490
16741
|
{
|
|
16491
|
-
"id": "
|
|
16742
|
+
"id": "second-order-thinking",
|
|
16492
16743
|
"approach_ids": [
|
|
16493
|
-
"reflect"
|
|
16494
|
-
"inspect"
|
|
16744
|
+
"reflect"
|
|
16495
16745
|
],
|
|
16496
|
-
"name": "
|
|
16746
|
+
"name": "Second-order Thinking",
|
|
16497
16747
|
"version": "1.0.0",
|
|
16498
|
-
"description": '
|
|
16748
|
+
"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.',
|
|
16499
16749
|
"category": "team_process",
|
|
16500
16750
|
"origin": {
|
|
16501
16751
|
"type": "practitioner",
|
|
16502
|
-
"attribution": "
|
|
16503
|
-
"description":
|
|
16504
|
-
"
|
|
16505
|
-
"
|
|
16752
|
+
"attribution": "Howard Marks / Charlie Munger",
|
|
16753
|
+
"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.`,
|
|
16754
|
+
"url": "https://www.oaktreecapital.com/insights/memo/dare-to-be-great-ii",
|
|
16755
|
+
"year": 2011,
|
|
16756
|
+
"license": "published_methodology"
|
|
16506
16757
|
},
|
|
16507
16758
|
"tags": [
|
|
16508
16759
|
"team_process",
|
|
16509
16760
|
"reflection",
|
|
16510
|
-
"
|
|
16761
|
+
"consequences",
|
|
16511
16762
|
"tree"
|
|
16512
16763
|
],
|
|
16513
16764
|
"slots": [
|
|
16514
16765
|
{
|
|
16515
|
-
"label": "
|
|
16516
|
-
"
|
|
16517
|
-
"
|
|
16766
|
+
"label": "First-order move",
|
|
16767
|
+
"role": "first_order",
|
|
16768
|
+
"entityTypeId": "decision",
|
|
16769
|
+
"description": "The decision or move under consideration."
|
|
16518
16770
|
},
|
|
16519
16771
|
{
|
|
16520
|
-
"label": "
|
|
16772
|
+
"label": "Second-order consequences",
|
|
16773
|
+
"role": "second_order",
|
|
16521
16774
|
"entityTypeId": "insight",
|
|
16522
|
-
"description":
|
|
16775
|
+
"description": "Downstream effects that follow from the first-order move."
|
|
16523
16776
|
},
|
|
16524
16777
|
{
|
|
16525
|
-
"label": "
|
|
16778
|
+
"label": "Higher-order consequences",
|
|
16779
|
+
"role": "higher_order",
|
|
16526
16780
|
"entityTypeId": "insight",
|
|
16527
|
-
"description": "
|
|
16781
|
+
"description": "Third-, fourth-, fifth-order ripples: second-order consequences of the second-order consequences."
|
|
16528
16782
|
}
|
|
16529
16783
|
],
|
|
16530
16784
|
"data": {
|
|
16531
16785
|
"entity_types": [
|
|
16532
16786
|
{
|
|
16533
|
-
"type": "
|
|
16787
|
+
"type": "decision",
|
|
16534
16788
|
"role": "root"
|
|
16535
16789
|
},
|
|
16536
16790
|
{
|
|
@@ -16558,382 +16812,461 @@ var UPG_FRAMEWORKS = [
|
|
|
16558
16812
|
]
|
|
16559
16813
|
},
|
|
16560
16814
|
"education": {
|
|
16561
|
-
"purpose": '
|
|
16562
|
-
"core_question": "
|
|
16815
|
+
"purpose": 'Resist first-order reasoning by chaining "and then what?" until non-obvious downstream consequences come into view.',
|
|
16816
|
+
"core_question": "If we make this move and it works, what does the world look like next, and is that the world we want?",
|
|
16563
16817
|
"when_to_use": [
|
|
16564
|
-
"A
|
|
16565
|
-
"
|
|
16566
|
-
"
|
|
16818
|
+
"A decision has feedback loops, market reactions, or behavioural ripples",
|
|
16819
|
+
"The first-order case is compelling, which is exactly when downstream effects bite",
|
|
16820
|
+
"Considering an irreversible or large-scale commitment"
|
|
16567
16821
|
],
|
|
16568
16822
|
"when_not_to_use": [
|
|
16569
|
-
"
|
|
16570
|
-
"
|
|
16571
|
-
|
|
16823
|
+
"Routine, reversible, low-blast-radius decisions where deliberation costs more than mistakes",
|
|
16824
|
+
"Higher-order branches diverge into pure speculation with no anchor in evidence",
|
|
16825
|
+
"Time pressure makes a deeper trace expensive and the first-order call is good enough"
|
|
16572
16826
|
]
|
|
16573
16827
|
}
|
|
16574
|
-
}
|
|
16575
|
-
|
|
16576
|
-
|
|
16577
|
-
|
|
16578
|
-
|
|
16579
|
-
|
|
16580
|
-
|
|
16581
|
-
|
|
16582
|
-
|
|
16583
|
-
|
|
16584
|
-
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
|
|
16588
|
-
|
|
16589
|
-
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
|
|
16593
|
-
|
|
16594
|
-
|
|
16595
|
-
|
|
16596
|
-
|
|
16597
|
-
],
|
|
16598
|
-
|
|
16599
|
-
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
|
|
16603
|
-
|
|
16604
|
-
|
|
16605
|
-
|
|
16606
|
-
|
|
16607
|
-
|
|
16608
|
-
|
|
16609
|
-
|
|
16610
|
-
|
|
16611
|
-
|
|
16612
|
-
|
|
16828
|
+
}
|
|
16829
|
+
];
|
|
16830
|
+
var UPG_FRAMEWORKS_BY_ID = Object.fromEntries(
|
|
16831
|
+
UPG_FRAMEWORKS.map((fw) => [fw.id, fw])
|
|
16832
|
+
);
|
|
16833
|
+
var UPG_FRAMEWORKS_BY_CATEGORY = {};
|
|
16834
|
+
for (const fw of UPG_FRAMEWORKS) {
|
|
16835
|
+
if (!UPG_FRAMEWORKS_BY_CATEGORY[fw.category]) UPG_FRAMEWORKS_BY_CATEGORY[fw.category] = [];
|
|
16836
|
+
UPG_FRAMEWORKS_BY_CATEGORY[fw.category].push(fw);
|
|
16837
|
+
}
|
|
16838
|
+
|
|
16839
|
+
// src/grammar/validate.ts
|
|
16840
|
+
var crossProductEdgeTypeSet = new Set(UPG_CROSS_EDGE_TYPES);
|
|
16841
|
+
var CONTENT_DEPTH_WARNING_RULES = /* @__PURE__ */ new Set([
|
|
16842
|
+
"property-type",
|
|
16843
|
+
"property-enum",
|
|
16844
|
+
"self-loop",
|
|
16845
|
+
"framework-score"
|
|
16846
|
+
]);
|
|
16847
|
+
function validateUPGDocument(doc) {
|
|
16848
|
+
const errors = [];
|
|
16849
|
+
const warnings = [];
|
|
16850
|
+
if (!doc || typeof doc !== "object") {
|
|
16851
|
+
return { valid: false, errors: [{ path: "$", message: "Document must be an object" }], warnings };
|
|
16852
|
+
}
|
|
16853
|
+
const d = doc;
|
|
16854
|
+
if (!d.upg_version || typeof d.upg_version !== "string") {
|
|
16855
|
+
errors.push({ path: "$.upg_version", message: "upg_version is required and must be a string" });
|
|
16856
|
+
}
|
|
16857
|
+
if (!d.exported_at || typeof d.exported_at !== "string") {
|
|
16858
|
+
errors.push({ path: "$.exported_at", message: "exported_at is required and must be an ISO 8601 string" });
|
|
16859
|
+
}
|
|
16860
|
+
if (!d.source || typeof d.source !== "object") {
|
|
16861
|
+
errors.push({ path: "$.source", message: "source is required and must be an object" });
|
|
16862
|
+
} else {
|
|
16863
|
+
const source = d.source;
|
|
16864
|
+
if (!source.tool || typeof source.tool !== "string") {
|
|
16865
|
+
errors.push({ path: "$.source.tool", message: "source.tool is required and must be a string" });
|
|
16866
|
+
}
|
|
16867
|
+
}
|
|
16868
|
+
if (!d.product || typeof d.product !== "object") {
|
|
16869
|
+
errors.push({ path: "$.product", message: "product is required and must be an object" });
|
|
16870
|
+
} else {
|
|
16871
|
+
const product = d.product;
|
|
16872
|
+
if (!product.id || typeof product.id !== "string") {
|
|
16873
|
+
errors.push({ path: "$.product.id", message: "product.id is required and must be a string" });
|
|
16874
|
+
}
|
|
16875
|
+
if (!product.title || typeof product.title !== "string") {
|
|
16876
|
+
errors.push({ path: "$.product.title", message: "product.title is required and must be a string" });
|
|
16877
|
+
}
|
|
16878
|
+
}
|
|
16879
|
+
const allKnownTypes = new Set(getTypes());
|
|
16880
|
+
const allKnownEdgeTypes = new Set(Object.keys(UPG_EDGE_CATALOG));
|
|
16881
|
+
if (!Array.isArray(d.nodes)) {
|
|
16882
|
+
errors.push({
|
|
16883
|
+
path: "$.nodes",
|
|
16884
|
+
message: d.nodes === void 0 ? "nodes is required and must be an array" : `nodes must be an array, got ${describeKind(d.nodes)}`
|
|
16885
|
+
});
|
|
16886
|
+
} else {
|
|
16887
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
16888
|
+
const nodesById = /* @__PURE__ */ new Map();
|
|
16889
|
+
d.nodes.forEach((node, i) => {
|
|
16890
|
+
const path = `$.nodes[${i}]`;
|
|
16891
|
+
if (!node || typeof node !== "object") {
|
|
16892
|
+
errors.push({ path, message: "Each node must be an object" });
|
|
16893
|
+
return;
|
|
16894
|
+
}
|
|
16895
|
+
const n = node;
|
|
16896
|
+
if (!n.id || typeof n.id !== "string") {
|
|
16897
|
+
errors.push({ path: `${path}.id`, message: "Node id is required and must be a string" });
|
|
16898
|
+
} else {
|
|
16899
|
+
if (nodeIds.has(n.id)) {
|
|
16900
|
+
errors.push({ path: `${path}.id`, message: `Duplicate node id: ${n.id}` });
|
|
16901
|
+
}
|
|
16902
|
+
nodeIds.add(n.id);
|
|
16903
|
+
nodesById.set(n.id, n);
|
|
16904
|
+
}
|
|
16905
|
+
if (!n.type || typeof n.type !== "string") {
|
|
16906
|
+
errors.push({ path: `${path}.type`, message: "Node type is required and must be a string" });
|
|
16907
|
+
} else if (!allKnownTypes.has(n.type)) {
|
|
16908
|
+
warnings.push({ path: `${path}.type`, message: `Unknown UPG type: "${n.type}". Node will be preserved with its original type.` });
|
|
16909
|
+
}
|
|
16910
|
+
if (!n.title || typeof n.title !== "string") {
|
|
16911
|
+
errors.push({ path: `${path}.title`, message: "Node title is required and must be a string" });
|
|
16912
|
+
} else if (n.title.trim().length === 0) {
|
|
16913
|
+
errors.push({ path: `${path}.title`, message: "Node title must not be blank (whitespace-only)" });
|
|
16914
|
+
}
|
|
16915
|
+
if (n.properties !== void 0 && n.properties !== null) {
|
|
16916
|
+
if (typeof n.properties !== "object" || Array.isArray(n.properties)) {
|
|
16917
|
+
errors.push({
|
|
16918
|
+
path: `${path}.properties`,
|
|
16919
|
+
message: `Node properties must be a plain object when present, got ${describeKind(n.properties)}.`
|
|
16920
|
+
});
|
|
16921
|
+
}
|
|
16922
|
+
}
|
|
16923
|
+
if (typeof n.type === "string") {
|
|
16924
|
+
validateNodeProperties(n, `${path}`, warnings);
|
|
16925
|
+
}
|
|
16926
|
+
});
|
|
16927
|
+
if (!Array.isArray(d.edges)) {
|
|
16928
|
+
errors.push({
|
|
16929
|
+
path: "$.edges",
|
|
16930
|
+
message: d.edges === void 0 ? "edges is required and must be an array" : `edges must be an array, got ${describeKind(d.edges)}`
|
|
16931
|
+
});
|
|
16932
|
+
} else {
|
|
16933
|
+
d.edges.forEach((edge, i) => {
|
|
16934
|
+
const path = `$.edges[${i}]`;
|
|
16935
|
+
if (!edge || typeof edge !== "object") {
|
|
16936
|
+
errors.push({ path, message: "Each edge must be an object" });
|
|
16937
|
+
return;
|
|
16938
|
+
}
|
|
16939
|
+
const e = edge;
|
|
16940
|
+
if (!e.id || typeof e.id !== "string") {
|
|
16941
|
+
errors.push({ path: `${path}.id`, message: "Edge id is required and must be a string" });
|
|
16942
|
+
}
|
|
16943
|
+
if (typeof e.type === "string" && crossProductEdgeTypeSet.has(e.type)) {
|
|
16944
|
+
errors.push({
|
|
16945
|
+
path: `${path}.type`,
|
|
16946
|
+
message: `Cross-product edge type "${e.type}" must live in portfolio.cross_edges[], not product edges[].`
|
|
16947
|
+
});
|
|
16948
|
+
return;
|
|
16949
|
+
}
|
|
16950
|
+
if (!e.source || typeof e.source !== "string") {
|
|
16951
|
+
errors.push({ path: `${path}.source`, message: "Edge source is required and must be a string" });
|
|
16952
|
+
} else if (!nodeIds.has(e.source)) {
|
|
16953
|
+
errors.push({ path: `${path}.source`, message: `Edge source references unknown node id: ${e.source}` });
|
|
16954
|
+
}
|
|
16955
|
+
if (!e.target || typeof e.target !== "string") {
|
|
16956
|
+
errors.push({ path: `${path}.target`, message: "Edge target is required and must be a string" });
|
|
16957
|
+
} else if (!nodeIds.has(e.target)) {
|
|
16958
|
+
errors.push({ path: `${path}.target`, message: `Edge target references unknown node id: ${e.target}` });
|
|
16959
|
+
}
|
|
16960
|
+
if (typeof e.source === "string" && typeof e.target === "string" && e.source === e.target) {
|
|
16961
|
+
warnings.push({
|
|
16962
|
+
path: `${path}`,
|
|
16963
|
+
message: `Self-loop edge: source and target are the same node ("${e.source}"). A node related to itself is almost always an error.`,
|
|
16964
|
+
rule: "self-loop"
|
|
16965
|
+
});
|
|
16966
|
+
}
|
|
16967
|
+
if (!e.type || typeof e.type !== "string") {
|
|
16968
|
+
errors.push({ path: `${path}.type`, message: "Edge type is required and must be a string" });
|
|
16969
|
+
} else if (!allKnownEdgeTypes.has(e.type)) {
|
|
16970
|
+
warnings.push({
|
|
16971
|
+
path: `${path}.type`,
|
|
16972
|
+
message: `Unknown edge type: "${e.type}". Edge preserved but not in the edge registry.`
|
|
16973
|
+
});
|
|
16974
|
+
}
|
|
16975
|
+
});
|
|
16976
|
+
const valueToAxis = /* @__PURE__ */ new Map();
|
|
16977
|
+
const edgesArr = d.edges;
|
|
16978
|
+
for (const edge of edgesArr) {
|
|
16979
|
+
if (!edge || typeof edge !== "object") continue;
|
|
16980
|
+
const e = edge;
|
|
16981
|
+
if (e.type === "classification_axis_includes_classification_value" && typeof e.source === "string" && typeof e.target === "string") {
|
|
16982
|
+
valueToAxis.set(e.target, e.source);
|
|
16983
|
+
}
|
|
16613
16984
|
}
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
"role": "bucket"
|
|
16624
|
-
},
|
|
16625
|
-
{
|
|
16626
|
-
"type": "initiative",
|
|
16627
|
-
"role": "bucket"
|
|
16985
|
+
for (const [id, n] of nodesById.entries()) {
|
|
16986
|
+
if (n.type !== "classification_value") continue;
|
|
16987
|
+
if (valueToAxis.has(id)) continue;
|
|
16988
|
+
const pid = n.parent_id;
|
|
16989
|
+
if (typeof pid === "string") {
|
|
16990
|
+
const parent = nodesById.get(pid);
|
|
16991
|
+
if (parent && parent.type === "classification_axis") {
|
|
16992
|
+
valueToAxis.set(id, pid);
|
|
16993
|
+
}
|
|
16628
16994
|
}
|
|
16629
|
-
],
|
|
16630
|
-
"required_properties": {}
|
|
16631
|
-
},
|
|
16632
|
-
"structure": {
|
|
16633
|
-
"pattern": "collection"
|
|
16634
|
-
},
|
|
16635
|
-
"presentation": {
|
|
16636
|
-
"layout": {
|
|
16637
|
-
"type": "grid",
|
|
16638
|
-
"groupBy": "type"
|
|
16639
|
-
},
|
|
16640
|
-
"sort_by": {
|
|
16641
|
-
"property": "title",
|
|
16642
|
-
"direction": "asc"
|
|
16643
|
-
},
|
|
16644
|
-
"colour_by": "group",
|
|
16645
|
-
"card_fields": [
|
|
16646
|
-
"title",
|
|
16647
|
-
"description"
|
|
16648
|
-
]
|
|
16649
|
-
},
|
|
16650
|
-
"education": {
|
|
16651
|
-
"purpose": "Surface project risks early by inverting hindsight: imagine the project has already failed and ask why, while there is still time to mitigate.",
|
|
16652
|
-
"core_question": "It is six months from now and the project has failed catastrophically. What happened, and why?",
|
|
16653
|
-
"when_to_use": [
|
|
16654
|
-
"Kicking off a project with significant downside or irreversible commitment",
|
|
16655
|
-
"A plan looks too clean and the team senses unspoken concerns",
|
|
16656
|
-
"Stakeholders disagree on risk; the exercise externalises and ranks them"
|
|
16657
|
-
],
|
|
16658
|
-
"when_not_to_use": [
|
|
16659
|
-
"The work is small, reversible, and cheap to course-correct",
|
|
16660
|
-
"The team is in execution mode and reflective ceremonies will derail momentum",
|
|
16661
|
-
"Risk surfacing has become performative: the team names risks but never mitigates them"
|
|
16662
|
-
]
|
|
16663
|
-
}
|
|
16664
|
-
},
|
|
16665
|
-
{
|
|
16666
|
-
"id": "red-team",
|
|
16667
|
-
"approach_ids": [
|
|
16668
|
-
"reflect",
|
|
16669
|
-
"inspect"
|
|
16670
|
-
],
|
|
16671
|
-
"name": "Red Team",
|
|
16672
|
-
"version": "1.0.0",
|
|
16673
|
-
"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.",
|
|
16674
|
-
"category": "team_process",
|
|
16675
|
-
"origin": {
|
|
16676
|
-
"type": "practitioner",
|
|
16677
|
-
"attribution": "US Department of Defense (Cold War era); broadened by security and intelligence practice",
|
|
16678
|
-
"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.`,
|
|
16679
|
-
"year": 1960,
|
|
16680
|
-
"license": "public_domain"
|
|
16681
|
-
},
|
|
16682
|
-
"tags": [
|
|
16683
|
-
"team_process",
|
|
16684
|
-
"reflection",
|
|
16685
|
-
"adversarial",
|
|
16686
|
-
"collection"
|
|
16687
|
-
],
|
|
16688
|
-
"slots": [
|
|
16689
|
-
{
|
|
16690
|
-
"label": "Target",
|
|
16691
|
-
"entityTypeId": "initiative",
|
|
16692
|
-
"description": "The plan, design, or proposal under adversarial review."
|
|
16693
|
-
},
|
|
16694
|
-
{
|
|
16695
|
-
"label": "Attack vectors",
|
|
16696
|
-
"entityTypeId": "risk",
|
|
16697
|
-
"description": "The angles the red team uses to probe weaknesses."
|
|
16698
|
-
},
|
|
16699
|
-
{
|
|
16700
|
-
"label": "Findings",
|
|
16701
|
-
"entityTypeId": "insight",
|
|
16702
|
-
"description": "Weaknesses, blind spots, or unstated assumptions surfaced by the review."
|
|
16703
16995
|
}
|
|
16704
|
-
|
|
16705
|
-
|
|
16706
|
-
|
|
16707
|
-
|
|
16708
|
-
|
|
16709
|
-
|
|
16710
|
-
},
|
|
16711
|
-
{
|
|
16712
|
-
"type": "risk",
|
|
16713
|
-
"role": "bucket"
|
|
16714
|
-
},
|
|
16715
|
-
{
|
|
16716
|
-
"type": "insight",
|
|
16717
|
-
"role": "bucket"
|
|
16996
|
+
const axesPerAnalysis = /* @__PURE__ */ new Map();
|
|
16997
|
+
for (const edge of edgesArr) {
|
|
16998
|
+
if (!edge || typeof edge !== "object") continue;
|
|
16999
|
+
const e = edge;
|
|
17000
|
+
if (e.type === "competitive_analysis_dimensioned_by_classification_axis" && typeof e.source === "string") {
|
|
17001
|
+
axesPerAnalysis.set(e.source, (axesPerAnalysis.get(e.source) ?? 0) + 1);
|
|
16718
17002
|
}
|
|
16719
|
-
],
|
|
16720
|
-
"required_properties": {}
|
|
16721
|
-
},
|
|
16722
|
-
"structure": {
|
|
16723
|
-
"pattern": "collection"
|
|
16724
|
-
},
|
|
16725
|
-
"presentation": {
|
|
16726
|
-
"layout": {
|
|
16727
|
-
"type": "grid",
|
|
16728
|
-
"groupBy": "type"
|
|
16729
|
-
},
|
|
16730
|
-
"sort_by": {
|
|
16731
|
-
"property": "title",
|
|
16732
|
-
"direction": "asc"
|
|
16733
|
-
},
|
|
16734
|
-
"colour_by": "group",
|
|
16735
|
-
"card_fields": [
|
|
16736
|
-
"title",
|
|
16737
|
-
"description"
|
|
16738
|
-
]
|
|
16739
|
-
},
|
|
16740
|
-
"education": {
|
|
16741
|
-
"purpose": "Stress-test a plan against an explicit adversary by assigning reviewers to attack rather than agree, so weaknesses surface before reality finds them.",
|
|
16742
|
-
"core_question": "If a competent adversary wanted this to fail, where would they push first, and would we hold?",
|
|
16743
|
-
"when_to_use": [
|
|
16744
|
-
"A high-stakes decision, launch, or security posture needs hardening",
|
|
16745
|
-
"Inside-out thinking is dominant and dissent has gone quiet",
|
|
16746
|
-
"Risk register is suspiciously short for the size of the bet"
|
|
16747
|
-
],
|
|
16748
|
-
"when_not_to_use": [
|
|
16749
|
-
"Early-stage exploration where adversarial framing would crush a fragile idea prematurely",
|
|
16750
|
-
"Team trust is too low: red-teaming will read as personal attack rather than role-play",
|
|
16751
|
-
"The work is small enough that a lightweight devil's-advocate pass is sufficient"
|
|
16752
|
-
]
|
|
16753
|
-
}
|
|
16754
|
-
},
|
|
16755
|
-
{
|
|
16756
|
-
"id": "devils-advocate",
|
|
16757
|
-
"approach_ids": [
|
|
16758
|
-
"reflect"
|
|
16759
|
-
],
|
|
16760
|
-
"name": "Devil's Advocate",
|
|
16761
|
-
"version": "1.0.0",
|
|
16762
|
-
"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.",
|
|
16763
|
-
"category": "team_process",
|
|
16764
|
-
"origin": {
|
|
16765
|
-
"type": "practitioner",
|
|
16766
|
-
"attribution": "Roman Catholic Church (advocatus diaboli); broadened by decision-quality practice",
|
|
16767
|
-
"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.",
|
|
16768
|
-
"year": 1587,
|
|
16769
|
-
"license": "public_domain"
|
|
16770
|
-
},
|
|
16771
|
-
"tags": [
|
|
16772
|
-
"team_process",
|
|
16773
|
-
"reflection",
|
|
16774
|
-
"decision_quality",
|
|
16775
|
-
"collection"
|
|
16776
|
-
],
|
|
16777
|
-
"slots": [
|
|
16778
|
-
{
|
|
16779
|
-
"label": "Proposal",
|
|
16780
|
-
"entityTypeId": "initiative",
|
|
16781
|
-
"description": "The plan or recommendation under consideration."
|
|
16782
|
-
},
|
|
16783
|
-
{
|
|
16784
|
-
"label": "Opposing arguments",
|
|
16785
|
-
"entityTypeId": "insight",
|
|
16786
|
-
"description": "The case against the proposal, voiced by the assigned contrarian regardless of personal view."
|
|
16787
|
-
},
|
|
16788
|
-
{
|
|
16789
|
-
"label": "Counter-evidence",
|
|
16790
|
-
"entityTypeId": "evidence",
|
|
16791
|
-
"description": "Data points the contrarian raises that the proposal does not yet account for."
|
|
16792
17003
|
}
|
|
16793
|
-
|
|
16794
|
-
|
|
16795
|
-
|
|
16796
|
-
|
|
16797
|
-
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
|
|
16801
|
-
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
|
|
16807
|
-
}
|
|
16808
|
-
|
|
16809
|
-
|
|
16810
|
-
|
|
16811
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
|
|
16822
|
-
|
|
16823
|
-
|
|
16824
|
-
|
|
16825
|
-
|
|
16826
|
-
|
|
16827
|
-
|
|
16828
|
-
|
|
16829
|
-
|
|
16830
|
-
|
|
16831
|
-
|
|
16832
|
-
|
|
16833
|
-
|
|
16834
|
-
|
|
16835
|
-
|
|
16836
|
-
|
|
16837
|
-
|
|
16838
|
-
|
|
16839
|
-
|
|
16840
|
-
|
|
16841
|
-
|
|
17004
|
+
d.nodes.forEach((node, i) => {
|
|
17005
|
+
if (!node || typeof node !== "object") return;
|
|
17006
|
+
const n = node;
|
|
17007
|
+
if (n.type !== "competitive_analysis") return;
|
|
17008
|
+
const props = n.properties;
|
|
17009
|
+
if (!props || typeof props !== "object") return;
|
|
17010
|
+
const empty = props.empty_cells;
|
|
17011
|
+
if (!Array.isArray(empty)) return;
|
|
17012
|
+
const axisCount = axesPerAnalysis.get(n.id ?? "") ?? 0;
|
|
17013
|
+
if (axisCount !== 2) {
|
|
17014
|
+
warnings.push({
|
|
17015
|
+
path: `$.nodes[${i}].properties.empty_cells`,
|
|
17016
|
+
message: `empty_cells is meaningful only on a 2-axis competitive_analysis; this node is dimensioned by ${axisCount} classification_axis ${axisCount === 1 ? "child" : "children"}.`
|
|
17017
|
+
});
|
|
17018
|
+
}
|
|
17019
|
+
empty.forEach((cell, j) => {
|
|
17020
|
+
const cpath = `$.nodes[${i}].properties.empty_cells[${j}]`;
|
|
17021
|
+
if (!cell || typeof cell !== "object") {
|
|
17022
|
+
errors.push({ path: cpath, message: "empty_cells entry must be an object" });
|
|
17023
|
+
return;
|
|
17024
|
+
}
|
|
17025
|
+
const c = cell;
|
|
17026
|
+
const aRef = c.axis_a_value_ref;
|
|
17027
|
+
const bRef = c.axis_b_value_ref;
|
|
17028
|
+
if (typeof aRef !== "string") {
|
|
17029
|
+
errors.push({ path: `${cpath}.axis_a_value_ref`, message: "axis_a_value_ref is required and must be a string" });
|
|
17030
|
+
}
|
|
17031
|
+
if (typeof bRef !== "string") {
|
|
17032
|
+
errors.push({ path: `${cpath}.axis_b_value_ref`, message: "axis_b_value_ref is required and must be a string" });
|
|
17033
|
+
}
|
|
17034
|
+
if (typeof aRef !== "string" || typeof bRef !== "string") return;
|
|
17035
|
+
const aNode = nodesById.get(aRef);
|
|
17036
|
+
const bNode = nodesById.get(bRef);
|
|
17037
|
+
if (!aNode) {
|
|
17038
|
+
errors.push({ path: `${cpath}.axis_a_value_ref`, message: `Unknown node id: ${aRef}` });
|
|
17039
|
+
} else if (aNode.type !== "classification_value") {
|
|
17040
|
+
errors.push({ path: `${cpath}.axis_a_value_ref`, message: `Expected classification_value, found "${String(aNode.type)}" at ${aRef}` });
|
|
17041
|
+
}
|
|
17042
|
+
if (!bNode) {
|
|
17043
|
+
errors.push({ path: `${cpath}.axis_b_value_ref`, message: `Unknown node id: ${bRef}` });
|
|
17044
|
+
} else if (bNode.type !== "classification_value") {
|
|
17045
|
+
errors.push({ path: `${cpath}.axis_b_value_ref`, message: `Expected classification_value, found "${String(bNode.type)}" at ${bRef}` });
|
|
17046
|
+
}
|
|
17047
|
+
if (aNode && bNode && aNode.type === "classification_value" && bNode.type === "classification_value") {
|
|
17048
|
+
const aAxis = valueToAxis.get(aRef);
|
|
17049
|
+
const bAxis = valueToAxis.get(bRef);
|
|
17050
|
+
if (aAxis && bAxis && aAxis === bAxis) {
|
|
17051
|
+
errors.push({
|
|
17052
|
+
path: cpath,
|
|
17053
|
+
message: `empty_cells references two classification_values from the same axis (${aAxis}); axis_a and axis_b must come from distinct classification_axis parents.`
|
|
17054
|
+
});
|
|
17055
|
+
}
|
|
17056
|
+
}
|
|
17057
|
+
const kind = c.rationale_kind;
|
|
17058
|
+
if (kind !== void 0 && kind !== "structural" && kind !== "ideological" && kind !== "opportunity") {
|
|
17059
|
+
errors.push({
|
|
17060
|
+
path: `${cpath}.rationale_kind`,
|
|
17061
|
+
message: `rationale_kind must be one of 'structural' | 'ideological' | 'opportunity'; got "${String(kind)}".`
|
|
17062
|
+
});
|
|
17063
|
+
}
|
|
17064
|
+
});
|
|
17065
|
+
});
|
|
17066
|
+
validateFrameworkScores(d.nodes, d.edges, warnings);
|
|
17067
|
+
}
|
|
17068
|
+
}
|
|
17069
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
17070
|
+
}
|
|
17071
|
+
function validateFrameworkScores(nodes, edges, warnings) {
|
|
17072
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
17073
|
+
const exercises = [];
|
|
17074
|
+
for (const node of nodes) {
|
|
17075
|
+
if (!node || typeof node !== "object") continue;
|
|
17076
|
+
const n = node;
|
|
17077
|
+
if (typeof n.id === "string") nodeById.set(n.id, n);
|
|
17078
|
+
if (n.type !== "framework_exercise") continue;
|
|
17079
|
+
const props = n.properties;
|
|
17080
|
+
if (!props || typeof props !== "object" || Array.isArray(props)) continue;
|
|
17081
|
+
const frameworkId = props.framework_id;
|
|
17082
|
+
if (typeof frameworkId !== "string") continue;
|
|
17083
|
+
const framework = UPG_FRAMEWORKS_BY_ID[frameworkId];
|
|
17084
|
+
if (!framework) continue;
|
|
17085
|
+
if (typeof n.id === "string") exercises.push({ id: n.id, framework });
|
|
17086
|
+
}
|
|
17087
|
+
if (exercises.length === 0) return;
|
|
17088
|
+
const exerciseById = new Map(exercises.map((e) => [e.id, e.framework]));
|
|
17089
|
+
edges.forEach((edge, i) => {
|
|
17090
|
+
if (!edge || typeof edge !== "object") return;
|
|
17091
|
+
const e = edge;
|
|
17092
|
+
if (e.type !== "framework_exercise_includes_node") return;
|
|
17093
|
+
if (typeof e.source !== "string" || typeof e.target !== "string") return;
|
|
17094
|
+
const framework = exerciseById.get(e.source);
|
|
17095
|
+
if (!framework) return;
|
|
17096
|
+
const score = e.properties;
|
|
17097
|
+
if (!score || typeof score !== "object" || Array.isArray(score)) return;
|
|
17098
|
+
const targetNode = nodeById.get(e.target);
|
|
17099
|
+
const targetType = typeof targetNode?.type === "string" ? targetNode.type : void 0;
|
|
17100
|
+
if (!targetType) return;
|
|
17101
|
+
const requirements = framework.data?.required_properties?.[targetType];
|
|
17102
|
+
if (!requirements || requirements.length === 0) return;
|
|
17103
|
+
const divisors = divisorInputs(framework);
|
|
17104
|
+
const path = `$.edges[${i}].properties`;
|
|
17105
|
+
const scoreObj = score;
|
|
17106
|
+
for (const req of requirements) {
|
|
17107
|
+
const value = scoreObj[req.property];
|
|
17108
|
+
if (value === void 0 || value === null) continue;
|
|
17109
|
+
const finding = checkScoreValue(req, value, divisors.has(req.property), framework.id);
|
|
17110
|
+
if (finding) {
|
|
17111
|
+
warnings.push({
|
|
17112
|
+
path: `${path}.${req.property}`,
|
|
17113
|
+
message: finding,
|
|
17114
|
+
rule: "framework-score"
|
|
17115
|
+
});
|
|
17116
|
+
}
|
|
16842
17117
|
}
|
|
16843
|
-
}
|
|
16844
|
-
|
|
16845
|
-
|
|
16846
|
-
|
|
16847
|
-
|
|
16848
|
-
|
|
16849
|
-
|
|
16850
|
-
|
|
16851
|
-
|
|
16852
|
-
|
|
16853
|
-
|
|
16854
|
-
|
|
16855
|
-
|
|
16856
|
-
|
|
16857
|
-
|
|
16858
|
-
|
|
16859
|
-
"
|
|
16860
|
-
|
|
16861
|
-
"tags": [
|
|
16862
|
-
"team_process",
|
|
16863
|
-
"reflection",
|
|
16864
|
-
"consequences",
|
|
16865
|
-
"tree"
|
|
16866
|
-
],
|
|
16867
|
-
"slots": [
|
|
16868
|
-
{
|
|
16869
|
-
"label": "First-order move",
|
|
16870
|
-
"entityTypeId": "decision",
|
|
16871
|
-
"description": "The decision or move under consideration."
|
|
16872
|
-
},
|
|
16873
|
-
{
|
|
16874
|
-
"label": "Second-order consequences",
|
|
16875
|
-
"entityTypeId": "insight",
|
|
16876
|
-
"description": "Downstream effects that follow from the first-order move."
|
|
16877
|
-
},
|
|
16878
|
-
{
|
|
16879
|
-
"label": "Higher-order consequences",
|
|
16880
|
-
"entityTypeId": "insight",
|
|
16881
|
-
"description": "Third-, fourth-, fifth-order ripples: second-order consequences of the second-order consequences."
|
|
17118
|
+
});
|
|
17119
|
+
}
|
|
17120
|
+
function divisorInputs(framework) {
|
|
17121
|
+
const expr = framework.data?.computed_properties?.[0]?.expression;
|
|
17122
|
+
if (typeof expr !== "string" || expr.length === 0) return EMPTY_STRING_SET;
|
|
17123
|
+
const out = /* @__PURE__ */ new Set();
|
|
17124
|
+
const re = /\/\s*\(?\s*([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
17125
|
+
let m;
|
|
17126
|
+
while ((m = re.exec(expr)) !== null) out.add(m[1]);
|
|
17127
|
+
return out;
|
|
17128
|
+
}
|
|
17129
|
+
var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
|
|
17130
|
+
function checkScoreValue(req, value, isDivisor, frameworkId) {
|
|
17131
|
+
switch (req.type) {
|
|
17132
|
+
case "enum": {
|
|
17133
|
+
const allowed = req.enum_values ?? [];
|
|
17134
|
+
if (typeof value !== "string") {
|
|
17135
|
+
return `${frameworkId} score "${req.property}" must be one of ${allowed.join(", ")}; got ${describeKind(value)}.`;
|
|
16882
17136
|
}
|
|
16883
|
-
|
|
16884
|
-
|
|
16885
|
-
|
|
16886
|
-
|
|
16887
|
-
|
|
16888
|
-
|
|
16889
|
-
|
|
16890
|
-
|
|
16891
|
-
|
|
16892
|
-
|
|
17137
|
+
if (allowed.length > 0 && !allowed.includes(value)) {
|
|
17138
|
+
return `${frameworkId} score "${req.property}" has value "${value}" outside its allowed set: ${allowed.join(", ")}.`;
|
|
17139
|
+
}
|
|
17140
|
+
return null;
|
|
17141
|
+
}
|
|
17142
|
+
case "assessment": {
|
|
17143
|
+
const num = numericScore(value);
|
|
17144
|
+
if (num === null) {
|
|
17145
|
+
return `${frameworkId} score "${req.property}" must be a number; got ${describeKind(value)}.`;
|
|
17146
|
+
}
|
|
17147
|
+
const scale = req.scale_id ? getScale(req.scale_id) : void 0;
|
|
17148
|
+
if (scale) {
|
|
17149
|
+
if (num < scale.min || num > scale.max) {
|
|
17150
|
+
return `${frameworkId} score "${req.property}" is ${num}, outside the ${req.scale_id} scale range [${scale.min}, ${scale.max}].`;
|
|
16893
17151
|
}
|
|
16894
|
-
|
|
16895
|
-
|
|
16896
|
-
|
|
16897
|
-
|
|
16898
|
-
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
|
|
16907
|
-
|
|
16908
|
-
|
|
16909
|
-
|
|
16910
|
-
|
|
16911
|
-
"
|
|
16912
|
-
|
|
16913
|
-
|
|
16914
|
-
|
|
16915
|
-
|
|
16916
|
-
|
|
16917
|
-
|
|
16918
|
-
|
|
16919
|
-
|
|
16920
|
-
|
|
16921
|
-
|
|
16922
|
-
"
|
|
16923
|
-
|
|
16924
|
-
|
|
16925
|
-
|
|
16926
|
-
]
|
|
17152
|
+
} else if (num <= 0) {
|
|
17153
|
+
return `${frameworkId} score "${req.property}" must be greater than 0; got ${num}.`;
|
|
17154
|
+
}
|
|
17155
|
+
if (isDivisor && num === 0) {
|
|
17156
|
+
return `${frameworkId} score "${req.property}" must not be 0 (it is a divisor in the framework's formula).`;
|
|
17157
|
+
}
|
|
17158
|
+
return null;
|
|
17159
|
+
}
|
|
17160
|
+
case "number": {
|
|
17161
|
+
const num = numericScore(value);
|
|
17162
|
+
if (num === null) {
|
|
17163
|
+
return `${frameworkId} score "${req.property}" must be a number; got ${describeKind(value)}.`;
|
|
17164
|
+
}
|
|
17165
|
+
if (isDivisor && num === 0) {
|
|
17166
|
+
return `${frameworkId} score "${req.property}" must not be 0 (it is a divisor in the framework's formula).`;
|
|
17167
|
+
}
|
|
17168
|
+
if (num < 0) {
|
|
17169
|
+
return `${frameworkId} score "${req.property}" must not be negative; got ${num}.`;
|
|
17170
|
+
}
|
|
17171
|
+
return null;
|
|
17172
|
+
}
|
|
17173
|
+
case "boolean": {
|
|
17174
|
+
if (typeof value !== "boolean") {
|
|
17175
|
+
return `${frameworkId} score "${req.property}" must be a boolean; got ${describeKind(value)}.`;
|
|
17176
|
+
}
|
|
17177
|
+
return null;
|
|
17178
|
+
}
|
|
17179
|
+
case "string": {
|
|
17180
|
+
if (typeof value !== "string") {
|
|
17181
|
+
return `${frameworkId} score "${req.property}" must be a string; got ${describeKind(value)}.`;
|
|
17182
|
+
}
|
|
17183
|
+
return null;
|
|
16927
17184
|
}
|
|
17185
|
+
default:
|
|
17186
|
+
return null;
|
|
16928
17187
|
}
|
|
16929
|
-
|
|
16930
|
-
|
|
16931
|
-
|
|
16932
|
-
)
|
|
16933
|
-
|
|
16934
|
-
|
|
16935
|
-
|
|
16936
|
-
|
|
17188
|
+
}
|
|
17189
|
+
function numericScore(value) {
|
|
17190
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
17191
|
+
if (typeof value === "string") {
|
|
17192
|
+
const n = Number.parseFloat(value);
|
|
17193
|
+
return Number.isFinite(n) ? n : null;
|
|
17194
|
+
}
|
|
17195
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
17196
|
+
const v = value.value;
|
|
17197
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
17198
|
+
}
|
|
17199
|
+
return null;
|
|
17200
|
+
}
|
|
17201
|
+
function runtimeKind(val) {
|
|
17202
|
+
if (val === null || val === void 0) return null;
|
|
17203
|
+
if (Array.isArray(val)) return "string[]";
|
|
17204
|
+
switch (typeof val) {
|
|
17205
|
+
case "string":
|
|
17206
|
+
return "string";
|
|
17207
|
+
case "number":
|
|
17208
|
+
return "number";
|
|
17209
|
+
case "boolean":
|
|
17210
|
+
return "boolean";
|
|
17211
|
+
case "object":
|
|
17212
|
+
return "object";
|
|
17213
|
+
default:
|
|
17214
|
+
return null;
|
|
17215
|
+
}
|
|
17216
|
+
}
|
|
17217
|
+
function valueMatchesType(defType, val) {
|
|
17218
|
+
const kind = runtimeKind(val);
|
|
17219
|
+
if (kind === null) return true;
|
|
17220
|
+
switch (defType) {
|
|
17221
|
+
case "string":
|
|
17222
|
+
return kind === "string";
|
|
17223
|
+
case "number":
|
|
17224
|
+
return kind === "number";
|
|
17225
|
+
case "boolean":
|
|
17226
|
+
return kind === "boolean";
|
|
17227
|
+
case "string[]":
|
|
17228
|
+
return kind === "string[]";
|
|
17229
|
+
case "object":
|
|
17230
|
+
case "assessment":
|
|
17231
|
+
return kind === "object";
|
|
17232
|
+
default:
|
|
17233
|
+
return true;
|
|
17234
|
+
}
|
|
17235
|
+
}
|
|
17236
|
+
function describeKind(val) {
|
|
17237
|
+
if (Array.isArray(val)) return "array";
|
|
17238
|
+
return typeof val;
|
|
17239
|
+
}
|
|
17240
|
+
function validateNodeProperties(node, basePath, warnings) {
|
|
17241
|
+
const type = node.type;
|
|
17242
|
+
if (typeof type !== "string") return;
|
|
17243
|
+
const schema = getPropertySchema(type);
|
|
17244
|
+
if (!schema) return;
|
|
17245
|
+
const props = node.properties;
|
|
17246
|
+
if (!props || typeof props !== "object" || Array.isArray(props)) return;
|
|
17247
|
+
for (const [key, value] of Object.entries(props)) {
|
|
17248
|
+
const def = schema[key];
|
|
17249
|
+
if (!def) continue;
|
|
17250
|
+
if (value === null || value === void 0) continue;
|
|
17251
|
+
if (!valueMatchesType(def.type, value)) {
|
|
17252
|
+
warnings.push({
|
|
17253
|
+
path: `${basePath}.properties.${key}`,
|
|
17254
|
+
message: `Property "${key}" on ${type} should be ${def.type}, got ${describeKind(value)}.`,
|
|
17255
|
+
rule: "property-type"
|
|
17256
|
+
});
|
|
17257
|
+
continue;
|
|
17258
|
+
}
|
|
17259
|
+
if (def.enum && typeof value === "string" && !def.enum.includes(value)) {
|
|
17260
|
+
warnings.push({
|
|
17261
|
+
path: `${basePath}.properties.${key}`,
|
|
17262
|
+
message: `Property "${key}" on ${type} has value "${value}" outside its allowed set: ${def.enum.join(", ")}.`,
|
|
17263
|
+
rule: "property-enum"
|
|
17264
|
+
});
|
|
17265
|
+
}
|
|
17266
|
+
}
|
|
17267
|
+
}
|
|
17268
|
+
function isUPGDocument(doc) {
|
|
17269
|
+
return validateUPGDocument(doc).valid;
|
|
16937
17270
|
}
|
|
16938
17271
|
|
|
16939
17272
|
// src/presentation/labels.ts
|