@synap-core/workspace-templates 0.12.0 → 0.23.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.
Files changed (245) hide show
  1. package/README.md +483 -0
  2. package/automations/bookmark-enrichment.automation.json +321 -0
  3. package/automations/github-pr-events.automation.json +505 -0
  4. package/automations/mail-feed.automation.json +86 -0
  5. package/automations/ship-a-change-checks-green.automation.json +106 -0
  6. package/automations/ship-a-change-checks-red.automation.json +106 -0
  7. package/automations/ship-a-change-deployed.automation.json +56 -0
  8. package/automations/ship-a-change-pr-merged.automation.json +110 -0
  9. package/automations/ship-a-change-pr-opened.automation.json +104 -0
  10. package/automations/url-bookmark-capture.automation.json +132 -0
  11. package/capabilities/agency-skills.capability.json +157 -0
  12. package/capabilities/apify.capability.json +265 -0
  13. package/capabilities/apollo.capability.json +120 -0
  14. package/capabilities/arch-backend.capability.json +97 -0
  15. package/capabilities/arch-client-intelligence.capability.json +401 -0
  16. package/capabilities/backblaze-b2.capability.json +205 -0
  17. package/capabilities/cal-com.capability.json +266 -0
  18. package/capabilities/canva.capability.json +274 -0
  19. package/capabilities/claude-code-routines.capability.json +172 -0
  20. package/capabilities/claude-managed-agents.capability.json +215 -0
  21. package/capabilities/client-comms-report.capability.json +246 -0
  22. package/capabilities/content-idea-to-production.capability.json +29 -0
  23. package/capabilities/datagouv.capability.json +315 -0
  24. package/capabilities/discord-bot.capability.json +157 -0
  25. package/capabilities/dropbox.capability.json +281 -0
  26. package/capabilities/exa.capability.json +109 -0
  27. package/capabilities/fal-ai.capability.json +83 -0
  28. package/capabilities/fal-media-lab.capability.json +158 -0
  29. package/capabilities/fireflies.capability.json +191 -0
  30. package/capabilities/freellmapi.capability.json +114 -0
  31. package/capabilities/generic-apikey.capability.json +92 -0
  32. package/capabilities/github-merge.capability.json +99 -0
  33. package/capabilities/github-read.capability.json +325 -0
  34. package/capabilities/governance-tighten-recommender.capability.json +81 -0
  35. package/capabilities/jev.capability.json +226 -0
  36. package/capabilities/lead-conversion-enrollment.capability.json +246 -0
  37. package/capabilities/mail-feed.capability.json +277 -0
  38. package/capabilities/mailgun.capability.json +52 -0
  39. package/capabilities/nango-google.capability.json +639 -0
  40. package/capabilities/notion.capability.json +417 -0
  41. package/capabilities/omniroute.capability.json +137 -0
  42. package/capabilities/posthog-analytics.capability.json +258 -0
  43. package/capabilities/postiz.capability.json +467 -0
  44. package/capabilities/proactive-digest.capability.json +190 -0
  45. package/capabilities/proton.capability.json +288 -0
  46. package/capabilities/replicate-rvm.capability.json +97 -0
  47. package/capabilities/research-methods.capability.json +57 -0
  48. package/capabilities/shopify.capability.json +395 -0
  49. package/capabilities/slack.capability.json +369 -0
  50. package/capabilities/stripe.capability.json +420 -0
  51. package/capabilities/telegram-bridge.capability.json +56 -0
  52. package/capabilities/treg.capability.json +102 -0
  53. package/capabilities/unipile-linkedin.capability.json +471 -0
  54. package/capabilities/vercel.capability.json +424 -0
  55. package/dist/define-CPS0n8BV.d.ts +1331 -0
  56. package/dist/index.d.ts +174 -900
  57. package/dist/index.js +26148 -23931
  58. package/dist/index.js.map +1 -1
  59. package/dist/wire.d.ts +222 -0
  60. package/dist/wire.js +77 -0
  61. package/dist/wire.js.map +1 -0
  62. package/package.json +27 -4
  63. package/packs/bookmark-views-pack.json +33 -0
  64. package/packs/crm-pack.json +76 -0
  65. package/packs/customer-profile.json +28 -0
  66. package/packs/deep-research.playbook.json +30 -0
  67. package/packs/event-views-pack.json +33 -0
  68. package/packs/habit-profile.json +27 -0
  69. package/packs/hr-pack.json +54 -0
  70. package/packs/invoice-profile.json +28 -0
  71. package/packs/knowledge-base-pack.json +56 -0
  72. package/packs/meeting-profile.json +29 -0
  73. package/packs/product-profile.json +30 -0
  74. package/packs/project-management-pack.json +49 -0
  75. package/packs/recipe-profile.json +29 -0
  76. package/packs/task-views-pack.json +39 -0
  77. package/retired.yaml +136 -0
  78. package/skills/app-factory-build-slice/SKILL.md +70 -0
  79. package/skills/app-factory-build-slice.catalog.json +17 -0
  80. package/skills/app-factory-design-canvas/SKILL.md +96 -0
  81. package/skills/app-factory-design-canvas.catalog.json +17 -0
  82. package/skills/app-factory-design-review/SKILL.md +99 -0
  83. package/skills/app-factory-design-review.catalog.json +18 -0
  84. package/skills/app-factory-research/SKILL.md +123 -0
  85. package/skills/app-factory-research.catalog.json +17 -0
  86. package/skills/bookmark-enrichment/SKILL.md +40 -0
  87. package/skills/carousel-format/carousel-format.skill.json +18 -0
  88. package/skills/collage-broll/collage-broll.skill.json +20 -0
  89. package/skills/content-taste/content-taste.skill.json +18 -0
  90. package/skills/creative-director/creative-director.skill.json +18 -0
  91. package/skills/design-critique/SKILL.md +135 -0
  92. package/skills/design-critique.catalog.json +12 -0
  93. package/skills/designing-with-real-components/SKILL.md +140 -0
  94. package/skills/designing-with-real-components.catalog.json +12 -0
  95. package/skills/edit-loop/edit-loop.skill.json +25 -0
  96. package/skills/facecam-edit/facecam-edit.skill.json +24 -0
  97. package/skills/mapping-the-server/SKILL.md +54 -0
  98. package/skills/math-explainer-manimgx/math-explainer-manimgx.skill.json +20 -0
  99. package/skills/motion-quality-gates/motion-quality-gates.skill.json +20 -0
  100. package/skills/open-design-edit/open-design-edit.skill.json +24 -0
  101. package/skills/reading-client-progress/SKILL.md +65 -0
  102. package/skills/routing-to-channels/SKILL.md +54 -0
  103. package/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
  104. package/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
  105. package/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
  106. package/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
  107. package/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
  108. package/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
  109. package/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
  110. package/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
  111. package/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
  112. package/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
  113. package/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
  114. package/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
  115. package/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
  116. package/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
  117. package/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
  118. package/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
  119. package/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
  120. package/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
  121. package/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
  122. package/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
  123. package/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
  124. package/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
  125. package/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
  126. package/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
  127. package/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
  128. package/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
  129. package/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
  130. package/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
  131. package/skills/video-direction/video-direction.skill.json +20 -0
  132. package/skills/video-style-vocabulary/video-style-vocabulary.skill.json +20 -0
  133. package/skills/video-toolbox/video-toolbox.skill.json +20 -0
  134. package/src/__fixtures__/lifecycle-presets.flat-before.json +205 -0
  135. package/src/agency-os.yaml +122 -0
  136. package/src/agent-os-skill-slugs.test.ts +65 -0
  137. package/src/apify-social-reader.test.ts +317 -0
  138. package/src/base.yaml +85 -39
  139. package/src/blockchain-ecosystem.yaml +1 -0
  140. package/src/brand-library.yaml +525 -14
  141. package/src/builder-dev-fold.template.test.ts +113 -0
  142. package/src/builder.yaml +779 -91
  143. package/src/business-model-catalog-description.test.ts +47 -0
  144. package/src/business-model-grp.template.test.ts +214 -0
  145. package/src/business-model.yaml +296 -49
  146. package/src/catalog-safety.test.ts +198 -0
  147. package/src/catalog.template-items.test.ts +75 -0
  148. package/src/catalog.ts +66 -2
  149. package/src/concept-guard.test.ts +28 -8
  150. package/src/concept-guard.ts +2 -2
  151. package/src/content-creation.template.test.ts +52 -0
  152. package/src/content-creation.yaml +192 -0
  153. package/src/content-os.edit-loop.template.test.ts +210 -0
  154. package/src/content-os.publish-due-posts.template.test.ts +261 -0
  155. package/src/content-os.template.test.ts +322 -0
  156. package/src/content-os.video-takes.template.test.ts +338 -0
  157. package/src/content-os.work-a-take.template.test.ts +125 -0
  158. package/src/content-os.yaml +1830 -39
  159. package/src/content-studio-retired.template.test.ts +82 -0
  160. package/src/corpus-flow-validity.test.ts +4 -5
  161. package/src/crm-absorbs-outreach.template.test.ts +117 -0
  162. package/src/crm-build-plan.template.test.ts +128 -0
  163. package/src/crm-lists.template.test.ts +96 -0
  164. package/src/crm-messages-cadence.template.test.ts +164 -0
  165. package/src/crm-one-send-door.template.test.ts +65 -0
  166. package/src/crm.yaml +1456 -181
  167. package/src/decisions-wave.template.test.ts +186 -0
  168. package/src/define.ts +130 -17
  169. package/src/design-to-implementation.template.test.ts +94 -0
  170. package/src/domain-template-type.test.ts +75 -0
  171. package/src/ecosystem.yaml +27 -66
  172. package/src/enterprise-os.yaml +10 -134
  173. package/src/facecam-edit.audio-studio.test.ts +188 -0
  174. package/src/fal-generate-image.test.ts +200 -0
  175. package/src/fal-media-lab-pod-files.test.ts +180 -0
  176. package/src/finance.yaml +254 -1
  177. package/src/foundation.yaml +78 -38
  178. package/src/frame-host-modules.d.mts +6 -0
  179. package/src/frame-host-modules.mjs +58 -0
  180. package/src/generate.mjs +9 -3
  181. package/src/github-merge.test.ts +210 -0
  182. package/src/grants.yaml +46 -17
  183. package/src/hr.yaml +39 -6
  184. package/src/index.ts +36 -1
  185. package/src/intent.test.ts +161 -0
  186. package/src/internal-runbook.yaml +19 -1
  187. package/src/jev.test.ts +356 -0
  188. package/src/legal.yaml +33 -1
  189. package/src/life-os.yaml +51 -240
  190. package/src/lifecycle-coverage.test.ts +124 -0
  191. package/src/lifecycle-declarations.test.ts +252 -0
  192. package/src/lifecycle-presets.test.ts +468 -0
  193. package/src/lifecycle-presets.ts +494 -0
  194. package/src/lifecycle-stuck.test.ts +46 -0
  195. package/src/lifecycle-waits.parity.test.ts +319 -0
  196. package/src/marketing.yaml +108 -4
  197. package/src/meetings-knowledge.yaml +18 -0
  198. package/src/networking.yaml +23 -2
  199. package/src/operations.yaml +304 -4
  200. package/src/pipeline-views.template.test.ts +88 -0
  201. package/src/playbook-goal-placeholders.test.ts +57 -0
  202. package/src/postiz-contract.test.ts +134 -0
  203. package/src/presentation-declarations.test.ts +174 -0
  204. package/src/process-contract.validate.test.ts +352 -0
  205. package/src/project-management.yaml +39 -740
  206. package/src/publish-body.d.mts +29 -0
  207. package/src/publish-body.mjs +74 -0
  208. package/src/publish-body.test.ts +74 -0
  209. package/src/publish-official-lib.test.ts +176 -0
  210. package/src/replicate-rvm.test.ts +504 -0
  211. package/src/research-base.yaml +119 -20
  212. package/src/research-decision.yaml +60 -3
  213. package/src/reserved-profile-slugs.test.ts +83 -0
  214. package/src/retired-goal-kinds.test.ts +58 -0
  215. package/src/role-reference-and-step-domain.test.ts +192 -0
  216. package/src/seed-obligation.test.ts +1 -6
  217. package/src/send-message-maps.test.ts +227 -0
  218. package/src/shared-role-ssot.test.ts +40 -2
  219. package/src/ship-a-change.template.test.ts +177 -0
  220. package/src/skills.test.ts +170 -0
  221. package/src/social.yaml +85 -27
  222. package/src/space-packages.freshness.test.ts +97 -0
  223. package/src/space-packages.generated.ts +40 -0
  224. package/src/space-packages.test.ts +125 -0
  225. package/src/space-packages.ts +73 -0
  226. package/src/start.test.ts +188 -0
  227. package/src/task-intents.test.ts +293 -0
  228. package/src/templates.ts +20851 -21496
  229. package/src/types.ts +443 -60
  230. package/src/unipile-publish-post.test.ts +162 -0
  231. package/src/validate-flow.drift.test.ts +2 -3
  232. package/src/validate-flow.ts +22 -2
  233. package/src/validate-onboarding.test.ts +59 -0
  234. package/src/validate.test.ts +71 -4
  235. package/src/validate.ts +1335 -7
  236. package/src/wire-core.ts +231 -0
  237. package/src/wire.test.ts +159 -0
  238. package/src/wire.ts +107 -0
  239. package/src/agent-fleet.yaml +0 -1645
  240. package/src/business-developer.template.test.ts +0 -351
  241. package/src/business-developer.yaml +0 -860
  242. package/src/content-studio.yaml +0 -361
  243. package/src/dev-dashboard.yaml +0 -990
  244. package/src/outreach-comms.yaml +0 -486
  245. package/src/the-arch.yaml +0 -201
