@elevasis/sdk 1.12.0 → 1.13.1

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,255 @@
1
+ ---
2
+ title: Build and Extend CRM
3
+ description: Map the CRM platform primitives available to SDK projects: shared UI pages, sidebar composition, data hooks, action definitions, workflow adapters, contracts, and org-model extension boundaries.
4
+ ---
5
+ <!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
6
+ <!-- Regenerate: pnpm scaffold:sync -->
7
+
8
+
9
+ # Build and Extend CRM
10
+
11
+ Use this recipe when a downstream project wants to build on the shared CRM instead of forking it.
12
+
13
+ Good trigger phrases:
14
+
15
+ - "Add a CRM reports page."
16
+ - "Build a custom deal workspace."
17
+ - "Read and update deals from a workflow."
18
+ - "Add a Send Quote button."
19
+ - "Change the CRM pipeline stages for this business."
20
+
21
+ CRM is a layered platform surface, not one component:
22
+
23
+ - **Organization OS:** feature gates, sales pipeline semantics, quick access, and labels live in the organization model.
24
+ - **Shared UI:** CRM pages, sidebars, workbench panels, overview widgets, and Kanban/detail components live in `@elevasis/ui`.
25
+ - **Headless hooks:** deal, company, contact, note, task, list, transition, and action hooks live under `@elevasis/ui/hooks`.
26
+ - **Action system:** `ActionDef`, `DEFAULT_CRM_ACTIONS`, `deriveActions`, and provider-level `crmActions` configure deal actions.
27
+ - **Workflow adapters:** `crm` and `acqDb` from `@elevasis/sdk/worker` let workflows read and mutate CRM/acquisition data through platform tools.
28
+ - **Contracts:** generated contract docs expose the current CRM shapes in `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
29
+
30
+ ## Decision Table
31
+
32
+ | User wants | Start here | Notes |
33
+ | --- | --- | --- |
34
+ | Change CRM feature availability, labels, or pipeline stages | `core/config/organization-model.ts` plus `core/config/organization-model.examples.ts` | Treat this as Organization OS work. Use the project's configure ceremony if available. |
35
+ | Add CRM sidebar nav or a CRM route | `@elevasis/ui/features/crm` and `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md` | Prefer manifest/sidebar composition. Do not fork shared source first. |
36
+ | Wrap a shared CRM page with project chrome | `DealsListPage`, `DealDetailPage`, `CrmOverview` from `@elevasis/ui/features/crm` | Keep route files thin and put project-specific logic in local feature modules. |
37
+ | Build a custom deal page | `useDealDetail`, `useDealNotes`, `useDealTasks`, `useExecuteAction` from `@elevasis/ui/hooks` | Use hooks for platform data and compose your own UI. |
38
+ | Add, hide, or replace deal action buttons | [customize-crm-actions.md](customize-crm-actions.md) | Start with the shared `crmActions` provider path; use project-owned UI when a custom workflow path is outside platform-known/default action dispatch constraints. |
39
+ | Read or mutate CRM data inside a workflow | `crm` or `acqDb` from `@elevasis/sdk/worker` | `organizationId` is injected server-side by the platform dispatcher. Do not pass it from workflow code. |
40
+ | Add a new persisted CRM column or table | Platform/API migration work, not just scaffold work | Update DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts together. |
41
+
42
+ ## Published CRM Surfaces
43
+
44
+ | Surface | Import from | Use for |
45
+ | --- | --- | --- |
46
+ | `crmManifest`, `CRM_ITEMS`, `CrmSidebar`, `CrmSidebarTop`, `CrmSidebarMiddle` | `@elevasis/ui/features/crm` | Feature registration and sidebar composition |
47
+ | `CrmOverview`, `DealsListPage`, `DealDetailPage`, `CompanyDetailPage` | `@elevasis/ui/features/crm` | Shared CRM pages you can route to or wrap |
48
+ | `MyTasksPanel`, `SavedViewsPanel`, `QuickCreateActions` | `@elevasis/ui/features/crm` | Workbench/sidebar panels for custom CRM sidebars |
49
+ | `PipelineFunnelWidget`, `ActivityFeedWidget`, `MetricsStrip` | `@elevasis/ui/features/crm` | Overview widgets for custom dashboards |
50
+ | `KanbanBoard`, `DealKanbanCard`, `DealDrawer` | `@elevasis/ui/components` | Lower-level CRM deal UI primitives |
51
+ | `useDeals`, `useDealDetail`, `useTransitionItem`, `useExecuteAction`, `useDealNotes`, `useDealTasks` | `@elevasis/ui/hooks` | Headless deal and task data access |
52
+ | `useCompanies`, `useContacts`, `useLists`, `useBatchTelemetry` | `@elevasis/ui/hooks` | Acquisition substrate data access |
53
+ | `ElevasisUIProvider`, `ElevasisCoreProvider`, `useElevasisServices` | `@elevasis/ui/provider` | Provider setup, API access, and organization context |
54
+ | `ActionDef`, `DEFAULT_CRM_ACTIONS`, `deriveActions` | `@elevasis/sdk` | Deal action configuration and render-time derivation |
55
+ | `crm`, `acqDb` | `@elevasis/sdk/worker` | Workflow-side CRM/acquisition platform adapters |
56
+
57
+ Read the generated contracts before changing typed boundaries:
58
+
59
+ `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
60
+
61
+ Look for the **CRM Platform Primitives** section. It includes deal stages, deal rows, task shapes, API schemas, action definitions, and the focused CRM workflow adapter map. The broader acquisition/list adapter maps live under **Lead Gen Platform Primitives**.
62
+
63
+ ## 1. Extend CRM Navigation or Sidebar
64
+
65
+ For a simple nav addition, extend `CRM_ITEMS` and override the CRM feature manifest:
66
+
67
+ ```tsx
68
+ // ui/src/routes/__root.tsx
69
+ import { crmManifest, CRM_ITEMS, CrmSidebar, CrmSidebarMiddle } from '@elevasis/ui/features/crm'
70
+ import type { FeatureModule } from '@elevasis/ui/provider'
71
+ import type { NavItem } from '@elevasis/ui/layout'
72
+ import { IconFileText } from '@tabler/icons-react'
73
+
74
+ const customCrmItems: NavItem[] = [
75
+ ...CRM_ITEMS,
76
+ { label: 'Reports', to: '/crm/reports', icon: IconFileText, exact: false }
77
+ ]
78
+
79
+ const CustomCrmSidebar = () => (
80
+ <CrmSidebar>
81
+ <CrmSidebarMiddle items={customCrmItems} />
82
+ </CrmSidebar>
83
+ )
84
+
85
+ export const customCrmManifest: FeatureModule = {
86
+ ...crmManifest,
87
+ sidebar: CustomCrmSidebar
88
+ }
89
+ ```
90
+
91
+ Then replace `crmManifest` with `customCrmManifest` in the local `FEATURE_MANIFESTS` array and add the matching route under `ui/src/routes/crm/`.
92
+
93
+ For structural changes, compose `CrmSidebarTop`, `SubshellNavList`, `SubshellSidebarSection`, `MyTasksPanel`, and `QuickCreateActions`. The full sidebar decision tree lives in `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
94
+
95
+ ## 2. Wrap Shared CRM Pages
96
+
97
+ If the shared page is close, keep it and wrap it:
98
+
99
+ ```tsx
100
+ // ui/src/routes/crm/deals.index.tsx
101
+ import { createFileRoute } from '@tanstack/react-router'
102
+ import { DealsListPage } from '@elevasis/ui/features/crm'
103
+ import { Stack } from '@mantine/core'
104
+ import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
105
+
106
+ export const Route = createFileRoute('/crm/deals/')({
107
+ component: DealsRoute
108
+ })
109
+
110
+ function DealsRoute() {
111
+ return (
112
+ <Stack gap={0}>
113
+ <ProjectAnnouncementBanner context="crm-deals" />
114
+ <DealsListPage />
115
+ </Stack>
116
+ )
117
+ }
118
+ ```
119
+
120
+ Use the same pattern for `CrmOverview`, `DealDetailPage`, and `CompanyDetailPage`.
121
+
122
+ ## 3. Build a Custom Deal Page
123
+
124
+ When the project needs custom layout or additional workflows, use the hooks directly:
125
+
126
+ ```tsx
127
+ import { Button, Group, Stack, Textarea } from '@mantine/core'
128
+ import { useState } from 'react'
129
+ import { useDealDetail, useDealNotes, useCreateDealNote, useExecuteAction } from '@elevasis/ui/hooks'
130
+
131
+ export function CustomDealWorkspace({ dealId }: { dealId: string }) {
132
+ const { data: deal, isLoading } = useDealDetail(dealId)
133
+ const { data: notes = [] } = useDealNotes(dealId)
134
+ const createNote = useCreateDealNote()
135
+ const executeAction = useExecuteAction({ dealId })
136
+ const [body, setBody] = useState('')
137
+
138
+ if (isLoading) return null
139
+ if (!deal) return <div>Deal not found</div>
140
+
141
+ return (
142
+ <Stack>
143
+ <h1>{deal.contact_email}</h1>
144
+ <Group>
145
+ <Button onClick={() => executeAction.mutate({ key: 'move_to_proposal' })}>
146
+ Move to Proposal
147
+ </Button>
148
+ </Group>
149
+ <Textarea value={body} onChange={(event) => setBody(event.currentTarget.value)} />
150
+ <Button onClick={() => createNote.mutate({ dealId, body })}>Add Note</Button>
151
+ <pre>{JSON.stringify(notes, null, 2)}</pre>
152
+ </Stack>
153
+ )
154
+ }
155
+ ```
156
+
157
+ `useExecuteAction` is for platform-known/default action keys. Start custom action UI with the shared `crmActions` provider path; when a project-owned workflow is outside that server-dispatched action set, call `/execute` or `/execute-async` through `useElevasisServices`, or follow [customize-crm-actions.md](customize-crm-actions.md).
158
+
159
+ ## 4. Read and Mutate CRM Data in Workflows
160
+
161
+ Inside deployed workflows, use worker adapters instead of browser hooks or direct database access:
162
+
163
+ ```ts
164
+ // operations/src/sales/follow-up-stale-deals.ts
165
+ import type { WorkflowDefinition } from '@elevasis/sdk'
166
+ import { crm } from '@elevasis/sdk/worker'
167
+ import { z } from 'zod'
168
+
169
+ const inputSchema = z.object({
170
+ stage: z.string().default('proposal')
171
+ })
172
+
173
+ const outputSchema = z.object({
174
+ touched: z.number()
175
+ })
176
+
177
+ export const followUpStaleDealsWorkflow: WorkflowDefinition = {
178
+ config: {
179
+ resourceId: 'follow-up-stale-deals',
180
+ name: 'Follow Up Stale Deals',
181
+ type: 'workflow',
182
+ version: '1.0.0',
183
+ status: 'dev',
184
+ links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
185
+ },
186
+ contract: { inputSchema, outputSchema },
187
+ steps: {
188
+ followUp: {
189
+ id: 'followUp',
190
+ name: 'Follow Up',
191
+ inputSchema,
192
+ outputSchema,
193
+ next: null,
194
+ handler: async (input) => {
195
+ const deals = await crm.listDeals({ stage: input.stage })
196
+
197
+ for (const deal of deals) {
198
+ await crm.createDealTask({
199
+ dealId: deal.id,
200
+ title: 'Follow up',
201
+ kind: 'email'
202
+ })
203
+ await crm.recordActivity({
204
+ dealId: deal.id,
205
+ type: 'workflow.follow_up_task_created',
206
+ title: 'Follow-up task created',
207
+ payload: { workflow: 'follow-up-stale-deals' }
208
+ })
209
+ }
210
+
211
+ return { touched: deals.length }
212
+ }
213
+ }
214
+ },
215
+ entryPoint: 'followUp'
216
+ }
217
+ ```
218
+
219
+ Use `crm` for focused CRM deal reads, notes, tasks, activity, and cleanup. Use `acqDb` when the workflow needs the broader acquisition substrate: companies, contacts, lists, list-stage transitions, enrichment data, social posts, or lower-level deal sync methods.
220
+
221
+ ## 5. Customize Pipeline Semantics
222
+
223
+ CRM feature identity remains `sales.crm`, but the organization-model domain is `sales`.
224
+
225
+ For stage labels or stage sets, start from the project reference file:
226
+
227
+ `core/config/organization-model.examples.ts`
228
+
229
+ Then update the project model:
230
+
231
+ `core/config/organization-model.ts`
232
+
233
+ Keep these boundaries straight:
234
+
235
+ - Org-model sales pipelines describe business semantics.
236
+ - `DealStage` and platform action defaults describe the current shared platform deal state vocabulary.
237
+ - UI wrappers can relabel and route around shared surfaces, but changing persisted platform stages requires coordinated core/API/UI updates.
238
+ - Do not add `sales.actions` to the org model in v1. Deal action customization is covered by [customize-crm-actions.md](customize-crm-actions.md).
239
+
240
+ ## Verify
241
+
242
+ Run the checks for the surfaces you touched:
243
+
244
+ ```bash
245
+ pnpm -C ui run check
246
+ pnpm -C operations run check
247
+ pnpm -C operations exec elevasis-sdk check
248
+ ```
249
+
250
+ If you changed platform-level CRM contracts in the monorepo, the platform maintainer must also regenerate and verify scaffold output:
251
+
252
+ ```bash
253
+ pnpm scaffold:sync
254
+ pnpm scaffold:verify
255
+ ```
@@ -0,0 +1,297 @@
1
+ ---
2
+ title: Build and Extend Lead Gen
3
+ description: Map the lead-gen platform primitives available to SDK projects: shared UI pages, sidebar composition, data hooks, list/member state, artifacts, touchpoints, workflow adapters, contracts, and org-model extension boundaries.
4
+ ---
5
+ <!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
6
+ <!-- Regenerate: pnpm scaffold:sync -->
7
+
8
+
9
+ # Build and Extend Lead Gen
10
+
11
+ Use this recipe when a downstream project wants to build on the shared lead-gen system instead of forking it.
12
+
13
+ Good trigger phrases:
14
+
15
+ - "Add a lead-gen reports page."
16
+ - "Build a campaign creator surface."
17
+ - "Customize the lead-gen sidebar."
18
+ - "Show artifacts or touchpoints for a campaign."
19
+ - "Read or update lead-gen lists from a workflow."
20
+ - "Change the lead-gen lifecycle for this business."
21
+
22
+ Lead gen is a layered platform surface, not one component:
23
+
24
+ - **Organization OS:** prospecting semantics, feature gates, pipeline labels, and quick access live in the organization model.
25
+ - **Shared UI:** lead-gen overview, lists, list detail, companies, contacts, sidebars, and drawer components live in `@elevasis/ui`.
26
+ - **Headless hooks:** lists, companies, contacts, artifacts, touchpoints, list members, transitions, and derived actions live under `@elevasis/ui/hooks`.
27
+ - **Acquisition substrate:** `acq_lists`, `acq_companies`, `acq_contacts`, `acq_artifacts`, `acq_touchpoints`, list members, list companies, telemetry, and stateful transition shapes are exposed through generated contracts.
28
+ - **Workflow adapters:** `acqDb` and `list` from `@elevasis/sdk/worker` let workflows read and mutate lead-gen data through platform tools.
29
+ - **Contracts:** generated contract docs expose the current lead-gen shapes in `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
30
+
31
+ ## Decision Table
32
+
33
+ | User wants | Start here | Notes |
34
+ | --- | --- | --- |
35
+ | Change lead-gen feature availability, labels, quick access, or pipeline semantics | `core/config/organization-model.ts` plus `core/config/organization-model.examples.ts` | Treat this as Organization OS work. Use the project's configure ceremony if available. |
36
+ | Add lead-gen sidebar nav or a lead-gen route | `@elevasis/ui/features/lead-gen` and `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md` | Prefer manifest/sidebar composition. Do not fork shared source first. |
37
+ | Wrap a shared lead-gen page with project chrome | `LeadGenOverviewPage`, `LeadGenListsPage`, `LeadGenListDetailPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | Keep route files thin and put project-specific behavior in local feature modules. |
38
+ | Build a custom campaign/list workspace | `useLists`, `useList`, `useListMembers`, `useArtifacts`, `useTouchpoints`, `useTransitionListMember` from `@elevasis/ui/hooks` | Use hooks for platform data and compose your own UI. |
39
+ | Render artifacts, touchpoints, or list-member detail | `LeadGenListDetailPage`, `useArtifacts`, `useCreateArtifact`, `useTouchpoints`, `useListMember` | Artifacts and touchpoints are substrate primitives. Keep vertical-specific rendering local until there are repeated use cases. |
40
+ | Read or mutate lead-gen data inside a workflow | `acqDb` or `list` from `@elevasis/sdk/worker` | `organizationId` is injected server-side by the platform dispatcher. Do not pass it from workflow code. |
41
+ | Add a new persisted lead-gen field, artifact kind, or transition API | Platform/API migration work, not just scaffold work | Update DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts together. |
42
+
43
+ ## Published Lead-Gen Surfaces
44
+
45
+ | Surface | Import from | Use for |
46
+ | --- | --- | --- |
47
+ | `leadGenManifest`, `LEAD_GEN_ITEMS`, `LeadGenSidebar`, `LeadGenSidebarTop`, `LeadGenSidebarMiddle` | `@elevasis/ui/features/lead-gen` | Feature registration and sidebar composition |
48
+ | `LeadGenOverviewPage`, `LeadGenListsPage`, `LeadGenListDetailPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | `@elevasis/ui/features/lead-gen` | Shared lead-gen pages you can route to or wrap |
49
+ | `LeadGenRouteShell`, `CompanyDetailModal`, `ContactDetailModal`, `LIST_TEMPLATE_OPTIONS`, `buildListConfig` | `@elevasis/ui/features/lead-gen` | Route shell helpers, detail modals, and list creation config helpers |
50
+ | `useLists`, `useList`, `useListsTelemetry`, `useListProgress`, `useListExecutions`, `useCreateList`, `useUpdateList`, `useUpdateListConfig`, `useDeleteList` | `@elevasis/ui/hooks` | Headless list and telemetry data access |
51
+ | `useCompanies`, `useCompany`, `useContacts`, `useContact` | `@elevasis/ui/hooks` | Acquisition company/contact data access |
52
+ | `useArtifacts`, `useCreateArtifact`, `useTouchpoints`, `useListMembers`, `useListMember` | `@elevasis/ui/hooks` | Lead-gen substrate data access |
53
+ | `useTransitionList`, `useTransitionListMember`, `useTransitionListCompany`, `useDeriveActions` | `@elevasis/ui/hooks` | Stateful transition mutations and contextual action derivation |
54
+ | `ElevasisUIProvider`, `ElevasisCoreProvider`, `useElevasisServices` | `@elevasis/ui/provider` | Provider setup, API access, and organization context |
55
+ | `acqDb`, `list` | `@elevasis/sdk/worker` | Workflow-side acquisition and list-scoped platform adapters |
56
+
57
+ Read the generated contracts before changing typed boundaries:
58
+
59
+ `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
60
+
61
+ Look for the **Lead Gen Platform Primitives** section. It includes list shapes, company/contact shapes, list config, telemetry, artifacts, touchpoints, list members, stateful pipeline definitions, and workflow adapter method maps.
62
+
63
+ ## 1. Extend Lead-Gen Navigation or Sidebar
64
+
65
+ For a simple nav addition, extend `LEAD_GEN_ITEMS` and override the lead-gen feature manifest:
66
+
67
+ ```tsx
68
+ // ui/src/routes/__root.tsx
69
+ import { leadGenManifest, LEAD_GEN_ITEMS, LeadGenSidebar, LeadGenSidebarMiddle } from '@elevasis/ui/features/lead-gen'
70
+ import type { FeatureModule } from '@elevasis/ui/provider'
71
+ import type { NavItem } from '@elevasis/ui/layout'
72
+ import { IconChartBar } from '@tabler/icons-react'
73
+
74
+ const customLeadGenItems: NavItem[] = [
75
+ ...LEAD_GEN_ITEMS,
76
+ { label: 'Reports', to: '/lead-gen/reports', icon: IconChartBar, exact: false }
77
+ ]
78
+
79
+ const CustomLeadGenSidebar = () => (
80
+ <LeadGenSidebar>
81
+ <LeadGenSidebarMiddle items={customLeadGenItems} />
82
+ </LeadGenSidebar>
83
+ )
84
+
85
+ export const customLeadGenManifest: FeatureModule = {
86
+ ...leadGenManifest,
87
+ sidebar: CustomLeadGenSidebar
88
+ }
89
+ ```
90
+
91
+ Then replace `leadGenManifest` with `customLeadGenManifest` in the local `FEATURE_MANIFESTS` array and add the matching route under `ui/src/routes/lead-gen/`.
92
+
93
+ For structural changes, compose `LeadGenSidebarTop`, `SubshellNavList`, and `SubshellSidebarSection`. The full sidebar decision tree lives in `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
94
+
95
+ ## 2. Wrap Shared Lead-Gen Pages
96
+
97
+ If the shared page is close, keep it and wrap it:
98
+
99
+ ```tsx
100
+ // ui/src/routes/lead-gen/lists.index.tsx
101
+ import { createFileRoute } from '@tanstack/react-router'
102
+ import { LeadGenListsPage } from '@elevasis/ui/features/lead-gen'
103
+ import { Stack } from '@mantine/core'
104
+ import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
105
+
106
+ export const Route = createFileRoute('/lead-gen/lists/')({
107
+ component: ListsRoute
108
+ })
109
+
110
+ function ListsRoute() {
111
+ return (
112
+ <Stack gap={0}>
113
+ <ProjectAnnouncementBanner context="lead-gen-lists" />
114
+ <LeadGenListsPage />
115
+ </Stack>
116
+ )
117
+ }
118
+ ```
119
+
120
+ For list detail routes, pass the route param into `LeadGenListDetailPage`:
121
+
122
+ ```tsx
123
+ import { createFileRoute } from '@tanstack/react-router'
124
+ import { LeadGenListDetailPage } from '@elevasis/ui/features/lead-gen'
125
+
126
+ export const Route = createFileRoute('/lead-gen/lists/$listId')({
127
+ component: ListDetailRoute
128
+ })
129
+
130
+ function ListDetailRoute() {
131
+ const { listId } = Route.useParams()
132
+ return <LeadGenListDetailPage listId={listId} />
133
+ }
134
+ ```
135
+
136
+ Use the same wrapping pattern for `LeadGenOverviewPage`, `LeadGenCompaniesPage`, and `LeadGenContactsPage`.
137
+
138
+ ## 3. Build a Custom Campaign Workspace
139
+
140
+ When the project needs custom layout or vertical-specific rendering, use the hooks directly:
141
+
142
+ ```tsx
143
+ import { Badge, Button, Card, Group, Stack, Text } from '@mantine/core'
144
+ import { useArtifacts, useList, useListMembers, useTouchpoints, useTransitionListMember } from '@elevasis/ui/hooks'
145
+
146
+ export function CampaignWorkspace({ listId }: { listId: string }) {
147
+ const listQuery = useList(listId)
148
+ const membersQuery = useListMembers({ listId })
149
+ const artifactsQuery = useArtifacts({ ownerKind: 'list', ownerId: listId })
150
+ const touchpointsQuery = useTouchpoints({ listId })
151
+ const transitionMember = useTransitionListMember()
152
+
153
+ const list = listQuery.data
154
+ if (!list) return null
155
+
156
+ const firstMember = membersQuery.data?.members[0]
157
+
158
+ return (
159
+ <Stack>
160
+ <Group justify="space-between">
161
+ <div>
162
+ <Text fw={700}>{list.name}</Text>
163
+ <Text size="sm" c="dimmed">{list.description}</Text>
164
+ </div>
165
+ <Badge>{list.stateKey}</Badge>
166
+ </Group>
167
+
168
+ <Card withBorder>
169
+ <Text size="sm">Artifacts: {artifactsQuery.data?.artifacts.length ?? 0}</Text>
170
+ <Text size="sm">Touchpoints: {touchpointsQuery.data?.touchpoints.length ?? 0}</Text>
171
+ </Card>
172
+
173
+ {firstMember ? (
174
+ <Button
175
+ onClick={() =>
176
+ transitionMember.mutate({
177
+ memberId: firstMember.id,
178
+ listId,
179
+ pipelineKey: 'lead-gen',
180
+ stageKey: 'prospecting',
181
+ stateKey: 'verified'
182
+ })
183
+ }
184
+ >
185
+ Mark First Member Verified
186
+ </Button>
187
+ ) : null}
188
+ </Stack>
189
+ )
190
+ }
191
+ ```
192
+
193
+ Use `useArtifacts({ ownerKind, ownerId })` for durable JSON artifacts like audits, research summaries, snapshots, exports, or model outputs. Use `useTouchpoints({ listId })`, `useTouchpoints({ listMemberId })`, or `useTouchpoints({ contactId })` for timeline-style campaign interactions.
194
+
195
+ ## 4. Read and Mutate Lead-Gen Data in Workflows
196
+
197
+ Inside deployed workflows, use worker adapters instead of browser hooks or direct database access:
198
+
199
+ External projects should define workflow input/output schemas in `@shared/types`; the example below assumes those shared schemas already exist.
200
+
201
+ ```ts
202
+ // operations/src/sales/qualify-list.ts
203
+ import type { WorkflowDefinition } from '@elevasis/sdk'
204
+ import { acqDb, list } from '@elevasis/sdk/worker'
205
+ import { qualifyListInputSchema, qualifyListOutputSchema } from '@shared/types'
206
+
207
+ export const qualifyListWorkflow: WorkflowDefinition = {
208
+ config: {
209
+ resourceId: 'qualify-list',
210
+ name: 'Qualify List',
211
+ type: 'workflow',
212
+ version: '1.0.0',
213
+ status: 'dev',
214
+ links: [{ nodeId: 'feature:sales.lead-gen', kind: 'operates-on' }]
215
+ },
216
+ contract: { inputSchema: qualifyListInputSchema, outputSchema: qualifyListOutputSchema },
217
+ steps: {
218
+ qualify: {
219
+ id: 'qualify',
220
+ name: 'Qualify',
221
+ inputSchema: qualifyListInputSchema,
222
+ outputSchema: qualifyListOutputSchema,
223
+ next: null,
224
+ handler: async (input, context) => {
225
+ const config = await list.getConfig({ listId: input.listId })
226
+ const contacts = await acqDb.listContacts({ listId: input.listId, limit: 100, offset: 0 })
227
+
228
+ for (const contact of contacts.data) {
229
+ await list.updateContactStage({
230
+ listId: input.listId,
231
+ contactId: contact.id,
232
+ stage: 'verified',
233
+ executionId: context.executionId
234
+ })
235
+ }
236
+
237
+ await list.recordExecution({
238
+ listId: input.listId,
239
+ executionId: context.executionId,
240
+ configSnapshot: config
241
+ })
242
+
243
+ return { touched: contacts.data.length }
244
+ }
245
+ }
246
+ },
247
+ entryPoint: 'qualify'
248
+ }
249
+ ```
250
+
251
+ Use `list` for focused list-scoped workflow operations: fetch list config, record a list execution, and update company/contact list stages. Use `acqDb` when the workflow needs broader acquisition operations: create/update lists, companies, contacts, add contacts to lists, bulk import, enrichment, social posts, or CRM deal sync methods.
252
+
253
+ Keep these boundaries straight:
254
+
255
+ - Browser/UI code uses `@elevasis/ui/hooks`.
256
+ - Workflow code uses `@elevasis/sdk/worker` adapters.
257
+ - `organizationId` is injected by the platform dispatcher. Do not pass it from workflow code.
258
+ - Persisted API shapes are owned by core/API contracts, not route-local TypeScript.
259
+
260
+ ## 5. Customize Lead-Gen Semantics
261
+
262
+ The lead-gen shell feature key is `lead-gen`, the Organization OS feature id is `sales.lead-gen`, and the capability currently used by the shared manifest is `leadgen.lists.manage`.
263
+
264
+ For feature toggles, labels, or lifecycle semantics, start from the project reference file:
265
+
266
+ `core/config/organization-model.examples.ts`
267
+
268
+ Then update the project model:
269
+
270
+ `core/config/organization-model.ts`
271
+
272
+ The platform-side lead-gen stateful vocabulary is defined for:
273
+
274
+ - `acq.list`
275
+ - `acq.list-member`
276
+ - `acq.list-company`
277
+
278
+ The generated contracts expose `StatefulPipelineDefinition` and `LEAD_GEN_PIPELINE_DEFINITIONS` so downstream agents can inspect the current pipeline/stage/state vocabulary before adding UI labels or workflow transitions.
279
+
280
+ Changing persisted platform stages, adding new owner kinds, or extending artifact/touchpoint schema requires coordinated core/API/UI updates. Relabeling, route wrapping, sidebar changes, and project-owned rendering usually stay in the downstream project.
281
+
282
+ ## Verify
283
+
284
+ Run the checks for the surfaces you touched:
285
+
286
+ ```bash
287
+ pnpm -C ui run check
288
+ pnpm -C operations run check
289
+ pnpm -C operations exec elevasis-sdk check
290
+ ```
291
+
292
+ If you changed platform-level lead-gen contracts in the monorepo, the platform maintainer must also regenerate and verify scaffold output:
293
+
294
+ ```bash
295
+ pnpm scaffold:sync
296
+ pnpm scaffold:verify
297
+ ```
@@ -22,14 +22,23 @@ You want a new shell feature (e.g., `analytics`) with its own nav entry, sidebar
22
22
  **[Add a Resource](add-a-resource.md)**
