@synap-core/workspace-templates 0.6.4 → 0.7.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.
@@ -0,0 +1,644 @@
1
+ # Business Developer — a composable, lead-first CRM operating model.
2
+ #
3
+ # This is an ADDITIVE overlay on the CRM workspace, not a second CRM and not a
4
+ # rewrite of shared identity profiles. It deliberately uses workspace-scoped
5
+ # `crm-lead` and `crm-client` role facets instead of the pod-wide `lead` and
6
+ # `client` vocabulary from Foundation. A person or company can therefore be a
7
+ # lead in one CRM workspace without becoming a lead everywhere in the pod.
8
+ #
9
+ # Lifecycle:
10
+ # person / company + crm-lead → optional deal(s) → won deal → crm-client
11
+ #
12
+ # Deals are commercial proposals attached through CRM's existing
13
+ # `linked_to_deal` relation. The CRM requires one counterparty before a deal
14
+ # can be won, but a counterparty can have more than one proposal over time. A
15
+ # client facet is only added when a deal is won; post-sale delivery belongs to
16
+ # another application.
17
+
18
+ meta:
19
+ slug: business-developer
20
+ name: Business Developer
21
+ description: "A lead-first sales motion for a CRM workspace: qualify leads, work commercial proposals, and hand won clients to the next system."
22
+ icon: target
23
+ color: "#D97706"
24
+ tags: [crm, sales, lead-management, business-development]
25
+ isPublic: true
26
+
27
+ # Compose onto an existing CRM workspace. The resolver installs CRM first, then
28
+ # applies these profiles and relations to that same workspace.
29
+ dependencies:
30
+ - slug: crm
31
+ kind: workspace
32
+ relation: compose
33
+ reason: "Adds a lead-first business-development workflow to your CRM workspace."
34
+
35
+ workspace:
36
+ name: CRM
37
+ description: A focused pre-sale workspace for lead qualification and commercial proposals.
38
+ subtype: crm
39
+ capabilities:
40
+ - crm.business-development
41
+
42
+ profiles:
43
+ # Workspace-local operational status. This is intentionally separate from
44
+ # Foundation's shared `lead` role: lead ownership, next actions, and CRM
45
+ # lifecycle status must be visible to this CRM workspace's members only.
46
+ - slug: crm-lead
47
+ displayName: CRM Lead
48
+ icon: user-round-search
49
+ color: amber
50
+ description: A person or company actively being qualified or contacted by this CRM workspace.
51
+ scope: WORKSPACE
52
+ profileKind: role
53
+ applicableKinds: [person, company]
54
+ properties:
55
+ - slug: lifecycleStage
56
+ label: Lifecycle stage
57
+ valueType: string
58
+ inputType: select
59
+ enumValues: [new, researching, outreach, engaged, qualified, nurturing, disqualified, converted]
60
+ constraints: { defaultValue: new }
61
+ - slug: assignedUserId
62
+ label: Assigned user ID
63
+ valueType: string
64
+ inputType: text
65
+ placeholder: Workspace member ID
66
+ - slug: source
67
+ label: Source
68
+ valueType: string
69
+ inputType: select
70
+ enumValues: [manual, referral, linkedin, email, event, inbound, partner, import, other]
71
+ - slug: icpScore
72
+ label: ICP score
73
+ valueType: number
74
+ inputType: number
75
+ - slug: outreachChannels
76
+ label: Outreach channels
77
+ valueType: array
78
+ inputType: text
79
+ placeholder: linkedin, email, phone
80
+ - slug: lastContactedAt
81
+ label: Last contacted at
82
+ valueType: date
83
+ inputType: date
84
+ - slug: nextActionAt
85
+ label: Next action at
86
+ valueType: date
87
+ inputType: date
88
+ - slug: nextAction
89
+ label: Next action
90
+ valueType: string
91
+ inputType: textarea
92
+ - slug: qualificationNotes
93
+ label: Qualification notes
94
+ valueType: string
95
+ inputType: textarea
96
+ - slug: disqualificationReason
97
+ label: Disqualification reason
98
+ valueType: string
99
+ inputType: textarea
100
+ - slug: convertedAt
101
+ label: Converted at
102
+ valueType: date
103
+ inputType: date
104
+
105
+ # The same counterparty receives this facet when one of its commercial
106
+ # proposals is won. It intentionally carries only handoff context; client
107
+ # delivery, renewal, and service health remain outside this pre-sale addon.
108
+ - slug: crm-client
109
+ displayName: CRM Client
110
+ icon: badge-check
111
+ color: emerald
112
+ description: A CRM lead converted by a won commercial proposal and ready for handoff.
113
+ scope: WORKSPACE
114
+ profileKind: role
115
+ applicableKinds: [person, company]
116
+ properties:
117
+ - slug: becameClientAt
118
+ label: Became client at
119
+ valueType: date
120
+ inputType: date
121
+ - slug: handoffStatus
122
+ label: Handoff status
123
+ valueType: string
124
+ inputType: select
125
+ enumValues: [pending, ready, handed_off]
126
+ constraints: { defaultValue: pending }
127
+ - slug: handoffDestination
128
+ label: Handoff destination
129
+ valueType: string
130
+ inputType: text
131
+ - slug: handoffNotes
132
+ label: Handoff notes
133
+ valueType: string
134
+ inputType: textarea
135
+
136
+ # CRM's original deal profile remains the generic compatibility layer. These
137
+ # fields are the additive commercial contract used by this addon; applications
138
+ # that opt into Business Developer read them rather than treating a deal board
139
+ # as the lead pipeline.
140
+ - slug: deal
141
+ displayName: Deal
142
+ icon: git-branch
143
+ color: violet
144
+ properties:
145
+ - slug: commercialStage
146
+ label: Commercial stage
147
+ valueType: string
148
+ inputType: select
149
+ enumValues: [draft, proposed, negotiating, won, lost]
150
+ constraints: { defaultValue: draft }
151
+ - slug: synapFeeModel
152
+ label: Synap fee model
153
+ valueType: string
154
+ inputType: select
155
+ enumValues: [fixed, success_fee]
156
+ - slug: synapFixedFee
157
+ label: Synap fixed fee
158
+ valueType: number
159
+ inputType: number
160
+ - slug: expectedClientGain
161
+ label: Expected client gain
162
+ valueType: number
163
+ inputType: number
164
+ - slug: synapSuccessFeeRate
165
+ label: Synap success fee rate
166
+ valueType: number
167
+ inputType: number
168
+ - slug: sourcedByUserId
169
+ label: Sourced by user ID
170
+ valueType: string
171
+ inputType: text
172
+ - slug: synapFeeAmount
173
+ label: Synap fee amount
174
+ valueType: number
175
+ inputType: number
176
+ - slug: commissionEntitlementId
177
+ label: Commission entitlement
178
+ valueType: entity_id
179
+ targetProfileSlug: crm-commission-entitlement
180
+ inputType: entity_select
181
+ - slug: commissionStatus
182
+ label: Commission status
183
+ valueType: string
184
+ inputType: select
185
+ enumValues: [estimated, earned, paid, requires_review, void]
186
+ - slug: wonAt
187
+ label: Won at
188
+ valueType: date
189
+ inputType: date
190
+ - slug: lostReason
191
+ label: Lost reason
192
+ valueType: string
193
+ inputType: textarea
194
+
195
+ # Immutable-at-creation commission records make first- and repeat-deal
196
+ # commissions auditable. The CRM creates one on a win: 20% of Synap's fee for
197
+ # the counterparty's first won deal, then 10% for later won deals.
198
+ - slug: crm-commission-entitlement
199
+ displayName: Commission Entitlement
200
+ icon: receipt-text
201
+ color: green
202
+ description: An auditable business-developer commission created from a won deal.
203
+ scope: WORKSPACE
204
+ properties:
205
+ # Correlates this record with its originating deal. The configured
206
+ # handoff automation uses it as its idempotency key, so a resumed run
207
+ # cannot create a second entitlement after a partial write.
208
+ - slug: sourceDealId
209
+ label: Source deal ID
210
+ valueType: entity_id
211
+ targetProfileSlug: deal
212
+ inputType: entity_select
213
+ - slug: sourcedByUserId
214
+ label: Sourced by user ID
215
+ valueType: string
216
+ inputType: text
217
+ - slug: sequence
218
+ label: Deal sequence
219
+ valueType: string
220
+ inputType: select
221
+ enumValues: [first, subsequent]
222
+ - slug: rateBps
223
+ label: Commission rate (basis points)
224
+ valueType: number
225
+ inputType: number
226
+ - slug: feeBasisAmount
227
+ label: Synap fee basis amount
228
+ valueType: number
229
+ inputType: number
230
+ - slug: estimatedAmount
231
+ label: Estimated commission amount
232
+ valueType: number
233
+ inputType: number
234
+ - slug: status
235
+ label: Status
236
+ valueType: string
237
+ inputType: select
238
+ enumValues: [estimated, earned, paid, void]
239
+ constraints: { defaultValue: estimated }
240
+ - slug: earnedAt
241
+ label: Earned at
242
+ valueType: date
243
+ inputType: date
244
+ - slug: paidAt
245
+ label: Paid at
246
+ valueType: date
247
+ inputType: date
248
+ - slug: voidReason
249
+ label: Void reason
250
+ valueType: string
251
+ inputType: textarea
252
+
253
+ entityLinks:
254
+ # The existing generic `linked_to_deal` relation remains the deal ↔
255
+ # counterparty connection. This overlay adds only the new auditable
256
+ # commission relation, avoiding a parallel relationship model.
257
+ - sourceProfileSlug: deal
258
+ targetProfileSlug: crm-commission-entitlement
259
+ type: crm_commission_for_deal
260
+ label: Commission entitlement
261
+
262
+ # These are guided, proposal-first sessions — not background automations. They
263
+ # never create a deal merely because a lead exists, and never convert a lead to
264
+ # a client until a human-confirmed deal win is proposed and applied.
265
+ playbooks:
266
+ - name: Qualify a CRM lead
267
+ description: Research and qualify one CRM lead, then make the next action explicit without creating a commercial proposal automatically.
268
+ goalTemplate: >-
269
+ Work on CRM lead ({lead}). Verify and enrich only factual information that
270
+ can be supported, assess fit, and update its CRM lifecycle stage, ICP score,
271
+ qualification notes, outreach channels, and next action. Do not create a
272
+ deal simply because the lead is qualified. If a commercial proposal is
273
+ warranted, explain why and ask for or propose that next step separately.
274
+ subjectProfile:
275
+ profileSlug: crm-lead
276
+ params:
277
+ - { name: lead, type: entity, label: CRM lead, required: true }
278
+ executor: is-agent
279
+ status: active
280
+
281
+ - name: Advance a commercial proposal
282
+ description: Help progress a linked deal without confusing commercial state with the lead pipeline or client conversion.
283
+ goalTemplate: >-
284
+ Work on commercial proposal ({deal}). Confirm its primary counterparty,
285
+ current commercial stage, fee model, expected fee, and next blocker. Propose
286
+ the single next action needed to advance it. Never mark the counterparty as
287
+ a CRM client unless the deal is genuinely won; a win must preserve the
288
+ sourced business developer and create the appropriate commission entitlement
289
+ (20% of Synap's fee for the first won deal with that counterparty, 10% later).
290
+ subjectProfile:
291
+ profileSlug: deal
292
+ params:
293
+ - { name: deal, type: entity, label: Commercial proposal, required: true }
294
+ executor: is-agent
295
+ status: active
296
+
297
+ # The deterministic counterpart to the guided playbooks above. This runs only
298
+ # after a human explicitly confirms the commercial win. It remains entirely
299
+ # domain-configured: generic workflow nodes read, validate, calculate, and
300
+ # write through the Pod's governed facet/relation/entity doors.
301
+ automations:
302
+ - key: crm-win-and-handoff
303
+ name: Win and hand off
304
+ description: Convert one qualified CRM lead after its proposal is won, record its commission entitlement, and leave a resumable handoff trail.
305
+ triggerType: manual
306
+ triggerConfig: {}
307
+ status: active
308
+ flow:
309
+ nodes:
310
+ - id: trigger
311
+ type: trigger
312
+ position: { x: 0, y: 0 }
313
+ data:
314
+ label: Mark won and hand off
315
+ triggerType: manual
316
+ config: {}
317
+
318
+ - id: read-deal
319
+ type: entity_read
320
+ position: { x: 240, y: 0 }
321
+ data:
322
+ label: Read proposal
323
+ entityId: "{{trigger.payload.entityId}}"
324
+
325
+ - id: guard-deal
326
+ type: guard
327
+ position: { x: 480, y: 0 }
328
+ data:
329
+ label: Confirm proposal is ready
330
+ checks:
331
+ - path: steps.read-deal.output.entity.type
332
+ equals: deal
333
+ message: This action is only available for a commercial proposal.
334
+ - path: steps.read-deal.output.entity.properties.commercialStage
335
+ anyOf:
336
+ - { path: steps.read-deal.output.entity.properties.commercialStage, equals: won }
337
+ - { path: steps.read-deal.output.entity.properties.dealStage, equals: won }
338
+ message: Mark the proposal as won before handing it off.
339
+ - path: steps.read-deal.output.entity.properties.handoffStatus
340
+ notEquals: handed_off
341
+ message: This proposal has already been handed off.
342
+ - path: steps.read-deal.output.entity.properties.sourcedByUserId
343
+ exists: true
344
+ message: Set the business developer who sourced this proposal before handing it off.
345
+ - path: steps.read-deal.output.entity.properties.synapFeeAmount
346
+ numberGte: 0
347
+ message: Set the confirmed Synap fee before handing this proposal off.
348
+
349
+ - id: find-party
350
+ type: related_entities
351
+ position: { x: 720, y: 0 }
352
+ data:
353
+ label: Find proposal counterparty
354
+ entityId: "{{trigger.payload.entityId}}"
355
+ direction: inbound
356
+ relationTypes: [linked_to_deal]
357
+ limit: 2
358
+
359
+ - id: guard-party
360
+ type: guard
361
+ position: { x: 960, y: 0 }
362
+ data:
363
+ label: Confirm one qualified counterparty
364
+ checks:
365
+ - path: steps.find-party.output.entities
366
+ lengthEquals: 1
367
+ message: A proposal must have exactly one commercial counterparty before it can be handed off.
368
+
369
+ - id: read-party
370
+ type: entity_read
371
+ position: { x: 1200, y: 0 }
372
+ data:
373
+ label: Read CRM lead
374
+ entityId: "{{steps.find-party.output.entities.0.id}}"
375
+
376
+ - id: guard-lead
377
+ type: guard
378
+ position: { x: 1440, y: 0 }
379
+ data:
380
+ label: Confirm qualified CRM lead
381
+ checks:
382
+ - path: steps.read-party.output.entity.facetSlugs
383
+ arrayIncludes: crm-lead
384
+ message: The commercial counterparty must still be a CRM lead before it becomes a CRM client.
385
+
386
+ # Source-deal claim is the durable idempotency boundary for the whole
387
+ # financial handoff. It must happen before the first-deal decision so
388
+ # duplicate clicks cannot reserve different commission policies.
389
+ - id: claim-commission-entitlement
390
+ type: claim
391
+ position: { x: 1680, y: 0 }
392
+ data:
393
+ label: Reserve commission entitlement
394
+ namespace: crm-commission-entitlement
395
+ key: "{{trigger.payload.entityId}}"
396
+
397
+ - id: guard-commission-claim
398
+ type: guard
399
+ position: { x: 1920, y: 0 }
400
+ data:
401
+ label: Confirm this run owns the commission write
402
+ checks:
403
+ - path: steps.claim-commission-entitlement.output.claimed
404
+ equals: true
405
+ message: This proposal handoff is already being handled by another workflow run.
406
+
407
+ # A durable generic claim serializes the one-time policy decision. It
408
+ # prevents two concurrent wins for the same counterparty from both
409
+ # receiving the first-deal rate, while retries of the winning run keep
410
+ # their original decision.
411
+ - id: claim-first-client-deal
412
+ type: claim
413
+ position: { x: 2160, y: 0 }
414
+ data:
415
+ label: Reserve first-client commission rate
416
+ namespace: crm-commission-first-deal
417
+ key: "{{steps.read-party.output.entity.id}}"
418
+
419
+ # Existing pods may already have won work from before this add-on. Read
420
+ # that history before applying the claim policy, including the legacy
421
+ # `dealStage` field until old CRM records are migrated.
422
+ - id: prior-won-deals
423
+ type: related_entities
424
+ position: { x: 2400, y: 0 }
425
+ data:
426
+ label: Find earlier won proposals
427
+ entityId: "{{steps.read-party.output.entity.id}}"
428
+ direction: outbound
429
+ relationTypes: [linked_to_deal]
430
+ excludeEntityId: "{{trigger.payload.entityId}}"
431
+ propertyAnyEquals:
432
+ commercialStage: [won]
433
+ dealStage: [won]
434
+ limit: 100
435
+
436
+ - id: has-no-prior-won-deals
437
+ type: condition
438
+ position: { x: 2640, y: 0 }
439
+ data:
440
+ label: Is this the first historical win?
441
+ expression: steps.prior-won-deals.output.count === 0
442
+
443
+ - id: is-first-client-deal
444
+ type: compute
445
+ position: { x: 2880, y: 120 }
446
+ data:
447
+ label: Confirm first-client commission
448
+ operation: multiply
449
+ left: "{{steps.claim-first-client-deal.output.claimed}}"
450
+ right: "{{steps.has-no-prior-won-deals.output.result}}"
451
+
452
+ # 1,000 bps for every deal, plus 1,000 bps for the winning first-deal claim.
453
+ # Number(true) is deliberately supported by the generic compute node,
454
+ # keeping the first/subsequent policy in config rather than application code.
455
+ - id: first-deal-bonus-bps
456
+ type: compute
457
+ position: { x: 2880, y: 0 }
458
+ data:
459
+ label: Calculate first-deal bonus
460
+ operation: multiply
461
+ left: 1000
462
+ right: "{{steps.is-first-client-deal.output.result}}"
463
+
464
+ - id: commission-sequence
465
+ type: select
466
+ position: { x: 3120, y: 0 }
467
+ data:
468
+ label: Label commission sequence
469
+ when: "{{steps.is-first-client-deal.output.result}}"
470
+ ifTrue: first
471
+ ifFalse: subsequent
472
+
473
+ - id: commission-rate-bps
474
+ type: compute
475
+ position: { x: 3360, y: 0 }
476
+ data:
477
+ label: Calculate commission rate
478
+ operation: add
479
+ left: 1000
480
+ right: "{{steps.first-deal-bonus-bps.output.result}}"
481
+
482
+ - id: commission-product
483
+ type: compute
484
+ position: { x: 3600, y: 0 }
485
+ data:
486
+ label: Calculate commission basis points
487
+ operation: multiply
488
+ left: "{{steps.read-deal.output.entity.properties.synapFeeAmount}}"
489
+ right: "{{steps.commission-rate-bps.output.result}}"
490
+
491
+ - id: commission-amount
492
+ type: compute
493
+ position: { x: 3840, y: 0 }
494
+ data:
495
+ label: Calculate commission amount
496
+ operation: divide
497
+ left: "{{steps.commission-product.output.result}}"
498
+ right: 10000
499
+
500
+ - id: handoff-time
501
+ type: compute
502
+ position: { x: 4080, y: 0 }
503
+ data:
504
+ label: Record handoff time
505
+ operation: now
506
+
507
+ - id: create-commission
508
+ type: output
509
+ position: { x: 4320, y: 0 }
510
+ data:
511
+ label: Record commission entitlement
512
+ outputType: entity_create
513
+ config:
514
+ profileSlug: crm-commission-entitlement
515
+ title: "Commission · {{steps.read-deal.output.entity.title}}"
516
+ # The durable claim above serializes creators; this lookup heals a
517
+ # crash after the entity write but before its step ledger update.
518
+ dedupeBy: sourceDealId
519
+ properties:
520
+ sourceDealId: "{{trigger.payload.entityId}}"
521
+ sourcedByUserId: "{{steps.read-deal.output.entity.properties.sourcedByUserId}}"
522
+ sequence: "{{steps.commission-sequence.output.value}}"
523
+ rateBps: "{{steps.commission-rate-bps.output.result}}"
524
+ feeBasisAmount: "{{steps.read-deal.output.entity.properties.synapFeeAmount}}"
525
+ estimatedAmount: "{{steps.commission-amount.output.result}}"
526
+ status: estimated
527
+ earnedAt: "{{steps.handoff-time.output.result}}"
528
+
529
+ - id: link-commission
530
+ type: output
531
+ position: { x: 4560, y: 0 }
532
+ data:
533
+ label: Link proposal and commission
534
+ outputType: relation_create
535
+ config:
536
+ fromEntityId: "{{trigger.payload.entityId}}"
537
+ toEntityId: "{{steps.create-commission.output.entityId}}"
538
+ relationType: crm_commission_for_deal
539
+ dedupe: true
540
+
541
+ - id: mark-deal-won
542
+ type: output
543
+ position: { x: 4800, y: 0 }
544
+ data:
545
+ label: Mark proposal won
546
+ outputType: entity_update
547
+ config:
548
+ entityId: "{{trigger.payload.entityId}}"
549
+ properties:
550
+ commercialStage: won
551
+ wonAt: "{{steps.handoff-time.output.result}}"
552
+ handoffStatus: handed_off
553
+ commissionEntitlementId: "{{steps.create-commission.output.entityId}}"
554
+ commissionStatus: estimated
555
+
556
+ - id: attach-client
557
+ type: output
558
+ position: { x: 5040, y: 0 }
559
+ data:
560
+ label: Attach CRM client role
561
+ outputType: facet_attach
562
+ config:
563
+ entityId: "{{steps.read-party.output.entity.id}}"
564
+ facetSlug: crm-client
565
+ properties:
566
+ becameClientAt: "{{steps.handoff-time.output.result}}"
567
+ handoffStatus: handed_off
568
+
569
+ - id: convert-lead
570
+ type: output
571
+ position: { x: 5280, y: 0 }
572
+ data:
573
+ label: Complete CRM lead lifecycle
574
+ outputType: facet_update
575
+ config:
576
+ entityId: "{{steps.read-party.output.entity.id}}"
577
+ facetSlug: crm-lead
578
+ properties:
579
+ lifecycleStage: converted
580
+ convertedAt: "{{steps.handoff-time.output.result}}"
581
+
582
+ edges:
583
+ - { id: trigger-read, source: trigger, target: read-deal }
584
+ - { id: read-guard, source: read-deal, target: guard-deal }
585
+ - { id: guard-party-search, source: guard-deal, target: find-party }
586
+ - { id: party-search-guard, source: find-party, target: guard-party }
587
+ - { id: party-guard-read, source: guard-party, target: read-party }
588
+ - { id: party-read-guard, source: read-party, target: guard-lead }
589
+ - { id: lead-guard-commission-claim, source: guard-lead, target: claim-commission-entitlement }
590
+ - { id: commission-claim-guard, source: claim-commission-entitlement, target: guard-commission-claim }
591
+ - { id: commission-guard-first-claim, source: guard-commission-claim, target: claim-first-client-deal }
592
+ - { id: first-claim-history, source: claim-first-client-deal, target: prior-won-deals }
593
+ - { id: history-condition, source: prior-won-deals, target: has-no-prior-won-deals }
594
+ - { id: first-confirm-bonus, source: is-first-client-deal, target: first-deal-bonus-bps }
595
+ - { id: first-history-confirm, source: has-no-prior-won-deals, target: is-first-client-deal }
596
+ - { id: first-confirm-sequence, source: is-first-client-deal, target: commission-sequence }
597
+ - { id: bonus-rate, source: first-deal-bonus-bps, target: commission-rate-bps }
598
+ - { id: rate-product, source: commission-rate-bps, target: commission-product }
599
+ - { id: product-amount, source: commission-product, target: commission-amount }
600
+ - { id: amount-time, source: commission-amount, target: handoff-time }
601
+ - { id: time-commission, source: handoff-time, target: create-commission }
602
+ - { id: sequence-commission, source: commission-sequence, target: create-commission }
603
+ - { id: commission-link, source: create-commission, target: link-commission }
604
+ - { id: link-deal, source: link-commission, target: mark-deal-won }
605
+ - { id: deal-client, source: mark-deal-won, target: attach-client }
606
+ - { id: client-lead, source: attach-client, target: convert-lead }
607
+
608
+ # Rendered by both CRM and Browser through the shared entity-action runner.
609
+ # The lead facet requirement keeps the qualification playbook out of general
610
+ # person/company records; the commercial action is explicitly confirmed because
611
+ # it writes a client handoff and commission record.
612
+ actionPlacements:
613
+ - profileSlug: person
614
+ surface: entity-detail
615
+ kind: playbook
616
+ ref: Qualify a CRM lead
617
+ label: Qualify lead
618
+ when: { requiredFacetSlugs: [crm-lead] }
619
+
620
+ - profileSlug: company
621
+ surface: entity-detail
622
+ kind: playbook
623
+ ref: Qualify a CRM lead
624
+ label: Qualify lead
625
+ when: { requiredFacetSlugs: [crm-lead] }
626
+
627
+ - profileSlug: deal
628
+ surface: entity-detail
629
+ kind: playbook
630
+ ref: Advance a commercial proposal
631
+ label: Advance proposal
632
+
633
+ - profileSlug: deal
634
+ surface: entity-detail
635
+ kind: automation
636
+ ref: Win and hand off
637
+ label: Hand off won proposal
638
+ when:
639
+ propertyAnyEquals: { commercialStage: [won], dealStage: [won] }
640
+ propertyNotEquals: { handoffStatus: handed_off }
641
+ confirmation:
642
+ title: Hand off this won proposal?
643
+ description: This converts its qualified CRM lead counterparty and records the business developer commission.
644
+ confirmLabel: Hand off proposal