@@ -1,351 +0,0 @@
1
- /**
2
- * Contract tests for the Business Developer CRM overlay.
3
- *
4
- * The catalog intentionally reads `templates.ts`, the generated source
5
- * consumed by every template installer.
6
- */
7
-
8
- import { describe, expect, it } from "vitest";
9
- import { toWorkspaceDefinition } from "./define.js";
10
- import { getWorkspaceTemplate } from "./templates.js";
11
-
12
- const template = (() => {
13
- const resolved = getWorkspaceTemplate("business-developer");
14
- if (!resolved) throw new Error("Business Developer template is not registered");
15
- return resolved;
16
- })();
17
-
18
- const crmTemplate = (() => {
19
- const resolved = getWorkspaceTemplate("crm");
20
- if (!resolved) throw new Error("CRM template is not registered");
21
- return resolved;
22
- })();
23
-
24
- function profile(slug: string) {
25
- const found = template?.profiles.find((candidate) => candidate.slug === slug);
26
- expect(found, `expected ${slug} profile`).toBeDefined();
27
- if (!found) throw new Error(`expected ${slug} profile`);
28
- return found;
29
- }
30
-
31
- function propertySlugs(slug: string) {
32
- return new Set(profile(slug).properties.map((property) => property.slug));
33
- }
34
-
35
- describe("Business Developer workspace overlay", () => {
36
- it("composes onto CRM without replacing its workspace identity", () => {
37
- expect(template).toBeDefined();
38
- expect(template.dependencies).toEqual([
39
- {
40
- slug: "crm",
41
- kind: "workspace",
42
- relation: "compose",
43
- reason: "Adds a lead-first business-development workflow to your CRM workspace.",
44
- },
45
- ]);
46
- expect(template.workspace.subtype).toBe("crm");
47
- expect(template.workspace.visibility).toBeUndefined();
48
- });
49
-
50
- it("extends the SHARED lead/client roles instead of forking them", () => {
51
- // The lifecycle roles are the pod-wide foundation SSOT, re-declared so this
52
- // workspace receives the grant. Row-level fields must match foundation
53
- // VERBATIM (one row, no overlay mechanism — shared-role-ssot rule (c)); the
54
- // business-development fields ride along as workspace-overlay PROPERTIES.
55
- for (const slug of ["lead", "client"]) {
56
- const facet = profile(slug);
57
- expect(facet.scope).toBe("shared");
58
- expect(facet.profileKind).toBe("role");
59
- expect(facet.applicableKinds).toEqual(["company", "person"]);
60
- expect(facet.semanticSlug).toBeUndefined();
61
- }
62
-
63
- // The forked spellings are GONE — reintroducing either is the regression.
64
- expect(
65
- template.profiles.some((candidate) => ["crm-lead", "crm-client"].includes(candidate.slug)),
66
- ).toBe(false);
67
-
68
- // Extras only: foundation's own `leadStage` / `clientStatus` / `notes` are
69
- // NOT restated here (restating with a different definition is base drift).
70
- expect([...propertySlugs("lead")]).not.toContain("leadStage");
71
- expect([...propertySlugs("client")]).not.toContain("clientStatus");
72
-
73
- expect([...propertySlugs("lead")].sort()).toEqual([
74
- "assignedUserId",
75
- "convertedAt",
76
- "disqualificationReason",
77
- "icpScore",
78
- "lastContactedAt",
79
- "lifecycleStage",
80
- "nextAction",
81
- "nextActionAt",
82
- "outreachChannels",
83
- "qualificationNotes",
84
- "source",
85
- ].sort());
86
-
87
- expect([...propertySlugs("client")].sort()).toEqual([
88
- "becameClientAt",
89
- "handoffDestination",
90
- "handoffNotes",
91
- "handoffStatus",
92
- ].sort());
93
- });
94
-
95
- it("models proposals and auditable commission separately from leads", () => {
96
- const commercialStages = profile("deal")
97
- .properties.find((property) => property.slug === "commercialStage")?.enumValues;
98
- expect(commercialStages).toEqual(["draft", "proposed", "negotiating", "won", "lost"]);
99
-
100
- expect([...propertySlugs("crm-commission-entitlement")].sort()).toEqual([
101
- "earnedAt",
102
- "estimatedAmount",
103
- "feeBasisAmount",
104
- "paidAt",
105
- "rateBps",
106
- "sequence",
107
- "sourceDealId",
108
- "sourcedByUserId",
109
- "status",
110
- "voidReason",
111
- ].sort());
112
-
113
- expect([...propertySlugs("deal")].sort()).toEqual([
114
- "commercialStage",
115
- "commissionEntitlementId",
116
- "commissionStatus",
117
- "expectedClientGain",
118
- "lostReason",
119
- "sourcedByUserId",
120
- "synapFeeAmount",
121
- "synapFeeModel",
122
- "synapFixedFee",
123
- "synapSuccessFeeRate",
124
- "wonAt",
125
- ].sort());
126
-
127
- const entitlementId = profile("deal").properties.find(
128
- (property) => property.slug === "commissionEntitlementId",
129
- );
130
- expect(entitlementId).toMatchObject({
131
- valueType: "entity_id",
132
- inputType: "entity_select",
133
- targetProfileSlug: "crm-commission-entitlement",
134
- });
135
- expect(
136
- profile("crm-commission-entitlement").properties.find(
137
- (property) => property.slug === "sourceDealId",
138
- ),
139
- ).toMatchObject({
140
- valueType: "entity_id",
141
- inputType: "entity_select",
142
- targetProfileSlug: "deal",
143
- });
144
-
145
- expect(
146
- template.entityLinks?.map(({ sourceProfileSlug, targetProfileSlug, type }) => ({
147
- sourceProfileSlug,
148
- targetProfileSlug,
149
- type,
150
- })),
151
- ).toEqual([
152
- { sourceProfileSlug: "deal", targetProfileSlug: "crm-commission-entitlement", type: "crm_commission_for_deal" },
153
- { sourceProfileSlug: "person", targetProfileSlug: "campaign", type: "member_of" },
154
- { sourceProfileSlug: "company", targetProfileSlug: "campaign", type: "member_of" },
155
- ]);
156
- });
157
-
158
- it("relies on CRM's single generic counterparty relationship", () => {
159
- // `linked_to_deal` stays the ONE generic counterparty edge the handoff
160
- // flow's `find-party` node reads. The role-qualified edges beside it are
161
- // ADDITIVE (a brokered deal has a client side and a partner side) and must
162
- // never displace it — hence the exact-order assertion.
163
- expect(
164
- crmTemplate.entityLinks?.map(({ sourceProfileSlug, targetProfileSlug, type }) => ({
165
- sourceProfileSlug,
166
- targetProfileSlug,
167
- type,
168
- })),
169
- ).toEqual([
170
- { sourceProfileSlug: "person", targetProfileSlug: "deal", type: "linked_to_deal" },
171
- { sourceProfileSlug: "company", targetProfileSlug: "deal", type: "linked_to_deal" },
172
- { sourceProfileSlug: "person", targetProfileSlug: "deal", type: "client_on_deal" },
173
- { sourceProfileSlug: "company", targetProfileSlug: "deal", type: "client_on_deal" },
174
- { sourceProfileSlug: "person", targetProfileSlug: "deal", type: "partner_on_deal" },
175
- { sourceProfileSlug: "company", targetProfileSlug: "deal", type: "partner_on_deal" },
176
- ]);
177
-
178
- // The handoff still reads ONLY the generic edge — a brokered deal's partner
179
- // side must not be mistaken for the counterparty that becomes the client.
180
- const handoff = template.automations?.find((a) => a.name === "Win and hand off");
181
- const findParty = (handoff?.flow.nodes as Array<{ id?: string; data?: unknown }>).find(
182
- (n) => n.id === "find-party",
183
- );
184
- expect((findParty?.data as { relationTypes?: string[] }).relationTypes).toEqual([
185
- "linked_to_deal",
186
- ]);
187
- });
188
-
189
- it("keeps playbooks lead-first and proposal-gated", () => {
190
- expect(
191
- template.playbooks?.map(({ name, subjectProfile }) => ({ name, profileSlug: subjectProfile?.profileSlug })),
192
- ).toEqual([
193
- { name: "Qualify a CRM lead", profileSlug: "lead" },
194
- { name: "Advance a commercial proposal", profileSlug: "deal" },
195
- ]);
196
- expect(template.playbooks?.[0]?.goalTemplate ?? "").toMatch(/Do not create a deal/);
197
- expect(template.playbooks?.[1]?.goalTemplate ?? "").toMatch(/Never mark the counterparty as a CRM client/);
198
- });
199
-
200
- it("declares the complete, human-confirmed handoff as a reusable flow", () => {
201
- const handoff = template.automations?.find(
202
- (automation) => automation.name === "Win and hand off",
203
- );
204
- expect(handoff).toMatchObject({
205
- key: "crm-win-and-handoff",
206
- triggerType: "manual",
207
- status: "active",
208
- });
209
- expect(handoff?.flow.nodes).toEqual(
210
- expect.arrayContaining([
211
- expect.objectContaining({ id: "read-deal", type: "entity_read" }),
212
- expect.objectContaining({ id: "find-party", type: "related_entities" }),
213
- expect.objectContaining({ id: "guard-lead", type: "guard" }),
214
- expect.objectContaining({ id: "commission-sequence", type: "select" }),
215
- expect.objectContaining({ id: "claim-first-client-deal", type: "claim" }),
216
- expect.objectContaining({ id: "claim-commission-entitlement", type: "claim" }),
217
- expect.objectContaining({ id: "prior-won-deals", type: "related_entities" }),
218
- expect.objectContaining({
219
- id: "is-first-client-deal",
220
- type: "compute",
221
- data: expect.objectContaining({
222
- left: "{{steps.claim-first-client-deal.output.claimed}}",
223
- right: "{{steps.has-no-prior-won-deals.output.result}}",
224
- }),
225
- }),
226
- expect.objectContaining({ id: "create-commission", type: "output" }),
227
- expect.objectContaining({ id: "convert-lead", type: "output" }),
228
- ]),
229
- );
230
- expect(handoff?.flow.nodes).toContainEqual(
231
- expect.objectContaining({
232
- id: "create-commission",
233
- data: expect.objectContaining({
234
- outputType: "entity_create",
235
- config: expect.objectContaining({ dedupeBy: "sourceDealId" }),
236
- }),
237
- }),
238
- );
239
- expect(handoff?.flow.nodes).toContainEqual(
240
- expect.objectContaining({
241
- id: "guard-deal",
242
- data: expect.objectContaining({
243
- checks: expect.arrayContaining([
244
- expect.objectContaining({
245
- path: "steps.read-deal.output.entity.properties.commercialStage",
246
- anyOf: expect.arrayContaining([
247
- expect.objectContaining({ path: "steps.read-deal.output.entity.properties.commercialStage", equals: "won" }),
248
- expect.objectContaining({ path: "steps.read-deal.output.entity.properties.dealStage", equals: "won" }),
249
- ]),
250
- }),
251
- ]),
252
- }),
253
- }),
254
- );
255
-
256
- // The handoff writes the SHARED role slugs — this is what makes the party
257
- // a client pod-wide (and therefore visible to the Operations pickup).
258
- expect(handoff?.flow.nodes).toContainEqual(
259
- expect.objectContaining({
260
- id: "attach-client",
261
- data: expect.objectContaining({
262
- outputType: "facet_attach",
263
- config: expect.objectContaining({ facetSlug: "client" }),
264
- }),
265
- }),
266
- );
267
- expect(handoff?.flow.nodes).toContainEqual(
268
- expect.objectContaining({
269
- id: "convert-lead",
270
- data: expect.objectContaining({
271
- outputType: "facet_update",
272
- config: expect.objectContaining({ facetSlug: "lead" }),
273
- }),
274
- }),
275
- );
276
- expect(handoff?.flow.nodes).toContainEqual(
277
- expect.objectContaining({
278
- id: "guard-lead",
279
- data: expect.objectContaining({
280
- checks: expect.arrayContaining([
281
- expect.objectContaining({ arrayIncludes: "lead" }),
282
- ]),
283
- }),
284
- }),
285
- );
286
-
287
- expect(template.actionPlacements).toEqual([
288
- expect.objectContaining({
289
- profileSlug: "person",
290
- kind: "playbook",
291
- ref: "Qualify a CRM lead",
292
- when: { requiredFacetSlugs: ["lead"] },
293
- }),
294
- expect.objectContaining({
295
- profileSlug: "company",
296
- kind: "playbook",
297
- ref: "Qualify a CRM lead",
298
- when: { requiredFacetSlugs: ["lead"] },
299
- }),
300
- expect.objectContaining({
301
- profileSlug: "deal",
302
- kind: "playbook",
303
- ref: "Advance a commercial proposal",
304
- }),
305
- expect.objectContaining({
306
- profileSlug: "deal",
307
- kind: "automation",
308
- ref: "Win and hand off",
309
- label: "Hand off won proposal",
310
- when: {
311
- propertyAnyEquals: { commercialStage: ["won"], dealStage: ["won"] },
312
- propertyNotEquals: { handoffStatus: "handed_off" },
313
- },
314
- confirmation: expect.objectContaining({
315
- confirmLabel: "Hand off proposal",
316
- }),
317
- }),
318
- ]);
319
- });
320
-
321
- it("carries playbooks through the workspace-create converter", () => {
322
- const { definition } = toWorkspaceDefinition("business-developer");
323
- const playbooks = definition.playbooks ?? [];
324
-
325
- expect(playbooks).toHaveLength(2);
326
- expect(playbooks[0]).toMatchObject({
327
- name: "Qualify a CRM lead",
328
- subjectProfile: { profileSlug: "lead" },
329
- params: [{ name: "lead", type: "string", description: "CRM lead" }],
330
- });
331
- expect(playbooks[1]).toMatchObject({
332
- name: "Advance a commercial proposal",
333
- subjectProfile: { profileSlug: "deal" },
334
- params: [{ name: "deal", type: "string", description: "Commercial proposal" }],
335
- });
336
-
337
- expect(definition.flowAutomations).toHaveLength(1);
338
- expect(definition.flowAutomations?.[0]).toMatchObject({
339
- key: "crm-win-and-handoff",
340
- name: "Win and hand off",
341
- triggerType: "manual",
342
- flowDefinition: expect.objectContaining({
343
- nodes: expect.arrayContaining([
344
- expect.objectContaining({ id: "read-deal", type: "entity_read" }),
345
- expect.objectContaining({ id: "commission-sequence", type: "select" }),
346
- expect.objectContaining({ id: "claim-first-client-deal", type: "claim" }),
347
- ]),
348
- }),
349
- });
350
- });
351
- });