@unified-product-graph/mcp-server 0.8.0 → 0.8.2
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 +11 -0
- package/README.md +1 -1
- package/TOOLS.md +20 -14
- package/dist/index.js +1289 -485
- package/dist/index.js.map +1 -1
- package/dist/tools-manifest.json +95 -76
- package/package.json +1 -1
- package/scripts/claudemd-snippet.md +7 -7
- package/scripts/install-skills.sh +2 -2
- package/skills/upg/SKILL.md +41 -41
- package/skills/{upg-gaps → upg-check-gaps}/SKILL.md +40 -43
- package/skills/{upg-schema-health → upg-check-schema}/SKILL.md +7 -7
- package/skills/{upg-schema-evolve → upg-check-schema-coverage}/SKILL.md +12 -12
- package/skills/{upg-schema-edges → upg-check-schema-edges}/SKILL.md +3 -3
- package/skills/{upg-schema-consolidate → upg-check-schema-merge}/SKILL.md +5 -5
- package/skills/upg-context/SKILL.md +96 -72
- package/skills/upg-context-intelligence/SKILL.md +23 -27
- package/skills/upg-design-system/SKILL.md +21 -26
- package/skills/{upg-verify → upg-find-untracked}/SKILL.md +7 -12
- package/skills/{upg-rollback → upg-fix-rollback}/SKILL.md +6 -12
- package/skills/{upg-migrate → upg-fix-types}/SKILL.md +5 -9
- package/skills/upg-link/SKILL.md +125 -0
- package/skills/{upg-discover → upg-new-discovery}/SKILL.md +42 -58
- package/skills/{upg-capture → upg-new-from-session}/SKILL.md +13 -15
- package/skills/{upg-template → upg-new-from-template}/SKILL.md +8 -12
- package/skills/{upg-init → upg-new-graph}/SKILL.md +50 -82
- package/skills/{upg-hypothesis → upg-new-hypothesis}/SKILL.md +27 -36
- package/skills/{upg-launch → upg-new-launch}/SKILL-DETAIL.md +36 -92
- package/skills/{upg-launch → upg-new-launch}/SKILL.md +8 -18
- package/skills/{upg-okr → upg-new-okr}/SKILL-DETAIL.md +28 -46
- package/skills/{upg-okr → upg-new-okr}/SKILL.md +3 -3
- package/skills/{upg-persona → upg-new-persona}/SKILL.md +35 -67
- package/skills/{upg-research → upg-new-research}/SKILL.md +25 -33
- package/skills/{upg-schema-update → upg-new-schema-type}/SKILL.md +2 -2
- package/skills/{upg-strategy → upg-new-strategy}/SKILL.md +21 -27
- package/skills/upg-prioritise/SKILL.md +4 -4
- package/skills/upg-reflect/SKILL.md +7 -7
- package/skills/{upg-feedback → upg-send-feedback}/SKILL.md +30 -51
- package/skills/{upg-diff → upg-show-diff}/SKILL.md +6 -12
- package/skills/{upg-inspect → upg-show-entity}/SKILL.md +7 -9
- package/skills/{upg-impact → upg-show-impact}/SKILL.md +11 -15
- package/skills/{upg-journey → upg-show-journey}/SKILL.md +31 -32
- package/skills/{upg-analytics → upg-show-metrics}/SKILL.md +9 -12
- package/skills/{upg-schema-changelog → upg-show-schema-changelog}/SKILL.md +5 -5
- package/skills/{upg-status → upg-show-status}/SKILL.md +39 -40
- package/skills/{upg-tree → upg-show-tree}/SKILL.md +15 -15
- package/skills/{upg-export → upg-sync-export}/SKILL.md +10 -13
- package/skills/{upg-import → upg-sync-import}/SKILL.md +7 -13
- package/skills/{upg-pull → upg-sync-pull}/SKILL-DETAIL.md +13 -17
- package/skills/{upg-pull → upg-sync-pull}/SKILL.md +3 -3
- package/skills/{upg-push → upg-sync-push}/SKILL-DETAIL.md +4 -10
- package/skills/{upg-push → upg-sync-push}/SKILL.md +4 -4
- package/skills/{upg-snapshot → upg-sync-snapshot}/SKILL.md +2 -6
- package/skills/upg-trace/SKILL.md +7 -7
- package/skills/{upg-workspace → upg-use-workspace}/SKILL.md +8 -14
- package/skills/{upg-run → upg-walk-playbook}/SKILL.md +14 -14
- package/skills/upg-walk-region/SKILL-DETAIL.md +320 -0
- package/skills/upg-walk-region/SKILL.md +89 -0
- package/skills/upg-connect/SKILL.md +0 -167
- package/skills/upg-explore/SKILL-DETAIL.md +0 -481
- package/skills/upg-explore/SKILL.md +0 -297
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: upg-
|
|
2
|
+
name: upg-new-graph
|
|
3
3
|
description: "Initialize a UPG Product Graph"
|
|
4
4
|
user-invocable: true
|
|
5
5
|
argument-hint: "[description]"
|
|
6
6
|
category: tooling
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# /upg-
|
|
9
|
+
# /upg-new-graph: Initialize a Unified Product Graph
|
|
10
10
|
|
|
11
11
|
You are a product discovery guide. Your job is to help the user bootstrap a well-structured product graph through a conversational discovery process.
|
|
12
12
|
|
|
@@ -29,6 +29,8 @@ Display as: **"Phase 2 of 4: Your user"**
|
|
|
29
29
|
|
|
30
30
|
Use the `mcp__unified-product-graph__*` MCP tools (create_node, create_edge, get_product_context, search_nodes).
|
|
31
31
|
|
|
32
|
+
> **MCP-first (applies to every create below).** Before creating any entity (product, persona, desired outcome, job, need, outcome, metric, hypothesis), call `get_entity_schema(<type>)`. Build `properties` from its `expected_properties`, set `status` **top-level** from one of the lifecycle phases the schema returns (don't hard-code the status enum), and pass any assessment as `{ value, label }`. For the chain children under a persona, confirm the valid child types with `get_valid_children("persona")`. Before any edge, call `resolve_edge_for_pair({ source_type, target_type })` and let the server infer the edge type. The payloads below show shape and intent; the authoritative keys, phases, and stage values come from the schema/spec at runtime.
|
|
33
|
+
|
|
32
34
|
## CRITICAL RULES
|
|
33
35
|
|
|
34
36
|
### Rule 1: One Question Per Message
|
|
@@ -88,41 +90,47 @@ STOP. Wait for the answer.
|
|
|
88
90
|
Ask: **"How far along is <product name>?"**
|
|
89
91
|
|
|
90
92
|
```
|
|
91
|
-
1. 💭
|
|
92
|
-
2.
|
|
93
|
-
3.
|
|
94
|
-
4.
|
|
93
|
+
1. 💭 Concept; still figuring it out
|
|
94
|
+
2. 🔬 Validation; testing demand before building
|
|
95
|
+
3. 🛠️ Build; building the first version
|
|
96
|
+
4. 🧪 Beta; early users, iterating
|
|
97
|
+
5. 🚀 Launch; generally available
|
|
98
|
+
6. 📈 Growth; scaling users and revenue
|
|
99
|
+
7. 🏗️ Mature; established, optimizing
|
|
95
100
|
```
|
|
96
101
|
|
|
97
|
-
STOP. Wait. Then create the product node:
|
|
102
|
+
Map the answer to a canonical product-stage value. **Don't hard-code the stage enum**: call `list_product_stages` (or read the `stage` property off `get_entity_schema("product")`) for the current valid values, and pick the one matching the user's answer. STOP. Wait. Then create the product node:
|
|
98
103
|
|
|
99
104
|
```
|
|
105
|
+
// Read get_entity_schema("product") / list_product_stages first, then:
|
|
100
106
|
create_node({
|
|
101
107
|
type: "product",
|
|
102
108
|
title: "<name>",
|
|
103
109
|
description: "<their vision one-liner>",
|
|
104
|
-
properties: { stage: "<stage>" }
|
|
110
|
+
properties: { stage: "<canonical stage from list_product_stages>" }
|
|
105
111
|
})
|
|
106
112
|
```
|
|
107
113
|
|
|
114
|
+
Pass a canonical stage value — `create_node` (like `create_product`) rejects non-canonical values; the live list is the source of truth.
|
|
115
|
+
|
|
108
116
|
Confirm: "🎯 **<Product Name>** is in the graph." Then move to Step 1d.
|
|
109
117
|
|
|
110
118
|
### Step 1d: Lens (infer, don't ask)
|
|
111
119
|
|
|
112
|
-
**Do not ask about lenses.** Infer the lens from what the user said about their product and role:
|
|
120
|
+
**Do not ask about lenses.** Infer the lens from what the user said about their product and role. **Fetch the valid lens ids first** with `list_lenses` (don't assume the id strings) and match intent to one of them:
|
|
113
121
|
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
- Otherwise → default
|
|
122
|
+
- Engineering signals (bugs, services, APIs, architecture, "I'm an engineer") → the engineering lens
|
|
123
|
+
- Design signals (screens, components, flows, "I'm a designer") → the design/UX lens id `list_lenses` returns
|
|
124
|
+
- Growth signals (funnels, channels, campaigns) → the growth lens
|
|
125
|
+
- Otherwise → the default product lens
|
|
118
126
|
|
|
119
|
-
Set silently:
|
|
127
|
+
Set silently (using the exact id from `list_lenses`):
|
|
120
128
|
|
|
121
129
|
```
|
|
122
130
|
update_session_context({ lens: "<inferred_lens>" })
|
|
123
131
|
```
|
|
124
132
|
|
|
125
|
-
No confirmation needed. The user discovers lenses naturally through `/upg` or `/upg-status` later. Cold-start users don't need lens vocabulary on their first run.
|
|
133
|
+
No confirmation needed. The user discovers lenses naturally through `/upg` or `/upg-show-status` later. Cold-start users don't need lens vocabulary on their first run.
|
|
126
134
|
|
|
127
135
|
### Step 2: Persona: Who
|
|
128
136
|
|
|
@@ -158,7 +166,7 @@ STOP. Wait for the answer.
|
|
|
158
166
|
|
|
159
167
|
### Step 2c: Persona: Desired Outcomes
|
|
160
168
|
|
|
161
|
-
> **
|
|
169
|
+
> **Chain model:** desired outcomes are SEPARATE nodes connected to the persona (resolve the edge with `resolve_edge_for_pair` at create time). Never inline them as a `goals` array on the persona.
|
|
162
170
|
|
|
163
171
|
Ask: **"What outcomes is <Name> trying to achieve? What does success look like for them?"**
|
|
164
172
|
|
|
@@ -193,7 +201,7 @@ STOP. Wait for the answer.
|
|
|
193
201
|
|
|
194
202
|
### Step 2e: Persona: Needs
|
|
195
203
|
|
|
196
|
-
> **
|
|
204
|
+
> **Chain model:** needs are SEPARATE nodes connected to the persona (resolve the edge with `resolve_edge_for_pair` at create time). Never inline as a `frustrations` array.
|
|
197
205
|
|
|
198
206
|
Ask: **"What gets in <Name>'s way today? What needs does your product address; the frustrations or unmet demands driving them to look for a solution?"**
|
|
199
207
|
|
|
@@ -213,60 +221,31 @@ STOP. Wait.
|
|
|
213
221
|
|
|
214
222
|
**Vibe check:** Before creating, show a brief summary and ask: "Here's what I've captured about **<Name>**: anything you'd change?"
|
|
215
223
|
|
|
216
|
-
Then create the persona node
|
|
224
|
+
Then create the persona node. **Read `get_entity_schema("persona")` first** and build `properties` from its `expected_properties` (don't assume a fixed allowlist). **Never set `goals` or `frustrations` on the persona**: those are separate chain nodes connected by edges. Confirm the chain child types with `get_valid_children("persona")` and resolve each edge with `resolve_edge_for_pair`:
|
|
217
225
|
|
|
218
226
|
```
|
|
219
|
-
// 1. Create the persona
|
|
227
|
+
// 1. Create the persona — properties from get_entity_schema("persona")
|
|
220
228
|
create_node({
|
|
221
229
|
type: "persona",
|
|
222
230
|
title: "<Name>; <Role>",
|
|
223
231
|
description: "<narrative combining context and motivation>",
|
|
224
|
-
properties: {
|
|
225
|
-
context: "<their world>",
|
|
226
|
-
motivation: "<what drives them>"
|
|
227
|
-
},
|
|
232
|
+
properties: { /* keys from the schema, e.g. context, motivation */ },
|
|
228
233
|
parent_id: "<product_id>"
|
|
229
234
|
})
|
|
230
235
|
// → persona_id = result.node.id
|
|
231
236
|
|
|
232
|
-
// 2. For each desired outcome
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
description: "<why this outcome matters to them>",
|
|
237
|
-
parent_id: "<persona_id>"
|
|
238
|
-
})
|
|
239
|
-
create_edge({
|
|
240
|
-
source_id: "<persona_id>",
|
|
241
|
-
target_id: "<desired_outcome_id>",
|
|
242
|
-
type: "persona_aspires_to_desired_outcome"
|
|
243
|
-
})
|
|
237
|
+
// 2. For each desired outcome — child type from get_valid_children("persona"),
|
|
238
|
+
// edge from resolve_edge_for_pair({ source_type: "persona", target_type: <child> }):
|
|
239
|
+
create_node({ /* type from schema */ title: "<outcome>", parent_id: "<persona_id>" })
|
|
240
|
+
create_edge({ source_id: "<persona_id>", target_id: "<child_id>" }) // server infers type
|
|
244
241
|
|
|
245
|
-
// 3. Create the job node (the domain anchor)
|
|
246
|
-
create_node({
|
|
247
|
-
|
|
248
|
-
title: "<concise job statement>",
|
|
249
|
-
description: "<When I… I want to… So I can… statement>",
|
|
250
|
-
parent_id: "<persona_id>"
|
|
251
|
-
})
|
|
252
|
-
create_edge({
|
|
253
|
-
source_id: "<persona_id>",
|
|
254
|
-
target_id: "<job_id>",
|
|
255
|
-
type: "persona_pursues_job"
|
|
256
|
-
})
|
|
242
|
+
// 3. Create the job node (the domain anchor) — read its schema first:
|
|
243
|
+
create_node({ type: "job", title: "<concise job statement>", parent_id: "<persona_id>" })
|
|
244
|
+
create_edge({ source_id: "<persona_id>", target_id: "<job_id>" }) // server infers type
|
|
257
245
|
|
|
258
|
-
// 4. For each need
|
|
259
|
-
create_node({
|
|
260
|
-
|
|
261
|
-
title: "<need statement>",
|
|
262
|
-
description: "<the specific frustration or unmet demand>",
|
|
263
|
-
parent_id: "<persona_id>"
|
|
264
|
-
})
|
|
265
|
-
create_edge({
|
|
266
|
-
source_id: "<persona_id>",
|
|
267
|
-
target_id: "<need_id>",
|
|
268
|
-
type: "persona_experiences_need"
|
|
269
|
-
})
|
|
246
|
+
// 4. For each need — same pattern (child type + edge resolved live):
|
|
247
|
+
create_node({ /* need type from schema */ title: "<need statement>", parent_id: "<persona_id>" })
|
|
248
|
+
create_edge({ source_id: "<persona_id>", target_id: "<need_id>" }) // server infers type
|
|
270
249
|
```
|
|
271
250
|
|
|
272
251
|
Tip: for 3+ chain nodes, use `batch_create_nodes` with `parent_ref: "$0"` chaining instead of N individual `create_node` calls.
|
|
@@ -297,6 +276,7 @@ Offer outcome options based on the product vision and persona:
|
|
|
297
276
|
STOP. Wait. Then create the outcome:
|
|
298
277
|
|
|
299
278
|
```
|
|
279
|
+
// Read get_entity_schema("outcome") first, then:
|
|
300
280
|
create_node({
|
|
301
281
|
type: "outcome",
|
|
302
282
|
title: "<measurable outcome>",
|
|
@@ -318,18 +298,13 @@ Offer metric options relevant to the outcome:
|
|
|
318
298
|
4. Different metric; what would you track?
|
|
319
299
|
```
|
|
320
300
|
|
|
321
|
-
STOP. Wait. Create the KPI
|
|
301
|
+
STOP. Wait. Create the KPI as a `metric` node. Read `get_entity_schema("metric")` first and build `properties` from its keys (the KPI designation, current/target value, unit):
|
|
322
302
|
|
|
323
303
|
```
|
|
324
304
|
create_node({
|
|
325
305
|
type: "metric",
|
|
326
306
|
title: "<metric name>",
|
|
327
|
-
properties: {
|
|
328
|
-
designation: "kpi",
|
|
329
|
-
current_value: <if known>,
|
|
330
|
-
target_value: <if known>,
|
|
331
|
-
unit: "<e.g. %, users, seconds>"
|
|
332
|
-
},
|
|
307
|
+
properties: { /* keys from the schema; mark this metric as a KPI per its designation property */ },
|
|
333
308
|
parent_id: "<outcome_id>"
|
|
334
309
|
})
|
|
335
310
|
```
|
|
@@ -349,18 +324,14 @@ Offer hypothesis options based on everything so far:
|
|
|
349
324
|
4. Different bet; what do you believe will work?
|
|
350
325
|
```
|
|
351
326
|
|
|
352
|
-
STOP. Wait. Create the hypothesis:
|
|
327
|
+
STOP. Wait. Create the hypothesis. Read `get_entity_schema("hypothesis")` first; build `properties` from its keys and set `status` top-level from its first lifecycle (draft) phase:
|
|
353
328
|
|
|
354
329
|
```
|
|
355
330
|
create_node({
|
|
356
331
|
type: "hypothesis",
|
|
357
332
|
title: "<concise hypothesis>",
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
will_result_in: "<the expected outcome>",
|
|
361
|
-
we_know_when: "<the success signal>",
|
|
362
|
-
status: "untested"
|
|
363
|
-
},
|
|
333
|
+
status: "<draft phase from the schema>",
|
|
334
|
+
properties: { /* keys from the schema: we-believe / will-result-in / we-know-when */ },
|
|
364
335
|
parent_id: "<outcome_id>"
|
|
365
336
|
})
|
|
366
337
|
```
|
|
@@ -377,13 +348,13 @@ Display what was created as an indented tree with entity type emojis:
|
|
|
377
348
|
🎯 <name> (<stage>)
|
|
378
349
|
├─ 👤 <persona name>
|
|
379
350
|
│ │ Context: <context>
|
|
380
|
-
│ ├─ 🎯 <desired outcome>
|
|
381
|
-
│ ├─ 💼 <job>
|
|
382
|
-
│ └─ ⚡ <need>
|
|
351
|
+
│ ├─ 🎯 <desired outcome>
|
|
352
|
+
│ ├─ 💼 <job>
|
|
353
|
+
│ └─ ⚡ <need>
|
|
383
354
|
├─ 🎯 <outcome>
|
|
384
355
|
│ └─ 📊 <metric> (<current> → <target>)
|
|
385
|
-
├─ ⚗️ <h1> ⚪
|
|
386
|
-
└─ ⚗️ <h2> ⚪
|
|
356
|
+
├─ ⚗️ <h1> ⚪ <draft phase>
|
|
357
|
+
└─ ⚗️ <h2> ⚪ <draft phase>
|
|
387
358
|
```
|
|
388
359
|
|
|
389
360
|
## Close with Smart Ending
|
|
@@ -394,11 +365,8 @@ Check the graph for the biggest gap across the 8 business areas (Identity, Under
|
|
|
394
365
|
|
|
395
366
|
> Based on what we built, your biggest gap is **[area]**. I'd suggest running `/upg-[skill]` next to [reason].
|
|
396
367
|
>
|
|
397
|
-
> Or run `/upg-journey` to see where you are in the bigger picture.
|
|
368
|
+
> Or run `/upg-show-journey` to see where you are in the bigger picture.
|
|
398
369
|
|
|
399
|
-
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
|
400
|
-
Your `.upg` file is yours: open standard, portable, git-friendly.
|
|
401
|
-
unifiedproductgraph.org
|
|
402
370
|
|
|
403
371
|
## Key Principles
|
|
404
372
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: upg-hypothesis
|
|
2
|
+
name: upg-new-hypothesis
|
|
3
3
|
description: "Structured Hypothesis Creation"
|
|
4
4
|
user-invocable: true
|
|
5
5
|
argument-hint: "[description]"
|
|
6
6
|
category: cognitive
|
|
7
7
|
approaches: [plan]
|
|
8
|
-
playbooks: [discovery-validation
|
|
8
|
+
playbooks: [playbook:discovery-research-validation]
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
# /upg-hypothesis: Structured Hypothesis Creation
|
|
11
|
+
# /upg-new-hypothesis: Structured Hypothesis Creation
|
|
12
12
|
|
|
13
|
-
Note: In user-facing conversation, use "bet" or "design experiment" instead of "hypothesis"; the word triggers "formal/academic" anxiety for non-PM users. The canonical entity type is `hypothesis
|
|
13
|
+
Note: In user-facing conversation, use "bet" or "design experiment" instead of "hypothesis"; the word triggers "formal/academic" anxiety for non-PM users. The canonical entity type is `hypothesis`. Evidence attaches via the edge `resolve_edge_for_pair` returns for the hypothesis→evidence pair, with the evidence node's direction carrying supports/refutes/neutral.
|
|
14
14
|
|
|
15
15
|
You are a Unified Product Graph validation specialist. Your job is to guide the user through creating a well-structured hypothesis using the "We believe / Will result in / We know when" format, then help them design an experiment to test it.
|
|
16
16
|
|
|
@@ -80,7 +80,7 @@ This should be falsifiable:
|
|
|
80
80
|
|
|
81
81
|
Ask: **"What's the riskiest assumption in this hypothesis? What's the one thing that, if wrong, kills the whole bet?"**
|
|
82
82
|
|
|
83
|
-
Use this to set the `
|
|
83
|
+
Use this to set the `risk_if_wrong` property and to inform experiment design. (The legacy `we_test_by` property was dropped in v0.2.8.)
|
|
84
84
|
|
|
85
85
|
### Step 3b: Vibe Check and Thresholds
|
|
86
86
|
|
|
@@ -94,39 +94,33 @@ Then ask for success/failure thresholds: "What would convince you this is workin
|
|
|
94
94
|
- If the hypothesis relates to an opportunity: ask "Does a solution already exist for this opportunity? If not, should I create one first?" Wait for the answer. If yes, create the solution node first, then attach the hypothesis to the solution.
|
|
95
95
|
- The hierarchy is: opportunity → solution → hypothesis. Don't skip the solution layer.
|
|
96
96
|
|
|
97
|
+
**MCP-first.** Call `get_entity_schema("hypothesis")` before creating. Build `properties` from its `expected_properties` (the "We believe / Will result in / We know when / riskiest assumption" answers map onto those keys), and set `status` **top-level** (not inside `properties`) using one of the lifecycle phases the schema returns. Don't hard-code the status enum or property keys from memory; the schema is the source of truth.
|
|
98
|
+
|
|
97
99
|
```
|
|
100
|
+
// Read get_entity_schema("hypothesis") first, then:
|
|
98
101
|
create_node({
|
|
99
102
|
type: "hypothesis",
|
|
100
103
|
title: "<concise hypothesis; e.g. 'Onboarding wizard reduces Day-1 drop-off'>",
|
|
101
104
|
description: "<full narrative combining all three parts>",
|
|
102
|
-
properties: {
|
|
103
|
-
|
|
104
|
-
will_result_in: "<the measurable outcome>",
|
|
105
|
-
we_know_when: "<the success signal and threshold>",
|
|
106
|
-
},
|
|
107
|
-
// Canonical lifecycle on UPGBaseNode.status (top-level, not in properties).
|
|
108
|
-
// hypothesis enum: drafted | active | validated | invalidated | archived.
|
|
109
|
-
// The legacy `we_test_by` property dropped in v0.2.8; experimental method
|
|
110
|
-
// now lives on the linked experiment_plan via `hypothesis_requires_experiment_plan`.
|
|
111
|
-
status: "drafted"
|
|
105
|
+
properties: { /* keys from get_entity_schema("hypothesis").expected_properties */ },
|
|
106
|
+
status: "<first lifecycle phase from the schema; typically the draft phase>"
|
|
112
107
|
})
|
|
113
108
|
```
|
|
114
109
|
|
|
115
110
|
Connect to a parent. The canonical OST chain is
|
|
116
|
-
**opportunity → solution → hypothesis
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
opportunity but no solution yet, surface a one-liner solution first
|
|
120
|
-
|
|
121
|
-
solution via `solution_proposes_hypothesis`.
|
|
111
|
+
**opportunity → solution → hypothesis**: solutions are the
|
|
112
|
+
articulated *approach* the hypothesis tests, so attach the hypothesis to a
|
|
113
|
+
solution rather than directly to an opportunity. If the user named an
|
|
114
|
+
opportunity but no solution yet, surface a one-liner solution first, then
|
|
115
|
+
attach the hypothesis to that solution.
|
|
122
116
|
|
|
123
|
-
-
|
|
124
|
-
-
|
|
117
|
+
- Resolve every link with `resolve_edge_for_pair({ source_type, target_type })` (e.g. `opportunity`→`solution`, then `solution`→`hypothesis`) and let the server infer the edge type, or use `parent_id: <solution_id>` for parent_ref auto-chaining.
|
|
118
|
+
- Do not skip the solution layer when starting from an opportunity.
|
|
125
119
|
|
|
126
120
|
### Step 5: Show the Result
|
|
127
121
|
|
|
128
122
|
```
|
|
129
|
-
### ⚗️ <Title> ⚪
|
|
123
|
+
### ⚗️ <Title> ⚪ drafted
|
|
130
124
|
|
|
131
125
|
**We believe that** <the change>
|
|
132
126
|
**will result in** <the measurable outcome>.
|
|
@@ -152,20 +146,17 @@ Offer experiment templates based on context:
|
|
|
152
146
|
| "The market is big enough" | Market sizing research, competitor analysis |
|
|
153
147
|
| "Users will trust/adopt this" | A/B test with behavioral tracking or longitudinal usage study |
|
|
154
148
|
|
|
155
|
-
If they describe an experiment, create it:
|
|
149
|
+
If they describe an experiment, create it as the canonical test-plan type a hypothesis links to (a later run-type entity records the actual execution). Find that child type via `get_valid_children("hypothesis")`, then **call `get_entity_schema(<that type>)`** before writing: drive `properties` from its `expected_properties` and set `status` top-level from its lifecycle phases.
|
|
156
150
|
|
|
157
151
|
```
|
|
152
|
+
// Read get_entity_schema for the experiment-plan type first, then:
|
|
158
153
|
create_node({
|
|
159
|
-
type: "experiment",
|
|
154
|
+
type: "<experiment-plan type from get_valid_children('hypothesis')>",
|
|
160
155
|
title: "<experiment name>",
|
|
161
156
|
description: "<what we're testing and how>",
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
start_date: "<if known>",
|
|
166
|
-
end_date: "<if known>"
|
|
167
|
-
},
|
|
168
|
-
parent_id: "<hypothesis_id>" // auto-creates hypothesis_has_experiment edge
|
|
157
|
+
status: "<draft phase from the schema>",
|
|
158
|
+
properties: { /* keys from the schema: method, success criteria, dates, etc. */ },
|
|
159
|
+
parent_id: "<hypothesis_id>" // parent_ref auto-chains the canonical edge
|
|
169
160
|
})
|
|
170
161
|
```
|
|
171
162
|
|
|
@@ -175,16 +166,16 @@ Check the graph for the biggest gap across the 8 business areas. Recommend ONE n
|
|
|
175
166
|
|
|
176
167
|
> Based on what we built, your biggest gap is **[area]**. I'd suggest running `/upg-[skill]` next to [reason].
|
|
177
168
|
>
|
|
178
|
-
> Or run `/upg-journey` to see where you are in the bigger picture.
|
|
169
|
+
> Or run `/upg-show-journey` to see where you are in the bigger picture.
|
|
179
170
|
|
|
180
171
|
After rendering your recommendation, call:
|
|
181
|
-
`update_session_context({ skill_invoked: "upg-hypothesis", recommendation: "<the next skill you recommended>" })`
|
|
172
|
+
`update_session_context({ skill_invoked: "upg-new-hypothesis", recommendation: "<the next skill you recommended>" })`
|
|
182
173
|
|
|
183
174
|
## Key Principles
|
|
184
175
|
|
|
185
176
|
- **Hypotheses must be falsifiable.** If there's no way to prove it wrong, it's not a hypothesis; it's a wish.
|
|
186
177
|
- **Specificity matters.** "Better retention" is not a hypothesis. "25% reduction in Day-7 churn for users who complete onboarding" is.
|
|
187
|
-
- **Status starts at "
|
|
178
|
+
- **Status starts at the draft phase** (read it from `get_entity_schema("hypothesis")`). Don't let anyone claim a validated phase without evidence from a 🧪 experiment.
|
|
188
179
|
- **Follow the design system.** Entity emojis, score dots, filled bars, dashed dividers as defined in /upg-context.
|
|
189
180
|
- **The riskiest assumption is the experiment target.** Don't test what's easy; test what's uncertain.
|
|
190
181
|
- **Always bridge to experiment.** A ⚗️ hypothesis without a 🧪 experiment plan is just a conversation.
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: upg-launch-detail
|
|
3
|
-
description: "Detailed discovery flow steps for /upg-launch"
|
|
3
|
+
description: "Detailed discovery flow steps for /upg-new-launch"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# /upg-launch: Discovery Flow (Detail)
|
|
6
|
+
# /upg-new-launch: Discovery Flow (Detail)
|
|
7
7
|
|
|
8
8
|
Loaded on demand when entering the guided launch planning flow.
|
|
9
9
|
|
|
10
|
+
> **MCP-first (applies to every create below).** Before creating any entity (GTM strategy, ICP, positioning, messaging, launch, acquisition channel, content strategy), call `get_entity_schema(<type>)`. Build `properties` from its `expected_properties`, set `status` **top-level** from one of the lifecycle phases the schema returns (don't hard-code the status enum), and pass any assessment as `{ value, label }`. Before any edge, call `resolve_edge_for_pair({ source_type, target_type })`: if it returns an edge, create it without an explicit `type:` (server infers); if it returns `null`, keep the relationship implicit (shared `parent_id`) rather than inventing an edge. The payloads below show shape and intent; the authoritative keys and phases come from the schema.
|
|
11
|
+
|
|
10
12
|
## Discovery Flow
|
|
11
13
|
|
|
12
14
|
### Starting Up
|
|
@@ -19,7 +21,7 @@ First, call `get_product_context` to understand the existing graph. Look for:
|
|
|
19
21
|
- Features and releases (what's being launched)
|
|
20
22
|
- Competitors (positioning context)
|
|
21
23
|
|
|
22
|
-
If the user passed an argument (e.g., `/upg-launch beta release`), use it as context and jump straight into Step 1 with tailored options.
|
|
24
|
+
If the user passed an argument (e.g., `/upg-new-launch beta release`), use it as context and jump straight into Step 1 with tailored options.
|
|
23
25
|
|
|
24
26
|
### Step 1: What Are You Launching?
|
|
25
27
|
|
|
@@ -43,27 +45,17 @@ STOP. Wait for the answer.
|
|
|
43
45
|
Create the GTM strategy container:
|
|
44
46
|
|
|
45
47
|
```
|
|
48
|
+
// Read get_entity_schema("gtm_strategy") first, then:
|
|
46
49
|
create_node({
|
|
47
50
|
type: "gtm_strategy",
|
|
48
51
|
title: "<Product Name> GTM; <launch description>",
|
|
49
52
|
description: "<what's being launched and why now>",
|
|
50
|
-
properties: {
|
|
51
|
-
launch_type: "<new_product | feature | release | pricing | expansion>",
|
|
52
|
-
target_date: "<if discussed>"
|
|
53
|
-
},
|
|
53
|
+
properties: { /* keys from the schema, e.g. launch type, target date */ },
|
|
54
54
|
parent_id: "<product_id>"
|
|
55
55
|
})
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
If an existing feature or release was selected,
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
create_edge({
|
|
62
|
-
source_id: "<gtm_strategy_id>",
|
|
63
|
-
target_id: "<feature_or_release_id>",
|
|
64
|
-
type: "launches"
|
|
65
|
-
})
|
|
66
|
-
```
|
|
58
|
+
If an existing feature or release was selected, link them through a `launch` entity rather than the GTM strategy directly: resolve the GTM-strategy→launch edge with `resolve_edge_for_pair`, and the launch then relates to the feature/release it ships. Always confirm a pair has a canonical edge via `resolve_edge_for_pair` before connecting; if it returns `null`, note the relationship in the description rather than inventing an edge.
|
|
67
59
|
|
|
68
60
|
Confirm: "📣 **GTM strategy started** for <launch description>."
|
|
69
61
|
|
|
@@ -75,8 +67,8 @@ Check for existing personas, ICPs, and market segments. Offer options:
|
|
|
75
67
|
|
|
76
68
|
```
|
|
77
69
|
1. <existing persona>; they're the primary audience
|
|
78
|
-
2. <existing ICP from `/upg-
|
|
79
|
-
3. <existing customer segment from `/upg-
|
|
70
|
+
2. <existing ICP from `/upg-walk-region market_intelligence`>; launch to the beachhead
|
|
71
|
+
3. <existing customer segment from `/upg-walk-region business_model`>; the paying segment
|
|
80
72
|
4. A new audience; <suggest based on launch type>
|
|
81
73
|
5. Different audience; tell me who this is for
|
|
82
74
|
6. Not sure yet; we can skip this or come back to it
|
|
@@ -87,28 +79,21 @@ STOP. Wait for the answer.
|
|
|
87
79
|
If they pick an existing entity, create an edge. If new, create an ICP:
|
|
88
80
|
|
|
89
81
|
```
|
|
82
|
+
// Read get_entity_schema("ideal_customer_profile") first, then:
|
|
90
83
|
create_node({
|
|
91
84
|
type: "ideal_customer_profile",
|
|
92
85
|
title: "<ICP name>",
|
|
93
86
|
description: "<who they are, why they care about this launch>",
|
|
94
|
-
properties: {
|
|
95
|
-
characteristics: ["<trait 1>", "<trait 2>", "<trait 3>"],
|
|
96
|
-
pain_level: "<how badly they need this>",
|
|
97
|
-
awareness: "<aware of problem | aware of solutions | aware of you>",
|
|
98
|
-
buying_stage: "<problem_aware | solution_aware | product_aware | most_aware>"
|
|
99
|
-
},
|
|
87
|
+
properties: { /* keys from the schema, e.g. characteristics, pain level, awareness, buying stage */ },
|
|
100
88
|
parent_id: "<gtm_strategy_id>"
|
|
101
89
|
})
|
|
102
90
|
```
|
|
103
91
|
|
|
104
|
-
Connect to existing persona if relevant:
|
|
92
|
+
Connect to existing persona if relevant; resolve the edge first:
|
|
105
93
|
|
|
106
94
|
```
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
target_id: "<persona_id>",
|
|
110
|
-
type: "represents"
|
|
111
|
-
})
|
|
95
|
+
// edge = resolve_edge_for_pair({ source_type: "ideal_customer_profile", target_type: "persona" })
|
|
96
|
+
create_edge({ source_id: "<icp_id>", target_id: "<persona_id>" }) // server infers type
|
|
112
97
|
```
|
|
113
98
|
|
|
114
99
|
Confirm: "🎯 **<ICP Name>** is the launch audience."
|
|
@@ -135,29 +120,21 @@ STOP. Wait for the answer.
|
|
|
135
120
|
Create the positioning entity:
|
|
136
121
|
|
|
137
122
|
```
|
|
123
|
+
// Read get_entity_schema("positioning") first, then:
|
|
138
124
|
create_node({
|
|
139
125
|
type: "positioning",
|
|
140
126
|
title: "<positioning statement>",
|
|
141
127
|
description: "<expanded positioning narrative>",
|
|
142
|
-
properties: {
|
|
143
|
-
framework: "<category | problem | competitive | new_category>",
|
|
144
|
-
for_who: "<target audience>",
|
|
145
|
-
unlike: "<the alternative or status quo>",
|
|
146
|
-
we_are: "<what you are>",
|
|
147
|
-
because: "<key differentiator>"
|
|
148
|
-
},
|
|
128
|
+
properties: { /* keys from the schema, e.g. framework, for-who, unlike, we-are, because */ },
|
|
149
129
|
parent_id: "<gtm_strategy_id>"
|
|
150
130
|
})
|
|
151
131
|
```
|
|
152
132
|
|
|
153
|
-
If competitors exist in the graph,
|
|
133
|
+
If competitors exist in the graph, resolve the edge first:
|
|
154
134
|
|
|
155
135
|
```
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
target_id: "<competitor_id>",
|
|
159
|
-
type: "differentiates_from"
|
|
160
|
-
})
|
|
136
|
+
// edge = resolve_edge_for_pair({ source_type: "positioning", target_type: "competitor" })
|
|
137
|
+
create_edge({ source_id: "<positioning_id>", target_id: "<competitor_id>" }) // server infers type
|
|
161
138
|
```
|
|
162
139
|
|
|
163
140
|
Confirm: "🎯 **Positioning locked in**: <brief summary>."
|
|
@@ -182,29 +159,17 @@ STOP. Wait for the answer.
|
|
|
182
159
|
Create the messaging entity:
|
|
183
160
|
|
|
184
161
|
```
|
|
162
|
+
// Read get_entity_schema("messaging") first, then:
|
|
185
163
|
create_node({
|
|
186
164
|
type: "messaging",
|
|
187
165
|
title: "<headline message>",
|
|
188
166
|
description: "<expanded messaging; the full narrative>",
|
|
189
|
-
properties: {
|
|
190
|
-
headline: "<the one-liner>",
|
|
191
|
-
subheadline: "<supporting detail>",
|
|
192
|
-
proof_points: ["<proof 1>", "<proof 2>", "<proof 3>"],
|
|
193
|
-
tone: "<inspiring | practical | bold | reassuring | playful>"
|
|
194
|
-
},
|
|
167
|
+
properties: { /* keys from the schema, e.g. headline, subheadline, proof points, tone */ },
|
|
195
168
|
parent_id: "<gtm_strategy_id>"
|
|
196
169
|
})
|
|
197
170
|
```
|
|
198
171
|
|
|
199
|
-
Connect to positioning:
|
|
200
|
-
|
|
201
|
-
```
|
|
202
|
-
create_edge({
|
|
203
|
-
source_id: "<messaging_id>",
|
|
204
|
-
target_id: "<positioning_id>",
|
|
205
|
-
type: "expresses"
|
|
206
|
-
})
|
|
207
|
-
```
|
|
172
|
+
Connect to positioning only if a canonical edge exists: call `resolve_edge_for_pair({ source_type: "messaging", target_type: "positioning" })`. If it returns `null`, keep the relationship implicit (both hang off the same `gtm_strategy` via `parent_id`) rather than inventing an edge type.
|
|
208
173
|
|
|
209
174
|
Confirm: "💬 **Key message set**: *\"<headline>\"*"
|
|
210
175
|
|
|
@@ -266,19 +231,18 @@ STOP. Wait for the answer.
|
|
|
266
231
|
Create the launch entity with phases:
|
|
267
232
|
|
|
268
233
|
```
|
|
234
|
+
// Read get_entity_schema("launch") first, then:
|
|
269
235
|
create_node({
|
|
270
236
|
type: "launch",
|
|
271
237
|
title: "<Product Name> Launch; <type>",
|
|
272
238
|
description: "<launch approach and rationale>",
|
|
239
|
+
status: "<planning phase from the schema>",
|
|
273
240
|
properties: {
|
|
274
|
-
|
|
241
|
+
/* keys from the schema, e.g. approach, phases[], success metric */
|
|
275
242
|
phases: [
|
|
276
243
|
{ "name": "<phase 1>", "target_date": "<date or timeframe>", "goal": "<what success looks like>" },
|
|
277
|
-
{ "name": "<phase 2>", "target_date": "<date or timeframe>", "goal": "<what success looks like>" }
|
|
278
|
-
|
|
279
|
-
],
|
|
280
|
-
success_metric: "<primary launch KPI>",
|
|
281
|
-
status: "planned"
|
|
244
|
+
{ "name": "<phase 2>", "target_date": "<date or timeframe>", "goal": "<what success looks like>" }
|
|
245
|
+
]
|
|
282
246
|
},
|
|
283
247
|
parent_id: "<gtm_strategy_id>"
|
|
284
248
|
})
|
|
@@ -314,28 +278,21 @@ STOP. Wait for the answer.
|
|
|
314
278
|
Create an `acquisition_channel` entity for each selected channel:
|
|
315
279
|
|
|
316
280
|
```
|
|
281
|
+
// Read get_entity_schema("acquisition_channel") first, then:
|
|
317
282
|
create_node({
|
|
318
283
|
type: "acquisition_channel",
|
|
319
284
|
title: "<channel name>",
|
|
320
285
|
description: "<how this channel works for the product and audience>",
|
|
321
|
-
properties: {
|
|
322
|
-
channel_type: "<seo | social | referral | paid | content | partnerships | community | product_led>",
|
|
323
|
-
cost_model: "<free | low | medium | high>",
|
|
324
|
-
time_to_impact: "<immediate | weeks | months | long_term>",
|
|
325
|
-
primary: <true if this is the lead channel, false otherwise>
|
|
326
|
-
},
|
|
286
|
+
properties: { /* keys from the schema, e.g. channel type, cost model, time to impact, primary */ },
|
|
327
287
|
parent_id: "<gtm_strategy_id>"
|
|
328
288
|
})
|
|
329
289
|
```
|
|
330
290
|
|
|
331
|
-
Connect each channel to the ICP:
|
|
291
|
+
Connect each channel to its audience. Resolve the canonical edge first: `resolve_edge_for_pair({ source_type: "acquisition_channel", target_type: "persona" })` (channels typically reach the underlying persona rather than the ICP directly — confirm via the resolver). Then:
|
|
332
292
|
|
|
333
293
|
```
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
target_id: "<icp_id>",
|
|
337
|
-
type: "targets"
|
|
338
|
-
})
|
|
294
|
+
// edge = resolve_edge_for_pair({ source_type: "acquisition_channel", target_type: "persona" })
|
|
295
|
+
create_edge({ source_id: "<acquisition_channel_id>", target_id: "<persona_id>" }) // server infers type
|
|
339
296
|
```
|
|
340
297
|
|
|
341
298
|
Confirm: "📣 **<N> acquisition channels mapped**: <primary channel> as the lead growth engine."
|
|
@@ -363,30 +320,17 @@ STOP. Wait for the answer.
|
|
|
363
320
|
Create the `content_strategy` entity:
|
|
364
321
|
|
|
365
322
|
```
|
|
323
|
+
// Read get_entity_schema("content_strategy") first, then:
|
|
366
324
|
create_node({
|
|
367
325
|
type: "content_strategy",
|
|
368
326
|
title: "<Product Name> Content Strategy",
|
|
369
327
|
description: "<content philosophy and approach>",
|
|
370
|
-
properties: {
|
|
371
|
-
content_types: ["<type 1>", "<type 2>", "<type 3>"],
|
|
372
|
-
primary_format: "<blog | newsletter | social | video | podcast | educational>",
|
|
373
|
-
publishing_cadence: "<daily | weekly | biweekly | monthly>",
|
|
374
|
-
target_audience: "<ICP name>",
|
|
375
|
-
goal: "<awareness | trust | education | conversion | retention>"
|
|
376
|
-
},
|
|
328
|
+
properties: { /* keys from the schema, e.g. content types, primary format, cadence, audience, goal */ },
|
|
377
329
|
parent_id: "<gtm_strategy_id>"
|
|
378
330
|
})
|
|
379
331
|
```
|
|
380
332
|
|
|
381
|
-
Connect to relevant acquisition channels:
|
|
382
|
-
|
|
383
|
-
```
|
|
384
|
-
create_edge({
|
|
385
|
-
source_id: "<content_strategy_id>",
|
|
386
|
-
target_id: "<acquisition_channel_id>",
|
|
387
|
-
type: "fuels"
|
|
388
|
-
})
|
|
389
|
-
```
|
|
333
|
+
Connect to relevant acquisition channels only if a canonical edge exists: call `resolve_edge_for_pair({ source_type: "content_strategy", target_type: "acquisition_channel" })`. If it returns `null`, keep the relationship implicit (both hang off the same `gtm_strategy` via `parent_id`) rather than inventing an edge type.
|
|
390
334
|
|
|
391
335
|
Confirm: "📝 **Content strategy set**: <primary format> focused on <goal>."
|
|
392
336
|
|