@unified-product-graph/mcp-server 0.8.1 → 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.
Files changed (61) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1 -1
  3. package/TOOLS.md +19 -13
  4. package/dist/index.js +1286 -285
  5. package/dist/index.js.map +1 -1
  6. package/dist/tools-manifest.json +94 -75
  7. package/package.json +1 -1
  8. package/scripts/claudemd-snippet.md +7 -7
  9. package/scripts/install-skills.sh +2 -2
  10. package/skills/upg/SKILL.md +41 -41
  11. package/skills/{upg-gaps → upg-check-gaps}/SKILL.md +40 -43
  12. package/skills/{upg-schema-health → upg-check-schema}/SKILL.md +7 -7
  13. package/skills/{upg-schema-evolve → upg-check-schema-coverage}/SKILL.md +12 -12
  14. package/skills/{upg-schema-edges → upg-check-schema-edges}/SKILL.md +3 -3
  15. package/skills/{upg-schema-consolidate → upg-check-schema-merge}/SKILL.md +5 -5
  16. package/skills/upg-context/SKILL.md +96 -72
  17. package/skills/upg-context-intelligence/SKILL.md +23 -27
  18. package/skills/upg-design-system/SKILL.md +21 -26
  19. package/skills/{upg-verify → upg-find-untracked}/SKILL.md +7 -12
  20. package/skills/{upg-rollback → upg-fix-rollback}/SKILL.md +6 -12
  21. package/skills/{upg-migrate → upg-fix-types}/SKILL.md +5 -9
  22. package/skills/upg-link/SKILL.md +125 -0
  23. package/skills/{upg-discover → upg-new-discovery}/SKILL.md +42 -58
  24. package/skills/{upg-capture → upg-new-from-session}/SKILL.md +13 -15
  25. package/skills/{upg-template → upg-new-from-template}/SKILL.md +8 -12
  26. package/skills/{upg-init → upg-new-graph}/SKILL.md +50 -82
  27. package/skills/{upg-hypothesis → upg-new-hypothesis}/SKILL.md +27 -36
  28. package/skills/{upg-launch → upg-new-launch}/SKILL-DETAIL.md +36 -92
  29. package/skills/{upg-launch → upg-new-launch}/SKILL.md +8 -18
  30. package/skills/{upg-okr → upg-new-okr}/SKILL-DETAIL.md +28 -46
  31. package/skills/{upg-okr → upg-new-okr}/SKILL.md +3 -3
  32. package/skills/{upg-persona → upg-new-persona}/SKILL.md +35 -67
  33. package/skills/{upg-research → upg-new-research}/SKILL.md +25 -33
  34. package/skills/{upg-schema-update → upg-new-schema-type}/SKILL.md +2 -2
  35. package/skills/{upg-strategy → upg-new-strategy}/SKILL.md +21 -27
  36. package/skills/upg-prioritise/SKILL.md +4 -4
  37. package/skills/upg-reflect/SKILL.md +7 -7
  38. package/skills/{upg-feedback → upg-send-feedback}/SKILL.md +30 -51
  39. package/skills/{upg-diff → upg-show-diff}/SKILL.md +6 -12
  40. package/skills/{upg-inspect → upg-show-entity}/SKILL.md +7 -9
  41. package/skills/{upg-impact → upg-show-impact}/SKILL.md +11 -15
  42. package/skills/{upg-journey → upg-show-journey}/SKILL.md +31 -32
  43. package/skills/{upg-analytics → upg-show-metrics}/SKILL.md +9 -12
  44. package/skills/{upg-schema-changelog → upg-show-schema-changelog}/SKILL.md +5 -5
  45. package/skills/{upg-status → upg-show-status}/SKILL.md +39 -40
  46. package/skills/{upg-tree → upg-show-tree}/SKILL.md +15 -15
  47. package/skills/{upg-export → upg-sync-export}/SKILL.md +10 -13
  48. package/skills/{upg-import → upg-sync-import}/SKILL.md +7 -13
  49. package/skills/{upg-pull → upg-sync-pull}/SKILL-DETAIL.md +13 -17
  50. package/skills/{upg-pull → upg-sync-pull}/SKILL.md +3 -3
  51. package/skills/{upg-push → upg-sync-push}/SKILL-DETAIL.md +4 -10
  52. package/skills/{upg-push → upg-sync-push}/SKILL.md +4 -4
  53. package/skills/{upg-snapshot → upg-sync-snapshot}/SKILL.md +2 -6
  54. package/skills/upg-trace/SKILL.md +7 -7
  55. package/skills/{upg-workspace → upg-use-workspace}/SKILL.md +8 -14
  56. package/skills/{upg-run → upg-walk-playbook}/SKILL.md +10 -10
  57. package/skills/upg-walk-region/SKILL-DETAIL.md +320 -0
  58. package/skills/upg-walk-region/SKILL.md +89 -0
  59. package/skills/upg-connect/SKILL.md +0 -167
  60. package/skills/upg-explore/SKILL-DETAIL.md +0 -481
  61. package/skills/upg-explore/SKILL.md +0 -297