23
23
  You want a new workflow or agent deployed to the platform. Covers `WorkflowDefinition` authoring, `DeploymentSpec` registration, relationship declarations, domain mapping, and CLI verification.
24
24
 
25
- **[Gate by Feature or Admin](gate-by-feature-or-admin.md)**
26
- You want to restrict a route, nav item, or UI element by feature flag or admin role. Covers the decision table, `FeatureGuard`, `AdminGuard`, nav-entry visibility fields, per-member `MembershipFeatureConfig` overrides, and the settings-asymmetry gotcha.
27
-
28
- ---
29
-
30
- ## Reference docs these recipes link into
31
-
32
- - [glossary.md](../reference/glossary.md) -- term disambiguation for Feature, Resource, featureId, Topology, Settings asymmetry
33
- - [contracts.md](../reference/contracts.md) -- TypeScript shapes: `FeatureModule`, `FeatureNavEntry`, `OrganizationModel`, `MembershipFeatureConfig`
34
- - [feature-flags-and-gating.md](../ui/feature-flags-and-gating.md) -- full three-concept gating model
35
- - [workflow-recipes.md](../operations/workflow-recipes.md) -- workflow anatomy, adapters, trigger patterns
25
+ **[Gate by Feature or Admin](gate-by-feature-or-admin.md)**
26
+ You want to restrict a route, nav item, or UI element by feature flag or admin role. Covers the decision table, `FeatureGuard`, `AdminGuard`, nav-entry visibility fields, per-member `MembershipFeatureConfig` overrides, and the settings-asymmetry gotcha.
27
+
28
+ **[Build and Extend CRM](extend-crm.md)**
29
+ You want to build on the shared CRM without forking it: add CRM routes, compose sidebars/pages, use deal/company/contact hooks, mutate CRM data from workflows, or understand which contracts and adapters form the extension surface.
30
+
31
+ **[Build and Extend Lead Gen](extend-lead-gen.md)**
32
+ You want to build on the shared lead-gen system without forking it: add lead-gen routes, compose sidebars/pages, use list/company/contact/artifact/touchpoint hooks, mutate list data from workflows, or understand which contracts and adapters form the extension surface.
33
+
34
+ **[Customize CRM Actions](customize-crm-actions.md)**
35
+ You want to add, hide, or replace CRM deal action buttons, configure the shared `crmActions` provider path, or call a project-owned workflow from custom UI when server-side action dispatch constraints require it. Covers `ActionDef`, `DEFAULT_CRM_ACTIONS`, provider wiring, and the current v1 boundary for custom action dispatch.
36
+
37
+ ---
38
+
39
+ ## Reference docs these recipes link into
40
+
41
+ - [glossary.md](../reference/glossary.md) -- term disambiguation for Feature, Resource, featureId, Topology, Settings asymmetry
42
+ - [contracts.md](../reference/contracts.md) -- TypeScript shapes: `FeatureModule`, `FeatureNavEntry`, `OrganizationModel`, `MembershipFeatureConfig`, CRM deal types, lead-gen list/member/artifact/touchpoint types, `CrmToolMap`, `LeadToolMap`, `ListToolMap`, `ActionDef`
43
+ - [feature-flags-and-gating.md](../ui/feature-flags-and-gating.md) -- full three-concept gating model
44
+ - [workflow-recipes.md](../operations/workflow-recipes.md) -- workflow anatomy, adapters, trigger patterns