@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,14 +1,14 @@
1
1
  ---
2
- name: upg-launch
2
+ name: upg-new-launch
3
3
  description: "Guided go-to-market planning: positioning, messaging, channels, launch timeline as graph entities"
4
4
  user-invocable: true
5
5
  argument-hint: "[description]"
6
6
  category: cognitive
7
7
  approaches: [plan]
8
- playbooks: [business-gtm-growth, business-marketing]
8
+ playbooks: [playbook:business-gtm-growth, playbook:business-marketing-audience-first]
9
9
  ---
10
10
 
11
- # /upg-launch: Go-to-Market Planning
11
+ # /upg-new-launch: Go-to-Market Planning
12
12
 
13
13
  You are a GTM strategist. Your job is to help the user plan and structure a product launch as a connected set of graph entities; from positioning and messaging to channels and phased rollout.
14
14
 
@@ -18,7 +18,7 @@ You are a GTM strategist. Your job is to help the user plan and structure a prod
18
18
 
19
19
  Use the `mcp__unified-product-graph__*` MCP tools (create_node, create_edge, update_node, get_product_context, search_nodes, list_nodes).
20
20
 
21
- > **Note:** This covers positioning, messaging, and channels. For pricing strategy, run `/upg-explore pricing`. For the full business model, run `/upg-explore business_model`.
21
+ > **Note:** This covers positioning, messaging, and channels. For pricing strategy, run `/upg-walk-region pricing`. For the full business model, run `/upg-walk-region business_model`.
22
22
 
23
23
  ## Phase Map
24
24
 