@@ -1,12 +1,12 @@
1
1
  ---
2
- name: upg-init
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-init: Initialize a Unified Product Graph
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. 💭 Idea; still figuring it out
92
- 2. 🛠️ MVP; building the first version
93
- 3. 📈 Growth; product exists, finding scale
94
- 4. 🏗️ Scale; established, optimizing
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
- - They mentioned bugs, services, APIs, architecture, or called themselves an engineer → set `engineering`
115
- - They mentioned screens, components, flows, or called themselves a designer → set `design`
116
- - They mentioned funnels, channels, campaigns, or growth set `growth`
117
- - Otherwise → default to `product`
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
- > **v0.2 chain model:** desired outcomes are SEPARATE nodes connected to the persona via `persona_aspires_to_desired_outcome`. Never inline them as a `goals` array on the persona.
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
- > **v0.2 chain model:** needs are SEPARATE nodes connected via `persona_experiences_need`. Never inline as a `frustrations` array.
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 with canonical v0.2 properties only; `context`, `is_primary`, `experience_level`, `motivation`, `tech_comfort`, `domain_expertise`. **Never set `goals` or `frustrations` on the persona**: those are separate nodes connected by edges:
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 (canonical v0.2 properties only)
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 the user picked:
233
- create_node({
234
- type: "desired_outcome",
235
- title: "<outcome statement>",
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
- type: "job",
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 the user picked:
259
- create_node({
260
- type: "need",
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 (as a `metric` node with `designation: "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
- properties: {
359
- we_believe: "<the change>",
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> (persona_aspires_to_desired_outcome)
381
- │ ├─ 💼 <job> (persona_pursues_job)
382
- │ └─ ⚡ <need> (persona_experiences_need)
351
+ │ ├─ 🎯 <desired outcome>
352
+ │ ├─ 💼 <job>
353
+ │ └─ ⚡ <need>
383
354
  ├─ 🎯 <outcome>
384
355
  │ └─ 📊 <metric> (<current> → <target>)
385
- ├─ ⚗️ <h1> ⚪ untested
386
- └─ ⚗️ <h2> ⚪ untested
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-hypothesis-cycle]
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` (re-promoted at v0.4.0; the "claim" suffix was redundant). Evidence attaches via `hypothesis_has_evidence` with `evidence.direction` carrying supports/refutes/neutral.
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 `we_test_by` property and to inform experiment design.
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
- we_believe: "<the change>",
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**. There is no canonical
117
- `opportunity hypothesis` edge by design; solutions are the
118
- articulated *approach* the hypothesis tests. If the user has named an
119
- opportunity but no solution yet, surface a one-liner solution first
120
- (`opportunity_drives_solution`), then attach the hypothesis to that
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
- - If related to a `solution` use the `solution_proposes_hypothesis` edge (or `parent_id: <solution_id>` for parent_ref auto-chaining).
124
- - If related to an `opportunity` → create the intermediate solution first, then attach the hypothesis to that solution. Do not skip the solution layer.
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> ⚪ untested
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
- properties: {
163
- method: "<e.g. A/B test, usability test, fake door>",
164
- status: "planned",
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 "untested".** Don't let anyone claim "validated" without evidence from a 🧪 experiment.
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, create an edge:
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-explore market_intelligence`>; launch to the beachhead
79
- 3. <existing customer segment from `/upg-explore business_model`>; the paying segment
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
- create_edge({
108
- source_id: "<icp_id>",
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, create edges:
133
+ If competitors exist in the graph, resolve the edge first:
154
134
 
155
135
  ```
156
- create_edge({
157
- source_id: "<positioning_id>",
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
- approach: "<gradual | big_bang | waitlist | internal_first>",
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
- { "name": "<phase 3>", "target_date": "<date or timeframe>", "goal": "<what success looks like>" }
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
- create_edge({
335
- source_id: "<acquisition_channel_id>",
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