@synap-core/workspace-templates 0.5.0 → 0.6.0

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/src/define.ts CHANGED
@@ -10,8 +10,8 @@
10
10
  * shape never drifts per call-site.
11
11
  */
12
12
 
13
- import type { WorkspaceYaml } from './types.js';
14
- import { getWorkspaceTemplate } from './templates.js';
13
+ import type { WorkspaceYaml } from "./types.js";
14
+ import { getWorkspaceTemplate } from "./templates.js";
15
15
 
16
16
  /** The flat definition object accepted by createFromDefinition. */
17
17
  export interface WorkspaceDefinitionInput {
@@ -37,6 +37,15 @@ export interface WorkspaceDefinitionInput {
37
37
  defaultSources?: NonNullable<WorkspaceYaml['workspace']>['defaultSources'];
38
38
  /** Per-workspace onboarding context — written to settings.onboarding. */
39
39
  onboarding?: WorkspaceYaml['onboarding'];
40
+ /**
41
+ * Template-composition dependencies. MUST be forwarded: the backend resolves
42
+ * these BEFORE materializing this workspace (`workspaces.createFromDefinition`
43
+ * → `materializeWorkspaceCore` Step 0 → `resolvePackageDependencies`), which
44
+ * is what installs a `require`d base FIRST and makes shared-profile base
45
+ * seeding deterministic. Dropping this field silently de-orders provisioning
46
+ * on the tRPC/UI door — see the note in `toWorkspaceDefinition`.
47
+ */
48
+ dependencies?: WorkspaceYaml['dependencies'];
40
49
  }
41
50
 
42
51
  export interface ToDefinitionResult {
@@ -89,6 +98,17 @@ export function toWorkspaceDefinition(
89
98
  sourceRoles: ws.sourceRoles,
90
99
  defaultSources: ws.defaultSources,
91
100
  onboarding: tpl.onboarding,
101
+ // Template-composition dependencies — forwarded so the tRPC/UI door gets the
102
+ // SAME deps-first ordering the CLI/Hub door gets via `toPackageDefinition`.
103
+ // `workspaces.createFromDefinition` declares `dependencies` in its zod input
104
+ // and branches on `input.definition.dependencies?.length` to run
105
+ // `materializeWorkspaceCore` (Step 0 = `resolvePackageDependencies`) BEFORE
106
+ // creating this workspace. Omitting it here made that branch dead on every
107
+ // frontend call-site, so a `require: foundation` dep was silently dropped and
108
+ // shared-profile bases were seeded by whichever template happened to apply
109
+ // first. This function is documented as "the single canonical seam" — it only
110
+ // is one if it forwards the fields the wire shape declares.
111
+ dependencies: tpl.dependencies,
92
112
  };
93
113
 
94
114
  return { definition, proposalId, workspaceName: name, templateId: tpl.meta.slug };
@@ -132,6 +152,8 @@ export interface PackageDefinitionOutput {
132
152
  capabilities?: WorkspaceYaml['integrations'];
133
153
  playbooks?: WorkspaceYaml['playbooks'];
134
154
  onboarding?: WorkspaceYaml['onboarding'];
155
+ /** Template-composition dependencies — resolved by the apply endpoint. */
156
+ dependencies?: WorkspaceYaml['dependencies'];
135
157
  }
136
158
 
137
159
  /**
@@ -180,5 +202,6 @@ export function toPackageDefinition(
180
202
  capabilities: tpl.integrations,
181
203
  playbooks: tpl.playbooks,
182
204
  onboarding: tpl.onboarding,
205
+ dependencies: tpl.dependencies,
183
206
  };
184
207
  }
@@ -115,6 +115,9 @@ profiles:
115
115
  color: "#8B5CF6"
116
116
  description: "External services, tools, and providers used by the Synap platform."
117
117
  scope: WORKSPACE
118
+ profileKind: role
119
+ applicableKinds:
120
+ - company
118
121
  properties:
119
122
  - slug: vendor-type
120
123
  label: Vendor Type
@@ -0,0 +1,478 @@
1
+ # Ecosystem workspace template
2
+ # The BASE market-graph domain: the players you operate around (partners,
3
+ # competitors, companies, people), the segments and trends that matter, and —
4
+ # the net-new value — the TYPED RELATIONSHIPS between actors (who partners with,
5
+ # competes with, invested in, integrates with whom).
6
+ # Merges + supersedes the old Radar template (carries its market-segment, trend,
7
+ # inspiration kinds, its competitor role, and its graph "Radar Map" view forward).
8
+
9
+ meta:
10
+ slug: ecosystem
11
+ name: Ecosystem
12
+ description: Map the market you operate in — the partners, competitors, and players, the segments and trends, and the relationships between them.
13
+ icon: radar
14
+ color: "#0EA5E9"
15
+ tags: [ecosystem, market, partners, competitors, graph]
16
+ isPublic: true
17
+
18
+ # SSOT ordering guarantee — this template REFERENCES foundation's pod-wide
19
+ # shared `partner` role. The apply engine seeds a shared profile's pod-wide BASE
20
+ # property body from whichever template creates it FIRST, and adds a REUSING
21
+ # template's extra props as workspace OVERLAYS. Requiring foundation makes the
22
+ # resolver install it first (deps-first), so the base is always foundation's
23
+ # SSOT body and never this template's. `require`, not `compose`: foundation is a
24
+ # real workspace, not an overlay. (`competitor` is owned HERE — not foundation's.)
25
+ dependencies:
26
+ - slug: foundation
27
+ kind: workspace
28
+ relation: require
29
+ reason: "Provides the pod-wide partner role vocabulary this workspace reuses."
30
+
31
+ workspace:
32
+ name: Ecosystem
33
+ description: Map the market you operate in — the partners, competitors, and players, the segments and trends, and the relationships between them.
34
+ subtype: ecosystem
35
+ visibility: pod_visible
36
+ capabilities:
37
+ - ecosystem.actors
38
+ - ecosystem.graph
39
+ - ecosystem.segments
40
+ sourceRoles:
41
+ ecosystem: provider
42
+
43
+ profiles:
44
+ # ── Actors ────────────────────────────────────────────────────────────────
45
+ # The pod-wide person and company kinds — declared minimally here so facets
46
+ # (competitor/partner) and actor↔actor relations resolve. Kept light on
47
+ # purpose: this is the base agnostic identity, not a full CRM record.
48
+ - slug: person
49
+ displayName: Person
50
+ icon: user
51
+ color: amber
52
+ description: A person in the ecosystem — a founder, operator, or contact.
53
+ properties:
54
+ - slug: email
55
+ label: Email
56
+ valueType: string
57
+ inputType: email
58
+ - slug: notes
59
+ label: Notes
60
+ valueType: string
61
+ inputType: textarea
62
+
63
+ - slug: company
64
+ displayName: Company
65
+ icon: building-2
66
+ color: "#10B981"
67
+ description: A company in the ecosystem — a player you operate around.
68
+ properties:
69
+ - slug: website
70
+ label: Website
71
+ valueType: string
72
+ inputType: url
73
+ - slug: industry
74
+ label: Industry
75
+ valueType: string
76
+ inputType: text
77
+ - slug: notes
78
+ label: Notes
79
+ valueType: string
80
+ inputType: textarea
81
+
82
+ # ── Market structure (carried verbatim from Radar) ──────────────────────────
83
+ - slug: market-segment
84
+ displayName: Market Segment
85
+ icon: pie-chart
86
+ color: blue
87
+ description: A slice of the market with its own size, maturity, and dynamics.
88
+ properties:
89
+ - slug: description
90
+ label: Description
91
+ valueType: string
92
+ inputType: textarea
93
+ - slug: tam
94
+ label: TAM
95
+ valueType: string
96
+ inputType: text
97
+ - slug: sam
98
+ label: SAM
99
+ valueType: string
100
+ inputType: text
101
+ - slug: som
102
+ label: SOM
103
+ valueType: string
104
+ inputType: text
105
+ - slug: ocean
106
+ label: Ocean
107
+ valueType: string
108
+ inputType: select
109
+ enumValues: [blue, red]
110
+ - slug: maturity
111
+ label: Maturity
112
+ valueType: string
113
+ inputType: select
114
+ enumValues: [emerging, growing, mature, declining]
115
+
116
+ - slug: trend
117
+ displayName: Trend
118
+ icon: trending-up
119
+ color: violet
120
+ description: A directional shift in the market, tech, or behavior worth tracking.
121
+ properties:
122
+ - slug: description
123
+ label: Description
124
+ valueType: string
125
+ inputType: textarea
126
+ - slug: direction
127
+ label: Direction
128
+ valueType: string
129
+ inputType: select
130
+ enumValues: [rising, steady, fading]
131
+ - slug: sources
132
+ label: Sources
133
+ valueType: array
134
+ inputType: text
135
+ - slug: relevance
136
+ label: Relevance
137
+ valueType: string
138
+ inputType: select
139
+ enumValues: [low, medium, high]
140
+ constraints: { defaultValue: medium }
141
+
142
+ - slug: inspiration
143
+ displayName: Inspiration
144
+ icon: sparkles
145
+ color: amber
146
+ description: A brand, founder, product, or campaign worth learning from.
147
+ properties:
148
+ - slug: kind
149
+ label: Kind
150
+ valueType: string
151
+ inputType: select
152
+ enumValues: [brand, founder, product, campaign]
153
+ - slug: why
154
+ label: Why
155
+ valueType: string
156
+ inputType: textarea
157
+ - slug: url
158
+ label: URL
159
+ valueType: string
160
+ inputType: url
161
+
162
+ # ── Market event (NEW) ──────────────────────────────────────────────────────
163
+ - slug: ecosystem-event
164
+ displayName: Ecosystem Event
165
+ icon: zap
166
+ color: orange
167
+ description: A notable market event — a raise, partnership, launch, acquisition, or listing.
168
+ properties:
169
+ - slug: event-type
170
+ label: Type
171
+ valueType: string
172
+ inputType: select
173
+ enumValues: [raise, partnership, launch, acquisition, listing, conference, other]
174
+ - slug: event-date
175
+ label: Date
176
+ valueType: date
177
+ inputType: date
178
+ - slug: event-actor
179
+ label: Actor
180
+ valueType: entity_id
181
+ targetProfileSlug: company
182
+ inputType: entity_select
183
+ - slug: event-summary
184
+ label: Summary
185
+ valueType: string
186
+ inputType: textarea
187
+ - slug: event-url
188
+ label: URL
189
+ valueType: string
190
+ inputType: url
191
+ - slug: event-impact
192
+ label: Impact
193
+ valueType: string
194
+ inputType: select
195
+ enumValues: [low, medium, high]
196
+
197
+ # ── Actor roles (facets — the generic hats an actor plays) ──────────────────
198
+ # Competitor: carried verbatim from Radar.
199
+ - slug: competitor
200
+ displayName: Competitor
201
+ icon: crosshair
202
+ color: red
203
+ description: A rival or alternative a buyer would compare you to.
204
+ # Shared like the other actor roles (partner/client/sponsor): defined once,
205
+ # pod-wide, so any workspace that needs the hat reuses THIS row + a grant
206
+ # rather than forking a workspace-scoped twin.
207
+ scope: shared
208
+ profileKind: role
209
+ applicableKinds:
210
+ - company
211
+ properties:
212
+ - slug: positioning
213
+ label: Positioning
214
+ valueType: string
215
+ inputType: textarea
216
+ - slug: strengths
217
+ label: Strengths
218
+ valueType: string
219
+ inputType: textarea
220
+ - slug: weaknesses
221
+ label: Weaknesses
222
+ valueType: string
223
+ inputType: textarea
224
+ - slug: pricing
225
+ label: Pricing
226
+ valueType: string
227
+ inputType: text
228
+ - slug: url
229
+ label: URL
230
+ valueType: string
231
+ inputType: url
232
+ - slug: status
233
+ label: Status
234
+ valueType: string
235
+ inputType: select
236
+ enumValues: [watching, active-threat, adjacent, archived]
237
+ constraints: { defaultValue: watching }
238
+
239
+ # Partner (shared ecosystem role — SSOT is foundation.yaml) ─────────────────
240
+ # `scope: shared` dedups to the ONE pod-wide `partner` profile seeded by
241
+ # foundation; provisioning grants THIS workspace access to it (SHARED profiles
242
+ # are grant-gated, so the reference is required). Shapes match foundation
243
+ # verbatim, so no property drift.
244
+ - slug: partner
245
+ displayName: Partner
246
+ icon: handshake
247
+ color: teal
248
+ description: A partner relationship — both a referral source and a co-delivery collaborator, distinguished by partner type.
249
+ scope: shared
250
+ profileKind: role
251
+ applicableKinds:
252
+ - company
253
+ - person
254
+ properties:
255
+ - slug: partnerStatus
256
+ label: Status
257
+ valueType: string
258
+ inputType: select
259
+ enumValues:
260
+ - prospect
261
+ - active
262
+ - paused
263
+ - churned
264
+ - slug: partnerType
265
+ label: Partner Type
266
+ valueType: string
267
+ inputType: select
268
+ enumValues:
269
+ - referral
270
+ - co-delivery
271
+ - both
272
+ - slug: accountManager
273
+ label: Account Manager
274
+ valueType: string
275
+ inputType: text
276
+ - slug: engagementStartDate
277
+ label: Engagement Start
278
+ valueType: date
279
+ inputType: date
280
+ - slug: satisfactionScore
281
+ label: Satisfaction
282
+ valueType: number
283
+ inputType: number
284
+ - slug: lifetimeValue
285
+ label: Lifetime Value
286
+ valueType: number
287
+ inputType: number
288
+ - slug: notes
289
+ label: Notes
290
+ valueType: string
291
+ inputType: textarea
292
+ - slug: contractValue
293
+ label: Contract Value
294
+ valueType: number
295
+ inputType: number
296
+ - slug: renewalDate
297
+ label: Renewal Date
298
+ valueType: date
299
+ inputType: date
300
+ - slug: healthScore
301
+ label: Health
302
+ valueType: string
303
+ inputType: select
304
+ enumValues:
305
+ - healthy
306
+ - at_risk
307
+ - critical
308
+
309
+ views:
310
+ - name: Ecosystem Map
311
+ slug: ecosystem-map
312
+ type: graph
313
+ scopeProfileSlugs: [company, market-segment, trend, ecosystem-event]
314
+ description: The market landscape as a connected map — actors, segments, trends, and events, with the edges between them.
315
+ defaultView: true
316
+ - name: Segments
317
+ slug: segments
318
+ type: table
319
+ scopeProfileSlug: market-segment
320
+ description: Market slices with rough size, ocean, and maturity.
321
+ config:
322
+ columns: [title, ocean, tam, maturity]
323
+ - name: Trends
324
+ slug: trends
325
+ type: table
326
+ scopeProfileSlug: trend
327
+ description: Directional shifts to track, by relevance.
328
+ config:
329
+ columns: [title, direction, relevance, description]
330
+ - name: Events
331
+ slug: events
332
+ type: table
333
+ scopeProfileSlug: ecosystem-event
334
+ description: Notable market events — raises, partnerships, launches, and more.
335
+ config:
336
+ columns: [title, event-type, event-date, event-impact]
337
+
338
+ # Schema-level relations — the market GRAPH. This is the net-new value: actor↔actor
339
+ # edges are first-class (source and target can both be `company`), so the Ecosystem
340
+ # Map and relation picker carry domain-specific links from day one.
341
+ entityLinks:
342
+ - sourceProfileSlug: company
343
+ targetProfileSlug: company
344
+ type: partners_with
345
+ label: Partners with
346
+ - sourceProfileSlug: company
347
+ targetProfileSlug: company
348
+ type: competes_with
349
+ label: Competes with
350
+ - sourceProfileSlug: company
351
+ targetProfileSlug: company
352
+ type: invested_in
353
+ label: Invested in
354
+ - sourceProfileSlug: company
355
+ targetProfileSlug: company
356
+ type: integrates_with
357
+ label: Integrates with
358
+ - sourceProfileSlug: competitor
359
+ targetProfileSlug: market-segment
360
+ type: competes_in
361
+ label: Competes in
362
+ - sourceProfileSlug: trend
363
+ targetProfileSlug: market-segment
364
+ type: shapes
365
+ label: Shapes
366
+ - sourceProfileSlug: ecosystem-event
367
+ targetProfileSlug: company
368
+ type: involves
369
+ label: Involves
370
+
371
+ onboarding:
372
+ goal: >
373
+ Map the real ecosystem — the key players (companies and people), who partners
374
+ and competes with whom, and the segments and trends that matter — so strategy
375
+ is grounded in the actual market, not a vague "the market".
376
+ framing: |
377
+ THE ECOSYSTEM LEAD: Act as a sharp ecosystem lead who maps the market
378
+ landscape AND the relationships inside it. Push for REAL named players — not
379
+ "the incumbents", but the actual companies — and for the concrete edges
380
+ between them: who partners with whom, who competes, who invested in whom.
381
+ A map with no edges is just a list; the value is in the relationships.
382
+ expertise:
383
+ starters:
384
+ - >
385
+ Name the actors, then draw the edges. A list of logos tells strategy
386
+ nothing; the market is a GRAPH. For every company you log, propose at least
387
+ one real relationship — partners_with, competes_with, invested_in,
388
+ integrates_with — because the edges are where the leverage and the threats
389
+ live.
390
+ - >
391
+ Map the CATEGORY, not just named rivals. Buyers compare across tiers:
392
+ direct competitors, adjacent tools that solve the same job differently, and
393
+ the status quo ("a spreadsheet", "do nothing"). Attach the competitor role
394
+ to real companies and place each one in the segment it competes in.
395
+ - >
396
+ Separate players from structure. Companies and people are the ACTORS;
397
+ segments and trends are the STRUCTURE they move within. Anchor each trend to
398
+ a direction and a "so what for us", and each segment to a rough size and
399
+ ocean — an untimed "AI is big" tells strategy nothing.
400
+ blindSpots:
401
+ - >
402
+ "We have no competitors" is a red flag, not a moat — it usually means the
403
+ category is undefined or the buyer's real alternative (a manual workaround)
404
+ hasn't been named. Push to name what a buyer does TODAY instead of buying.
405
+ - >
406
+ Founders over-watch the loud incumbent and miss the fast-moving insurgent
407
+ one tier down, and they log companies with no relationships at all. Surface
408
+ the insurgents, and force at least one real edge per key player.
409
+ - >
410
+ A trend or event with no stance is just a headline. Every trend needs a
411
+ direction (rising/steady/fading) and a "so what"; every logged event needs
412
+ an actor and an impact. Force the meaning, not just the mention.
413
+ bar: >
414
+ The map reads like a real ecosystem: named companies placed in their segments,
415
+ real edges between them (who partners, competes, invested), and each trend
416
+ carrying a direction and a "so what" — so a strategist can see the alliances,
417
+ the threats, and the open lane, not just a pile of logos.
418
+ collect:
419
+ - profileSlug: company
420
+ what: The real companies in your ecosystem — the players you operate around.
421
+ cardinality: several
422
+ keyFields: [industry, website]
423
+ - profileSlug: market-segment
424
+ what: The market slices you play in, with rough size and ocean.
425
+ cardinality: few
426
+ keyFields: [ocean, tam]
427
+ - profileSlug: trend
428
+ what: Directional shifts in tech, market, or behavior that affect you.
429
+ cardinality: few
430
+ keyFields: [direction, relevance]
431
+ openingQuestions:
432
+ - "Who are the key players in your space — the 3-5 companies that matter most?"
433
+ - "How do they relate — who partners with, competes with, or invested in whom?"
434
+ - "Which market segment matters most right now, and what trend is reshaping it?"
435
+ doneWhen: A few companies captured, with at least one relationship edge between them, plus one market segment.
436
+
437
+ app:
438
+ kind: stored
439
+ cellKey: entity-detail
440
+
441
+ layout:
442
+ defaultView: Ecosystem Map
443
+ defaultApp: null
444
+ pinnedApps: [dashboard, data]
445
+ sidebarItems:
446
+ - kind: view
447
+ viewName: Ecosystem Map
448
+ label: Ecosystem Map
449
+ icon: radar
450
+ - kind: view
451
+ viewName: Segments
452
+ label: Segments
453
+ icon: pie-chart
454
+ - kind: view
455
+ viewName: Trends
456
+ label: Trends
457
+ icon: trending-up
458
+ - kind: view
459
+ viewName: Events
460
+ label: Events
461
+ icon: zap
462
+ - kind: app
463
+ appId: data
464
+ label: Data
465
+ icon: database
466
+
467
+ bento:
468
+ viewName: Ecosystem Map
469
+ blocks:
470
+ - kind: view
471
+ viewName: Ecosystem Map
472
+ pos: { x: 0, y: 0, w: 8, h: 6 }
473
+ - kind: view
474
+ viewName: Segments
475
+ pos: { x: 8, y: 0, w: 4, h: 3 }
476
+ - kind: view
477
+ viewName: Trends
478
+ pos: { x: 8, y: 3, w: 4, h: 3 }