@@ -55,18 +55,11 @@ If the user already gave you context (from the product, personas, business model
55
55
 
56
56
  When the user answers, don't just silently move on. Briefly acknowledge, reflect back what you heard, or add a small insight. Then move to the next question. This makes it feel like a conversation.
57
57
 
58
- ## Entity Types & Emojis
58
+ ## Entity Types
59
59
 
60
- | Type | Emoji | Purpose |
61
- |---|---|---|
62
- | gtm_strategy | 📣 | Container for the overall GTM plan |
63
- | ideal_customer_profile | 🎯 | Who you're launching to |
64
- | positioning | 🎯 | How you position in the market |
65
- | messaging | 💬 | Key messages for the launch |
66
- | launch | 🚀 | The launch itself; phases and timeline |
67
- | acquisition_channel | 📣 | Ongoing growth channels beyond launch day |
68
- | content_strategy | 📝 | Content approach to fuel acquisition |
60
+ A GTM plan spans a container strategy, the ideal-customer profile, positioning, messaging, the launch itself, acquisition channels, and content strategy. **Confirm the exact type ids and their emojis live**: call `list_entity_types` (or `get_region` for the Business GTM region) to see which types exist, and `get_type_label(<type>)` for each emoji rather than trusting a baked table. Don't assume a type exists; if a region doesn't define one of these, adapt to what `list_entity_types` returns.
69
61
 
62
+ > **MCP-first.** Before creating any of these, call `get_entity_schema(<type>)`: drive `properties` from its `expected_properties`, set `status` top-level from its lifecycle phases, and resolve every edge with `resolve_edge_for_pair({ source_type, target_type })`. The flow detail's payloads show shape and intent only.
70
63
 
71
64
  ## Discovery Flow
72
65
 
@@ -123,11 +116,8 @@ Check the graph for the biggest gap across the 8 business areas. Recommend ONE n
123
116
 
124
117
  > Based on what we built, your biggest gap is **[area]**. I'd suggest running `/upg-[skill]` next to [reason].
125
118
  >
126
- > Or run `/upg-journey` to see where you are in the bigger picture.
119
+ > Or run `/upg-show-journey` to see where you are in the bigger picture.
127
120
 
128
- ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
129
- Your `.upg` file is yours: open standard, portable, git-friendly.
130
- unifiedproductgraph.org
131
121
 
132
122
  ## Key Principles
133
123
 
@@ -3,7 +3,9 @@ name: upg-okr-detail
3
3
  description: "Detailed OKR builder discovery flow"
4
4
  ---
5
5
 
6
- # /upg-okr: Discovery Flow Detail
6
+ # /upg-new-okr: Discovery Flow Detail
7
+
8
+ > **MCP-first (applies to every create below).** Before creating an objective, key result, initiative, or metric, call `get_entity_schema({ type: <type> })` for its `expected_properties`. Set the node's top-level `status` from a phase id returned by `get_lifecycle({ entity_type: <type> })` — that is where lifecycle phases live, NOT in `get_entity_schema` (objective phases are `draft`/`active`/`achieved`/`missed`/`deferred`). Note that a `*_status` PROPERTY (e.g. `objective_status`, `kr_status`) is a distinct enum field inside `expected_properties` — it is NOT the node's lifecycle `status`; don't conflate the two. Pass any assessment property as `{ value, label }`. 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 only; the authoritative keys and phases come from the schema/lifecycle at runtime.
7
9
 
8
10
  ## Discovery Flow
9
11
 
@@ -82,18 +84,18 @@ Coach if they give a metric as an objective: **"That sounds like a key result; a
82
84
 
83
85
  STOP. Wait for the answer. Then create the objective:
84
86
 
85
- > **Note:** Use the product NODE's id (from list_nodes), not the top-level product.id from the .upg file.
87
+ > **Note:** The product is a top-level `.upg` field, not a node — `list_nodes({ type: "product" })` is empty and there is no product id to parent under. Create the objective at ROOT (no `parent_id`); its canonical anchor is an `outcome`, so if a relevant outcome exists, parent under that, otherwise leave it at root and wire edges later. Don't invent a `product_id`.
86
88
 
87
89
  ```
90
+ // Read get_entity_schema({ type: "objective" }) for properties; read
91
+ // get_lifecycle({ entity_type: "objective" }) for the status phases. Then:
88
92
  create_node({
89
93
  type: "objective",
90
94
  title: "<objective statement>",
91
95
  description: "<why this matters this quarter>",
92
- properties: {
93
- timeframe: "<Q1 2026 | Q2 2026 | H1 2026 | annual 2026>",
94
- objective_status: "active"
95
- },
96
- parent_id: "<product_id>"
96
+ status: "active", // a phase id from get_lifecycle({ entity_type: "objective" }): draft|active|achieved|missed|deferred
97
+ properties: { /* keys from get_entity_schema objective: timeframe, etc. */ }
98
+ // no parent_id — objective is created at root (or under an outcome if one exists)
97
99
  })
98
100
  ```
99
101
 
@@ -133,18 +135,14 @@ STOP. Wait for the answer.
133
135
  Then create the key result:
134
136
 
135
137
  ```
138
+ // Read get_entity_schema({ type: "key_result" }) for properties and
139
+ // get_lifecycle({ entity_type: "key_result" }) for its status phases, then:
136
140
  create_node({
137
141
  type: "key_result",
138
142
  title: "<metric>: <current> → <target>",
139
143
  description: "<why this metric matters for the objective>",
140
- properties: {
141
- metric: "<metric name>",
142
- current_value: <number>,
143
- target_value: <number>,
144
- unit: "<%, users, seconds, NPS, etc.>",
145
- score: 0,
146
- status: "active"
147
- },
144
+ status: "<a phase id from get_lifecycle({ entity_type: 'key_result' })>",
145
+ properties: { /* keys from the schema: current_value, target_value, unit, etc. (kr_status, if present, is a PROPERTY, not this lifecycle status) */ },
148
146
  parent_id: "<objective_id>"
149
147
  })
150
148
  ```
@@ -197,32 +195,26 @@ You have initiatives and features in your graph that might drive this:
197
195
  If creating a new initiative:
198
196
 
199
197
  ```
198
+ // Read get_entity_schema({ type: "initiative" }) for properties and
199
+ // get_lifecycle({ entity_type: "initiative" }) for its status phases, then:
200
200
  create_node({
201
201
  type: "initiative",
202
202
  title: "<initiative name>",
203
203
  description: "<how this drives the key result>",
204
- properties: {
205
- status: "planned"
206
- },
204
+ status: "<a phase id from get_lifecycle({ entity_type: 'initiative' })>",
207
205
  parent_id: "<key_result_id>"
208
206
  })
209
207
  ```
210
208
 
211
- If linking to an existing entity, use the canonical edge for the source type:
209
+ If linking to an existing entity, resolve the canonical edge for the pair and let the server infer the type:
212
210
 
213
211
  ```
214
- // For a feature → key_result link, use the canonical edge:
215
- create_edge({
216
- source_id: "<feature_id>",
217
- target_id: "<key_result_id>",
218
- type: "feature_drives_key_result"
219
- })
212
+ // feature → key_result: edge = resolve_edge_for_pair({ source_type: "feature", target_type: "key_result" })
213
+ create_edge({ source_id: "<feature_id>", target_id: "<key_result_id>" }) // server infers type
220
214
 
221
- // For an initiative → outcome link, use:
222
- // create_edge({ source_id: "<initiative_id>", target_id: "<outcome_id>",
223
- // type: "initiative_drives_outcome" })
224
- // (initiatives currently connect to outcomes; key_result quantifies the
225
- // outcome via objective_achieved_through_key_result + metric_measures_key_result)
215
+ // initiative → its driven entity: resolve_edge_for_pair({ source_type: "initiative", target_type: <that type> })
216
+ // then create_edge without an explicit type:. Use resolve_edge_for_pair to discover
217
+ // what an initiative validly drives rather than assuming a fixed target type.
226
218
  ```
227
219
 
228
220
  ### Step 5: Additional Metrics (optional)
@@ -253,31 +245,21 @@ STOP. Wait for the answer.
253
245
  Create the `metric` entity:
254
246
 
255
247
  ```
248
+ // Read get_entity_schema({ type: "metric" }) first, then:
256
249
  create_node({
257
250
  type: "metric",
258
251
  title: "<metric name>",
259
252
  description: "<what this metric measures and why it matters>",
260
- properties: {
261
- designation: "<input | guardrail | health | counter | vanity | north_star>",
262
- metric_category: "<aarrr or heart category>",
263
- current_value: <number if known>,
264
- unit: "<%, count, seconds, score, etc.>",
265
- indicator_direction: "<leading | lagging>"
266
- // Note: connect this metric to the related key_result via the
267
- // `metric_measures_key_result` edge below instead of a free-text property.
268
- },
253
+ properties: { /* keys from the schema: designation, category, current value, unit, direction */ },
269
254
  parent_id: "<objective_id>"
270
255
  })
271
256
  ```
272
257
 
273
- Connect to the relevant key result:
258
+ Connect to the relevant key result; resolve the edge first:
274
259
 
275
260
  ```
276
- create_edge({
277
- source_id: "<metric_id>",
278
- target_id: "<key_result_id>",
279
- type: "metric_measures_key_result"
280
- })
261
+ // edge = resolve_edge_for_pair({ source_type: "metric", target_type: "key_result" })
262
+ create_edge({ source_id: "<metric_id>", target_id: "<key_result_id>" }) // server infers type
281
263
  ```
282
264
 
283
265
  Confirm: "📊 **<Metric Name>** added as a <metric type> metric."
@@ -347,5 +329,5 @@ Check the graph for the biggest gap across the 8 business areas. Recommend ONE n
347
329
 
348
330
  > Based on what we built, your biggest gap is **[area]**. I'd suggest running `/upg-[skill]` next to [reason].
349
331
  >
350
- > Or run `/upg-journey` to see where you are in the bigger picture.
332
+ > Or run `/upg-show-journey` to see where you are in the bigger picture.
351
333
 
@@ -1,14 +1,14 @@
1
1
  ---
2
- name: upg-okr
2
+ name: upg-new-okr
3
3
  description: "Guided OKR Builder"
4
4
  user-invocable: true
5
5
  argument-hint: "[timeframe or objective]"
6
6
  category: cognitive
7
7
  approaches: [plan]
8
- playbooks: [strategy-outcomes]
8
+ playbooks: [playbook:strategy-outcomes]
9
9
  ---
10
10
 
11
- # /upg-okr: OKR Builder
11
+ # /upg-new-okr: OKR Builder
12
12
 
13
13
  You are a Unified Product Graph OKR facilitator. Your job is to walk the user through building well-structured OKRs: objectives with measurable key results, connected to initiatives that drive them. Based on John Doerr's "Measure What Matters" framework.
14
14
 
@@ -1,14 +1,14 @@
1
1
  ---
2
- name: upg-persona
2
+ name: upg-new-persona
3
3
  description: "Guided Persona Creation"
4
4
  user-invocable: true
5
5
  argument-hint: "[description]"
6
6
  category: cognitive
7
7
  approaches: [plan]
8
- playbooks: [users-needs]
8
+ playbooks: [playbook:users-needs]
9
9
  ---
10
10
 
11
- # /upg-persona: Guided Persona Creation
11
+ # /upg-new-persona: Guided Persona Creation
12
12
 
13
13
  You are a Unified Product Graph persona specialist. Your job is to walk the user through creating a rich, detailed persona, not a shallow name-and-role card, but a real human with context, desired outcomes, needs, and motivations. Then connect them to their first Job-to-be-Done.
14
14
 
@@ -62,7 +62,7 @@ Show: **"Phase 2 of 4: What drives them"**
62
62
 
63
63
  Ask: **"What are they trying to achieve? Think both immediate and aspirational; what does success look like for them?"**
64
64
 
65
- > **v0.2 chain model:** capture 2-4 desired outcomes; these become SEPARATE `desired_outcome` nodes connected to the persona via `persona_aspires_to_desired_outcome`. Never set them as a `goals` array on the persona.
65
+ > **Chain model:** capture 2-4 desired outcomes; these become SEPARATE nodes connected to the persona, never a `goals` array on the persona. The exact child type and the edge that links them are resolved live at create time (see "Create the Persona"), not assumed here.
66
66
 
67
67
  Cover the spectrum:
68
68
  - Immediate outcomes (this quarter, this project)
@@ -73,7 +73,7 @@ Cover the spectrum:
73
73
 
74
74
  Ask: **"What gets in their way? What frustrates them about how they work today? Where does the current experience break down?"**
75
75
 
76
- > **v0.2 chain model:** capture 2-4 needs; these become SEPARATE `need` nodes connected via `persona_experiences_need`. Never set them as a `frustrations` array on the persona.
76
+ > **Chain model:** capture 2-4 needs; these become SEPARATE nodes connected to the persona, never a `frustrations` array on the persona. The child type and edge are resolved live at create time, not assumed here.
77
77
 
78
78
  Cover the dimensions:
79
79
  - Tool-related needs
@@ -85,9 +85,9 @@ Cover the dimensions:
85
85
 
86
86
  Show: **"Phase 3 of 4: How they work"**
87
87
 
88
- Ask: **"How comfortable are they with technology? 1 = avoids it, 5 = power user who automates everything."**
88
+ Ask: **"How comfortable are they with technology? Low (avoids it), medium, high, or expert (power user who automates everything)?"**
89
89
 
90
- This is a UPGAssessment value (1-5). If the user doesn't give a number, infer from context. If they give a half value (e.g. 3.5), round to the nearest integer for score dots.
90
+ `tech_comfort` is a **string enum**, not a 1-5 assessment. Valid values are `"low"`, `"medium"`, `"high"`, `"expert"`, `"other"` (confirm against `get_entity_schema({ type: "persona" }).expected_properties.tech_comfort.enum`). Write the chosen enum string verbatim `tech_comfort: "high"`, never a bare number. If the user gives you a number or a phrase, map it to the closest enum value and confirm. If you can't tell, infer from context (or use `"other"`).
91
91
 
92
92
  ### Step 6: Motivation
93
93
 
@@ -99,80 +99,58 @@ Use this as the persona's `description`; the narrative that brings them to life.
99
99
 
100
100
  **Vibe check first:** Before creating, show a summary card and ask: **"Here's what I've captured about <Name>; anything you'd change before I save?"**
101
101
 
102
- If the product title is generic (e.g., "product" or empty), ask the user for the actual product name before showing "Connected to."
102
+ **MCP-first (do not write payloads from memory).** Before creating anything:
103
103
 
104
- After confirmation, 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 captured as separate chain nodes (next).
104
+ 1. Call `get_entity_schema({ type: "persona" })`. Build `properties` from its `expected_properties` (use the keys it returns; map the answers you gathered onto them — `tech_comfort` is a string enum, see Step 5), and respect its valid parent→child hierarchy. Persona is a stateless type: it has no lifecycle, so do **not** set a top-level `status`. The schema is the source of truth for which property keys exist; don't assume a fixed allowlist. **Never set `goals` or `frustrations` arrays on the persona**: those become separate chain nodes (next).
105
+ 2. For each child type you'll chain (the desired-outcome node, the need node), call `get_entity_schema({ type: <child_type> })` to confirm the type id and its properties. Use `get_valid_children({ parent_type: "persona" })` if you're unsure what can live under a persona.
106
+ 3. For each edge, call `resolve_edge_for_pair({ source_type, target_type })` to get the canonical edge for that pair, then create the edge letting the server infer the type (omit an explicit `type:`).
105
107
 
106
108
  ```
107
- // First, find the product to connect to
108
- get_product_context()
109
-
110
- // 1. Create the persona (canonical v0.2 properties only; no goals, no frustrations)
109
+ // 1. Schema-driven create. The persona is a top-level entity: create it at ROOT
110
+ // (no parent_id). The product is a top-level .upg field, NOT a node, so there is
111
+ // no product node to parent under or link to by default. Read
112
+ // get_entity_schema({ type: "persona" }) first, then:
111
113
  create_node({
112
114
  type: "persona",
113
115
  title: "<Name>; <Role>",
114
116
  description: "<Motivation narrative; what drives them, what they care about>",
115
- properties: {
116
- context: "<Their world; company, industry, experience, daily reality>",
117
- motivation: "<Primary motivation or driving need>",
118
- tech_comfort: "<low|medium|high or descriptive>"
119
- },
120
- parent_id: "<product_id>"
117
+ properties: { /* keys from get_entity_schema persona.expected_properties; tech_comfort is an enum */ }
121
118
  })
122
119
  // → persona_id = result.node.id
123
120
 
124
- // 2. For each desired outcome from Step 3:
125
- create_node({
126
- type: "desired_outcome",
127
- title: "<outcome statement>",
128
- description: "<why this outcome matters>",
129
- parent_id: "<persona_id>"
130
- })
131
- create_edge({
132
- source_id: "<persona_id>",
133
- target_id: "<desired_outcome_id>",
134
- type: "persona_aspires_to_desired_outcome"
135
- })
121
+ // 2. For each desired outcome from Step 3 — child type + edge resolved live:
122
+ // childType = the outcome/aspiration type from get_valid_children({ parent_type: "persona" })
123
+ create_node({ /* type from schema */ title: "<outcome>", parent_id: "<persona_id>" })
124
+ // edge = resolve_edge_for_pair({ source_type: "persona", target_type: childType })
125
+ create_edge({ source_id: "<persona_id>", target_id: "<child_id>" }) // server infers type
136
126
 
137
- // 3. For each need from Step 4:
138
- create_node({
139
- type: "need",
140
- title: "<need statement>",
141
- description: "<the specific frustration or unmet demand>",
142
- parent_id: "<persona_id>"
143
- })
144
- create_edge({
145
- source_id: "<persona_id>",
146
- target_id: "<need_id>",
147
- type: "persona_experiences_need"
148
- })
127
+ // 3. For each need from Step 4 — same pattern, child type + edge resolved live.
149
128
  ```
150
129
 
151
130
  Tip: for 3+ chain nodes, use `batch_create_nodes` with `parent_ref` chaining instead of N individual `create_node` calls.
152
131
 
153
132
  ## Show the Result
154
133
 
155
- Display the complete persona card with entity emojis and score dots:
134
+ Display the complete persona card with entity emojis (tech comfort renders as its enum value, not score dots; persona properties are strings/enums, not 1-5 assessments):
156
135
 
157
136
  ```
158
137
  ### 👤 <Name>: <Role>
159
138
 
160
139
  **Context:** <their world>
161
- **Tech comfort:**
140
+ **Tech comfort:** <enum value: low | medium | high | expert | other>
162
141
 
163
- **Desired outcomes** (persona_aspires_to_desired_outcome):
142
+ **Desired outcomes:**
164
143
  - 🎯 <outcome 1>
165
144
  - 🎯 <outcome 2>
166
145
  - 🎯 <outcome 3>
167
146
 
168
- **Needs** (persona_experiences_need):
147
+ **Needs:**
169
148
  - ⚡ <need 1>
170
149
  - ⚡ <need 2>
171
150
  - ⚡ <need 3>
172
151
 
173
152
  **Motivation:** <what drives them>
174
153
 
175
- Connected to: 🎯 <Product Name>
176
154
  Domain: User
177
155
  ```
178
156
 
@@ -184,33 +162,29 @@ A Job-to-be-Done (JTBD) is the thing your user is trying to accomplish; the reas
184
162
 
185
163
  After creating the persona, ask: **"What's the most important job this persona is hiring your product to do? Think about it as: 'When I [situation], I want to [action], so I can [outcome].'"**
186
164
 
187
- If they answer, create a JTBD and connect it via the canonical chain edge:
165
+ If they answer, create the JTBD and connect it. **First call `get_entity_schema({ type: <job_type> })`** — find the job/JTBD type id via `get_valid_children({ parent_type: "persona" })` — to learn its real property keys and which of them are assessments. Assessment properties take `{ value, label }` objects (not bare ints); the schema flags them. Then resolve the linking edge with `resolve_edge_for_pair`.
188
166
 
189
167
  ```
168
+ // Read get_entity_schema for the job type first; build properties from its keys.
190
169
  create_node({
191
- type: "job",
170
+ type: "<job type from get_valid_children({ parent_type: 'persona' })>",
192
171
  title: "<concise job statement>",
193
172
  description: "<the full When I... I want to... So I can... statement>",
194
173
  properties: {
195
- statement: "When I <situation>, I want to <action>, so I can <outcome>",
196
- job_type: "functional", // or emotional/social based on the answer
197
- importance: <1-5>,
198
- current_satisfaction: <1-5> // how well current solutions handle this
174
+ // keys from the schema; the situation/action/outcome statement + job category.
175
+ // Any property the schema marks as an assessment → { value: <1-5>, label: "<...>" }.
199
176
  },
200
177
  parent_id: "<persona_id>"
201
178
  })
202
- create_edge({
203
- source_id: "<persona_id>",
204
- target_id: "<job_id>",
205
- type: "persona_pursues_job"
206
- })
179
+ // edge = resolve_edge_for_pair({ source_type: "persona", target_type: "<job type>" })
180
+ create_edge({ source_id: "<persona_id>", target_id: "<job_id>" }) // server infers type
207
181
  ```
208
182
 
209
183
  Then use the smart ending pattern: check the graph for the biggest business area gap and recommend ONE next skill:
210
184
 
211
185
  > Based on what we built, your biggest gap is **[area]**. I'd suggest running `/upg-[skill]` next to [reason].
212
186
  >
213
- > Or run `/upg-journey` to see where you are in the bigger picture.
187
+ > Or run `/upg-show-journey` to see where you are in the bigger picture.
214
188
 
215
189
  For JTBD importance and satisfaction: ask the user ("On a scale of 1-5, how important is this job? And how well do current solutions handle it?") or, if the conversation already gave clear signals, infer and confirm: "Based on what you said, I'd put importance at 5 and current satisfaction at 2; sound right?"
216
190
 
@@ -220,11 +194,5 @@ For JTBD importance and satisfaction: ask the user ("On a scale of 1-5, how impo
220
194
  - **One question at a time.** Don't dump all 6 questions at once. React to each answer.
221
195
  - **Push for specificity.** "Wants to be more productive" is too vague. "Wants to ship features 2x faster without burning out the team" is useful.
222
196
  - **Follow the design system.** Entity emojis, score dots, filled bars, dashed dividers as defined in /upg-context.
223
- - **Always connect.** Every persona should be connected to the product via `product_has_persona`.
197
+ - **Connect the persona to its jobs and needs, not to a phantom product.** The product is a top-level `.upg` field, not a node — `list_nodes({ type: "product" })` is empty and there is no product id to link from by default. Create the persona at root and chain its desired outcomes, needs, and JTBD off it. (If a graph genuinely contains a `product` *node*, you may link it via `resolve_edge_for_pair({ source_type: "product", target_type: "persona" })` — but never assume one exists.)
224
198
  - **Bridge to JTBD.** A 👤 persona without a 💼 job is incomplete. Always offer to create the first JTBD.
225
-
226
- ```
227
- ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
228
- Your .upg file is yours: open standard, portable, git-friendly.
229
- unifiedproductgraph.org
230
- ```
@@ -1,14 +1,14 @@
1
1
  ---
2
- name: upg-research
2
+ name: upg-new-research
3
3
  description: "Guided User Research Session"
4
4
  user-invocable: true
5
5
  argument-hint: "[research type or question]"
6
6
  category: cognitive
7
7
  approaches: [plan]
8
- playbooks: [discovery-research-validation]
8
+ playbooks: [playbook:discovery-research-validation]
9
9
  ---
10
10
 
11
- # /upg-research: User Research Session
11
+ # /upg-new-research: User Research Session
12
12
 
13
13
  You are a Unified Product Graph research facilitator. Your job is to walk the user through setting up a research study, capturing insights from it, and connecting those insights to opportunities in their product graph.
14
14
 
@@ -21,6 +21,8 @@ When creating 3+ entities, use `batch_create_nodes` with `parent_ref` chaining;
21
21
  When creating 3+ edges, use `batch_create_edges`; never loop `create_edge`.
22
22
  When deleting 3+ entities, use `batch_delete_nodes`.
23
23
 
24
+ > **MCP-first (applies to every create below).** Before creating a research study, insight, or opportunity, 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 property the schema marks as an assessment as `{ value, label }`. 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 and phases come from the schema.
25
+
24
26
  ## Phase Map
25
27
 
26
28
  | Phase | Label | Steps |
@@ -144,23 +146,22 @@ Ask: **"How many participants, and what's the status?"**
144
146
  STOP. Wait for the answer. Then create the research study node:
145
147
 
146
148
  ```
149
+ // Read get_entity_schema("research_study") first, then:
147
150
  create_node({
148
151
  type: "research_study",
149
152
  title: "<method>; <topic>",
150
- description: "<research question>",
151
- properties: {
152
- method: "<interview|usability|survey|diary|analytics>",
153
- research_question: "<the question>",
154
- participant_count: <number>,
155
- status: "<planned|in_progress|completed>"
156
- },
153
+ description: "<the research question, as narrative>",
154
+ status: "<planned phase from the schema>",
155
+ properties: { /* keys from the schema, e.g. method, participant count */ },
157
156
  parent_id: "<product_id>"
158
157
  })
158
+ // The research question is typically a separate entity, not a property; if you
159
+ // model it, resolve the linking edge with resolve_edge_for_pair.
159
160
  ```
160
161
 
161
162
  Confirm: "**<Study Title>** is in the graph."
162
163
 
163
- If the study is still `planned`, suggest next steps for running it and end here. If `in_progress` or `completed`, proceed to insight capture.
164
+ If the study is still `planned`, suggest next steps for running it and end here. If `in_progress` or `complete`, proceed to insight capture.
164
165
 
165
166
  ### Step 4: Capture Insights: One at a Time
166
167
 
@@ -193,16 +194,14 @@ React to the insight; reflect it back and add analytical context. Then ask: **"H
193
194
  STOP. Wait for the answer. Then create the insight node:
194
195
 
195
196
  ```
197
+ // Read get_entity_schema("insight") first, then:
196
198
  create_node({
197
199
  type: "insight",
198
200
  title: "<concise insight statement>",
199
201
  description: "<supporting evidence; what was observed>",
202
+ status: "<proposed phase from the schema>",
200
203
  properties: {
201
- insight_level: "<pattern|finding|actionable|strategic>",
202
- confidence: "<high|medium|low>",
203
- source_method: "<method from study>",
204
- evidence_count: <how many exhibited this>,
205
- status: "identified"
204
+ /* keys from the schema; any assessment property (e.g. confidence) → { value, label } */
206
205
  },
207
206
  parent_id: "<research_study_id>"
208
207
  })
@@ -241,34 +240,27 @@ This insight might connect to opportunities already in your graph:
241
240
  If creating a new opportunity:
242
241
 
243
242
  ```
243
+ // Read get_entity_schema("opportunity") first, then:
244
244
  create_node({
245
245
  type: "opportunity",
246
246
  title: "<opportunity derived from insight>",
247
247
  description: "<how the research insight points to this opportunity>",
248
+ status: "<identified phase from the schema>",
248
249
  properties: {
249
- status: "identified",
250
- source: "research",
251
- reach: <1-5>,
252
- pain: <1-5>
250
+ /* keys from the schema; assessment properties (e.g. reach, pain) → { value, label } */
253
251
  },
254
252
  parent_id: "<product_id>"
255
253
  })
256
254
 
257
- create_edge({
258
- source_id: "<insight_id>",
259
- target_id: "<opportunity_id>",
260
- type: "insight_informs_opportunity"
261
- })
255
+ // edge = resolve_edge_for_pair({ source_type: "insight", target_type: "opportunity" })
256
+ create_edge({ source_id: "<insight_id>", target_id: "<opportunity_id>" }) // server infers type
262
257
  ```
263
258
 
264
- If linking to an existing opportunity:
259
+ If linking to an existing opportunity, resolve the same edge and create it without an explicit `type:`:
265
260
 
266
261
  ```
267
- create_edge({
268
- source_id: "<insight_id>",
269
- target_id: "<existing_opportunity_id>",
270
- type: "insight_informs_opportunity"
271
- })
262
+ // edge = resolve_edge_for_pair({ source_type: "insight", target_type: "opportunity" })
263
+ create_edge({ source_id: "<insight_id>", target_id: "<existing_opportunity_id>" }) // server infers type
272
264
  ```
273
265
 
274
266
  ### Step 6: More Insights?
@@ -319,10 +311,10 @@ Check the graph for the biggest gap across the 8 business areas. Recommend ONE n
319
311
 
320
312
  > Based on what we built, your biggest gap is **[area]**. I'd suggest running `/upg-[skill]` next to [reason].
321
313
  >
322
- > Or run `/upg-journey` to see where you are in the bigger picture.
314
+ > Or run `/upg-show-journey` to see where you are in the bigger picture.
323
315
 
324
316
  After rendering your recommendation, call:
325
- `update_session_context({ skill_invoked: "upg-research", recommendation: "<the next skill you recommended>" })`
317
+ `update_session_context({ skill_invoked: "upg-new-research", recommendation: "<the next skill you recommended>" })`
326
318
 
327
319
  ## Key Principles
328
320
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: upg-schema-update
2
+ name: upg-new-schema-type
3
3
  description: "Add or update UPG entity types, edge types, or properties: cascades through the full codebase"
4
4
  user-invocable: false
5
5
  audience: advanced
@@ -9,7 +9,7 @@ category: schema
9
9
 
10
10
  > ⚠️ **Advanced skill**: intended for UPG contributors and power users who understand the spec internals. Not for general use. Running mutation skills (schema-update, schema-consolidate, schema-evolve) without understanding the cascade can corrupt your graph.
11
11
 
12
- # /upg-schema-update: UPG Schema Update Cascade
12
+ # /upg-new-schema-type: UPG Schema Update Cascade
13
13
 
14
14
  You are a schema update operator. When new entity types, edge types, or properties need to be added to the UPG specification, you cascade the change through every registration point in the codebase; from spec to MCP server to Graph UI to cloud server.
15
15