@elevasis/sdk 1.20.2 → 1.22.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 (164) hide show
  1. package/dist/cli.cjs +4220 -1583
  2. package/dist/index.d.ts +1035 -481
  3. package/dist/index.js +7381 -4187
  4. package/dist/node/index.d.ts +1 -3
  5. package/dist/node/index.js +40 -49
  6. package/dist/test-utils/index.d.ts +699 -123
  7. package/dist/test-utils/index.js +3826 -630
  8. package/dist/worker/index.js +3616 -442
  9. package/package.json +3 -3
  10. package/reference/_navigation.md +9 -7
  11. package/reference/_reference-manifest.json +1 -1
  12. package/reference/claude-config/hooks/post-edit-validate.mjs +98 -98
  13. package/reference/claude-config/hooks/scaffold-registry-reminder.mjs +188 -188
  14. package/reference/claude-config/hooks/tool-failure-recovery.mjs +73 -73
  15. package/reference/claude-config/registries/graph-skills.json +4 -4
  16. package/reference/claude-config/registries/knowledge-flags.json +0 -2
  17. package/reference/claude-config/rules/active-change-index.md +80 -80
  18. package/reference/claude-config/rules/agent-start-here.md +277 -273
  19. package/reference/claude-config/rules/deployment.md +57 -57
  20. package/reference/claude-config/rules/error-handling.md +56 -56
  21. package/reference/claude-config/rules/execution.md +40 -40
  22. package/reference/claude-config/rules/frontend.md +4 -4
  23. package/reference/claude-config/rules/observability.md +31 -31
  24. package/reference/claude-config/rules/operations.md +29 -17
  25. package/reference/claude-config/rules/organization-model.md +108 -40
  26. package/reference/claude-config/rules/organization-os.md +115 -113
  27. package/reference/claude-config/rules/package-taxonomy.md +33 -33
  28. package/reference/claude-config/rules/platform.md +42 -42
  29. package/reference/claude-config/rules/shared-types.md +49 -46
  30. package/reference/claude-config/rules/task-tracking.md +47 -47
  31. package/reference/claude-config/rules/ui.md +200 -200
  32. package/reference/claude-config/rules/vibe.md +235 -231
  33. package/reference/claude-config/scripts/statusline-command.js +18 -18
  34. package/reference/claude-config/settings.json +34 -34
  35. package/reference/claude-config/skills/deploy/{SKILL.md → skill.md} +156 -156
  36. package/reference/claude-config/skills/dsp/SKILL.md +66 -66
  37. package/reference/claude-config/skills/elevasis/SKILL.md +235 -235
  38. package/reference/claude-config/skills/explore/SKILL.md +6 -6
  39. package/reference/claude-config/skills/git-sync/SKILL.md +126 -126
  40. package/reference/claude-config/skills/knowledge/SKILL.md +330 -271
  41. package/reference/claude-config/skills/knowledge/operations/codify-level-a.md +100 -100
  42. package/reference/claude-config/skills/knowledge/operations/codify-level-b.md +159 -158
  43. package/reference/claude-config/skills/knowledge/operations/customers.md +109 -109
  44. package/reference/claude-config/skills/knowledge/operations/features.md +76 -113
  45. package/reference/claude-config/skills/knowledge/operations/goals.md +118 -118
  46. package/reference/claude-config/skills/knowledge/operations/identity.md +93 -93
  47. package/reference/claude-config/skills/knowledge/operations/labels.md +94 -89
  48. package/reference/claude-config/skills/knowledge/operations/offerings.md +109 -109
  49. package/reference/claude-config/skills/knowledge/operations/roles.md +99 -99
  50. package/reference/claude-config/skills/knowledge/operations/techStack.md +30 -30
  51. package/reference/claude-config/skills/project/SKILL.md +1088 -1088
  52. package/reference/claude-config/skills/run-ui/SKILL.md +73 -73
  53. package/reference/claude-config/skills/save/SKILL.md +3 -3
  54. package/reference/claude-config/skills/setup/SKILL.md +275 -275
  55. package/reference/claude-config/skills/status/SKILL.md +59 -59
  56. package/reference/claude-config/skills/submit-request/SKILL.md +180 -180
  57. package/reference/claude-config/skills/sync/SKILL.md +47 -47
  58. package/reference/claude-config/skills/tutorial/SKILL.md +259 -259
  59. package/reference/claude-config/skills/tutorial/progress-template.md +74 -74
  60. package/reference/claude-config/skills/tutorial/technical.md +1303 -1306
  61. package/reference/claude-config/skills/tutorial/vibe-coder.md +890 -890
  62. package/reference/claude-config/sync-notes/2026-04-22-git-sync-and-sync-notes.md +27 -27
  63. package/reference/claude-config/sync-notes/2026-04-22-lead-gen-deliverability-removal.md +30 -30
  64. package/reference/claude-config/sync-notes/2026-04-24-test-utils-and-template-tests.md +73 -73
  65. package/reference/claude-config/sync-notes/2026-04-24-ui-consolidation-and-sdk-cli-train.md +86 -86
  66. package/reference/claude-config/sync-notes/2026-04-25-auth-role-system-and-settings-roles.md +55 -55
  67. package/reference/claude-config/sync-notes/2026-04-27-crm-hitl-action-layer-cutover.md +97 -97
  68. package/reference/claude-config/sync-notes/2026-04-27-lead-gen-substrate-train.md +112 -112
  69. package/reference/claude-config/sync-notes/2026-04-29-crm-state-and-lead-gen-processing-status.md +93 -93
  70. package/reference/claude-config/sync-notes/2026-05-02-crm-ownership-next-action.md +58 -58
  71. package/reference/claude-config/sync-notes/2026-05-02-template-hardcode-workos-config.md +56 -56
  72. package/reference/claude-config/sync-notes/2026-05-04-elevasis-workspace.md +71 -71
  73. package/reference/claude-config/sync-notes/2026-05-04-knowledge-bundle.md +83 -83
  74. package/reference/claude-config/sync-notes/2026-05-04-template-skills-run-ui-and-tutorial.md +59 -59
  75. package/reference/claude-config/sync-notes/2026-05-05-list-builder.md +42 -42
  76. package/reference/claude-config/sync-notes/2026-05-06-crm-spine.md +60 -60
  77. package/reference/claude-config/sync-notes/2026-05-06-sdk-changes-release-train.md +37 -37
  78. package/reference/claude-config/sync-notes/2026-05-07-sdk-changes-release-train.md +34 -34
  79. package/reference/claude-config/sync-notes/2026-05-08-resource-governance-scaffold-guidance.md +38 -38
  80. package/reference/claude-config/sync-notes/2026-05-09-clients-domain.md +32 -32
  81. package/reference/claude-config/sync-notes/2026-05-09-command-system.md +33 -33
  82. package/reference/claude-config/sync-notes/2026-05-09-resource-governance-and-misc.md +69 -69
  83. package/reference/claude-config/sync-notes/2026-05-12-sdk-ready-release-train.md +30 -0
  84. package/reference/claude-config/sync-notes/2026-05-14-organization-model-ontology-refactor.md +42 -0
  85. package/reference/claude-config/sync-notes/README.md +43 -43
  86. package/reference/cli.mdx +808 -668
  87. package/reference/concepts.mdx +146 -146
  88. package/reference/deployment/api.mdx +297 -297
  89. package/reference/deployment/command-center.mdx +209 -209
  90. package/reference/deployment/index.mdx +195 -195
  91. package/reference/deployment/provided-features.mdx +107 -93
  92. package/reference/deployment/ui-execution.mdx +250 -250
  93. package/reference/examples/organization-model.ts +147 -84
  94. package/reference/framework/agent.mdx +156 -156
  95. package/reference/framework/index.mdx +195 -195
  96. package/reference/framework/interaction-guidance.mdx +182 -182
  97. package/reference/framework/memory.mdx +326 -326
  98. package/reference/framework/project-structure.mdx +282 -282
  99. package/reference/framework/tutorial-system.mdx +135 -135
  100. package/reference/getting-started.mdx +142 -142
  101. package/reference/index.mdx +106 -106
  102. package/reference/packages/core/src/README.md +14 -14
  103. package/reference/packages/core/src/business/README.md +2 -2
  104. package/reference/packages/core/src/knowledge/README.md +33 -32
  105. package/reference/packages/core/src/organization-model/README.md +149 -109
  106. package/reference/packages/core/src/test-utils/README.md +37 -37
  107. package/reference/packages/ui/src/api/README.md +18 -18
  108. package/reference/packages/ui/src/app/README.md +24 -24
  109. package/reference/packages/ui/src/auth/README.md +18 -18
  110. package/reference/packages/ui/src/components/README.md +24 -24
  111. package/reference/packages/ui/src/execution/README.md +16 -16
  112. package/reference/packages/ui/src/features/README.md +28 -28
  113. package/reference/packages/ui/src/graph/README.md +16 -16
  114. package/reference/packages/ui/src/hooks/README.md +23 -23
  115. package/reference/packages/ui/src/initialization/README.md +19 -19
  116. package/reference/packages/ui/src/knowledge/README.md +31 -31
  117. package/reference/packages/ui/src/organization/README.md +18 -18
  118. package/reference/packages/ui/src/profile/README.md +19 -19
  119. package/reference/packages/ui/src/provider/README.md +32 -32
  120. package/reference/packages/ui/src/router/README.md +18 -18
  121. package/reference/packages/ui/src/sse/README.md +13 -13
  122. package/reference/packages/ui/src/test-utils/README.md +7 -7
  123. package/reference/packages/ui/src/theme/README.md +23 -23
  124. package/reference/packages/ui/src/theme/presets/README.md +19 -19
  125. package/reference/packages/ui/src/types/README.md +16 -16
  126. package/reference/packages/ui/src/utils/README.md +18 -18
  127. package/reference/packages/ui/src/zustand/README.md +18 -18
  128. package/reference/platform-tools/adapters-integration.mdx +301 -301
  129. package/reference/platform-tools/adapters-platform.mdx +553 -553
  130. package/reference/platform-tools/index.mdx +217 -217
  131. package/reference/platform-tools/type-safety.mdx +82 -82
  132. package/reference/resources/index.mdx +349 -349
  133. package/reference/resources/patterns.mdx +449 -449
  134. package/reference/resources/types.mdx +116 -116
  135. package/reference/roadmap.mdx +165 -165
  136. package/reference/runtime.mdx +173 -173
  137. package/reference/scaffold/core/organization-graph.mdx +110 -89
  138. package/reference/scaffold/core/organization-model.mdx +226 -171
  139. package/reference/scaffold/index.mdx +67 -67
  140. package/reference/scaffold/operations/propagation-pipeline.md +77 -77
  141. package/reference/scaffold/operations/scaffold-maintenance.md +10 -10
  142. package/reference/scaffold/operations/workflow-recipes.md +138 -138
  143. package/reference/scaffold/recipes/add-a-feature.md +310 -88
  144. package/reference/scaffold/recipes/add-a-resource.md +137 -117
  145. package/reference/scaffold/recipes/customize-crm-actions.md +439 -439
  146. package/reference/scaffold/recipes/customize-knowledge-browser.md +384 -0
  147. package/reference/scaffold/recipes/customize-organization-model.md +281 -118
  148. package/reference/scaffold/recipes/extend-a-base-entity.md +8 -8
  149. package/reference/scaffold/recipes/extend-crm.md +40 -39
  150. package/reference/scaffold/recipes/extend-lead-gen.md +400 -401
  151. package/reference/scaffold/recipes/gate-by-feature-or-admin.md +118 -114
  152. package/reference/scaffold/recipes/index.md +47 -46
  153. package/reference/scaffold/recipes/query-the-knowledge-graph.md +227 -0
  154. package/reference/scaffold/reference/contracts.md +2389 -2121
  155. package/reference/scaffold/reference/feature-registry.md +9 -20
  156. package/reference/scaffold/reference/glossary.md +76 -76
  157. package/reference/scaffold/ui/composition-extensibility.mdx +233 -233
  158. package/reference/scaffold/ui/customization.md +243 -243
  159. package/reference/scaffold/ui/feature-flags-and-gating.md +46 -46
  160. package/reference/scaffold/ui/feature-shell.mdx +72 -72
  161. package/reference/scaffold/ui/recipes.md +221 -213
  162. package/reference/spine/spine-primer.md +96 -96
  163. package/reference/templates/index.mdx +47 -47
  164. package/reference/troubleshooting.mdx +223 -223
@@ -1,404 +1,403 @@
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, workflow adapters, contracts, and org-model extension boundaries.
4
- ---
5
1
  <!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
6
2
  <!-- Regenerate: pnpm scaffold:sync -->
7
3
 
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 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, list members, transitions, and derived actions live under `@elevasis/ui/hooks`.
27
- - **Acquisition substrate:** `acq_lists`, `acq_companies`, `acq_contacts`, `acq_artifacts`, 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, pipeline semantics, or resource descriptors | `core/config/organization-model.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`, `ListBuilderPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | Keep route files thin and put project-specific behavior in local feature modules. |
38
- | Build a custom campaign/list workspace | `ListBuilderPage`, `useLists`, `useList`, `useListProgress`, `useListExecutions`, `useWorkflowExecution`, `useExecutionSSE` from `@elevasis/ui` | Use the shared builder when possible; otherwise compose hooks for platform data and workflow execution. |
39
- | Render artifacts or list-member detail | `LeadGenListDetailPage`, `useArtifacts`, `useCreateArtifact`, `useListMember` | Artifacts are a substrate primitive. 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`, `ListBuilderPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | `@elevasis/ui/features/lead-gen` | Shared lead-gen pages you can route to or wrap |
49
- | `ListActionsProvider`, `useListActions`, `ListBuilderWorkflow`, `ListBuilderRegistry`, `LeadGenCapabilityKey` | `@elevasis/ui/features/lead-gen` | List Builder workflow registry, slot-based field contracts, and project-owned action wiring |
50
- | `LeadGenRouteShell`, `LIST_TEMPLATE_OPTIONS`, `buildListConfig` | `@elevasis/ui/features/lead-gen` | Route shell helpers and list creation config helpers (contact/company detail surfaces are now `ContactDetailPage` / `CompanyDetailPage` from `@elevasis/ui/features/crm`) |
51
- | `useLists`, `useList`, `useListsTelemetry`, `useListProgress`, `useListExecutions`, `useCreateList`, `useUpdateList`, `useUpdateListConfig`, `useDeleteList` | `@elevasis/ui/hooks` | Headless list and telemetry data access |
52
- | `useWorkflowExecution`, `useExecutionSSE`, `useAddCompaniesToList`, `useRemoveCompaniesFromList`, `useAddContactsToList` | `@elevasis/ui/hooks` | List Builder workflow triggering, live execution tailing, and list membership mutations |
53
- | `useCompanies`, `useCompany`, `useContacts`, `useContact` | `@elevasis/ui/hooks` | Acquisition company/contact data access |
54
- | `useArtifacts`, `useCreateArtifact`, `useListMembers`, `useListMember` | `@elevasis/ui/hooks` | Lead-gen substrate data access |
55
- | `useTransitionList`, `useTransitionListMember`, `useTransitionListCompany`, `useDeriveActions` | `@elevasis/ui/hooks` | Stateful transition mutations and contextual action derivation |
56
- | `ElevasisUIProvider`, `ElevasisCoreProvider`, `useElevasisServices` | `@elevasis/ui/provider` | Provider setup, API access, organization context, and `listActions` registry injection |
57
- | `acqDb`, `list` | `@elevasis/sdk/worker` | Workflow-side acquisition and list-scoped platform adapters |
58
-
59
- Read the generated contracts before changing typed boundaries:
60
-
61
- `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
62
-
63
- Look for the **Lead Gen Platform Primitives** section. It includes list shapes, company/contact shapes, list config, telemetry, artifacts, list members, stateful pipeline definitions, and workflow adapter method maps.
64
-
65
- ## 1. Extend Lead-Gen Navigation or Sidebar
66
-
67
- For a simple nav addition, extend `LEAD_GEN_ITEMS` and override the lead-gen feature manifest:
68
-
69
- ```tsx
70
- // ui/src/routes/__root.tsx
71
- import { leadGenManifest, LEAD_GEN_ITEMS, LeadGenSidebar, LeadGenSidebarMiddle } from '@elevasis/ui/features/lead-gen'
72
- import type { FeatureModule } from '@elevasis/ui/provider'
73
- import type { NavItem } from '@elevasis/ui/layout'
74
- import { IconChartBar } from '@tabler/icons-react'
75
-
76
- const customLeadGenItems: NavItem[] = [
77
- ...LEAD_GEN_ITEMS,
78
- { label: 'Reports', to: '/lead-gen/reports', icon: IconChartBar, exact: false }
79
- ]
80
-
81
- const CustomLeadGenSidebar = () => (
82
- <LeadGenSidebar>
83
- <LeadGenSidebarMiddle items={customLeadGenItems} />
84
- </LeadGenSidebar>
85
- )
86
-
87
- export const customLeadGenManifest: FeatureModule = {
88
- ...leadGenManifest,
89
- sidebar: CustomLeadGenSidebar
90
- }
91
- ```
92
-
93
- Then replace `leadGenManifest` with `customLeadGenManifest` in the local `FEATURE_MANIFESTS` array and add the matching route under `ui/src/routes/lead-gen/`.
94
-
95
- For structural changes, compose `LeadGenSidebarTop`, `SubshellNavList`, and `SubshellSidebarSection`. The full sidebar decision tree lives in `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
96
-
97
- ## 2. Wrap Shared Lead-Gen Pages
98
-
99
- If the shared page is close, keep it and wrap it:
100
-
101
- ```tsx
102
- // ui/src/routes/lead-gen/lists.index.tsx
103
- import { createFileRoute } from '@tanstack/react-router'
104
- import { LeadGenListsPage } from '@elevasis/ui/features/lead-gen'
105
- import { Stack } from '@mantine/core'
106
- import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
107
-
108
- export const Route = createFileRoute('/lead-gen/lists/')({
109
- component: ListsRoute
110
- })
111
-
112
- function ListsRoute() {
113
- return (
114
- <Stack gap={0}>
115
- <ProjectAnnouncementBanner context="lead-gen-lists" />
116
- <LeadGenListsPage />
117
- </Stack>
118
- )
119
- }
120
- ```
121
-
122
- For list detail routes, pass the route param into `LeadGenListDetailPage`:
123
-
124
- ```tsx
125
- import { createFileRoute } from '@tanstack/react-router'
126
- import { LeadGenListDetailPage } from '@elevasis/ui/features/lead-gen'
127
-
128
- export const Route = createFileRoute('/lead-gen/lists/$listId')({
129
- component: ListDetailRoute
130
- })
131
-
132
- function ListDetailRoute() {
133
- const { listId } = Route.useParams()
134
- return <LeadGenListDetailPage listId={listId} />
135
- }
136
- ```
137
-
138
- Use the same wrapping pattern for `LeadGenOverviewPage`, `LeadGenCompaniesPage`, and `LeadGenContactsPage`.
139
-
140
- For the real-time List Builder workspace, add a thin route that passes the route param into `ListBuilderPage`:
141
-
142
- ```tsx
143
- import { createFileRoute } from '@tanstack/react-router'
144
- import { ListBuilderPage } from '@elevasis/ui/features/lead-gen'
145
-
146
- export const Route = createFileRoute('/lead-gen/list-builder/$listId')({
147
- component: ListBuilderRoute
148
- })
149
-
150
- function ListBuilderRoute() {
151
- const { listId } = Route.useParams()
152
- return <ListBuilderPage listId={listId} />
153
- }
154
- ```
155
-
156
- Register project workflow actions at the provider boundary. The shared UI owns the contract; each project owns workflow ids, capability keys, default inputs, and field components.
157
-
158
- Each registry entry declares a Zod `schema` and a `layout` of declarative field hints (`StepConfigLayout<Input>`). The shared `StepConfigForm` renders the layout, validates against the schema, and wires `value`/`onChange` for you — no per-action React components. The List Builder right column renders the form as `Configuration | Advanced | Runs` tabs with a sticky action footer; `RunWorkflowModal` renders the same form inline. Omit the `advanced:` section when a step has none.
159
-
160
- Available field component variants: `textinput`, `textarea`, `numberinput`, `switch`, `segmented`, `select`, `multiselect`, `tags`, `json`. Field hints support `label`, `description`, `placeholder`, `min`/`max`/`step` (numbers), `options` (selects), and `when: (values) => boolean` for conditional visibility.
161
-
162
- ```tsx
163
- import * as z from 'zod'
164
- import { type ListBuilderRegistry } from '@elevasis/ui/features/lead-gen'
165
- import type { StepConfigLayout } from '@elevasis/ui/components/forms'
166
- import { ElevasisUIProvider } from '@elevasis/ui/provider'
167
- import { resourceDescriptors } from '@core/config/organization-model'
168
-
169
- const companyCleanupInputSchema = z.object({
170
- listId: z.string().uuid(),
171
- targetDescription: z.string(),
172
- dryRun: z.boolean().default(true),
173
- batchSize: z.number().int().min(1).default(20),
174
- mode: z.enum(['mock', 'live']).default('live')
175
- })
176
-
177
- type CompanyCleanupInput = z.infer<typeof companyCleanupInputSchema>
178
- const companyCleanupResource = resourceDescriptors.companyCleanup
179
-
180
- const companyCleanupLayout: StepConfigLayout<CompanyCleanupInput> = {
181
- sections: [
182
- {
183
- id: 'configuration',
184
- fields: [
185
- {
186
- path: 'targetDescription',
187
- component: 'textarea',
188
- label: 'Target description',
189
- placeholder: 'independent veterinary clinics in Orange County'
190
- },
191
- { path: 'dryRun', component: 'switch', label: 'Dry run' }
192
- ]
193
- }
194
- ],
195
- advanced: {
196
- id: 'advanced',
197
- fields: [
198
- { path: 'batchSize', component: 'numberinput', label: 'Batch size', min: 1 },
199
- {
200
- path: 'mode',
201
- component: 'segmented',
202
- options: [
203
- { value: 'mock', label: 'Mock' },
204
- { value: 'live', label: 'Live' }
205
- ],
206
- when: () => import.meta.env.DEV
207
- }
208
- ]
209
- }
210
- }
211
-
212
- const listActions: ListBuilderRegistry = [
213
- {
214
- resourceId: companyCleanupResource.id,
215
- workflowId: companyCleanupResource.id,
216
- capabilityKey: 'lead-gen.company.cleanup',
217
- label: 'Company Cleanup',
218
- description: 'Normalize and clean company records for the list.',
219
- category: 'utility',
220
- stagesAffected: ['populated'],
221
- schema: companyCleanupInputSchema,
222
- layout: companyCleanupLayout,
223
- defaultInput: (list) => ({
224
- listId: list.id,
225
- targetDescription: '',
226
- dryRun: true,
227
- batchSize: 20
228
- })
229
- }
230
- ]
231
-
232
- export function App() {
233
- return (
234
- <ElevasisUIProvider listActions={listActions} {...providerProps}>
235
- {children}
236
- </ElevasisUIProvider>
237
- )
238
- }
239
- ```
240
-
241
- Author the `schema` in a browser-safe sibling file (no Node-only imports) so it can be reused by the workflow runtime AND the UI form. See `apollo-import-schema.ts` in the platform for the canonical split pattern.
242
-
243
- Per-section split rule (operator-grade vs power-user):
244
-
245
- - **Configuration:** target/limit, dry-run toggle, mode (preview/finalize), and any field an operator changes between runs.
246
- - **Advanced:** concurrency, batch size, timeouts, credential names, workflow chaining, dev-only mode toggles. Omit the `advanced:` key entirely when a step has none.
247
- - **`defaultInput`:** seeds the form before the user touches anything; must include `listId: list.id`.
248
-
249
- ## 3. Build a Custom Campaign Workspace
250
-
251
- When the project needs custom layout or vertical-specific rendering, use the hooks directly:
252
-
253
- ```tsx
254
- import { Badge, Button, Card, Group, Stack, Text } from '@mantine/core'
255
- import { useArtifacts, useList, useListMembers, useTransitionListMember } from '@elevasis/ui/hooks'
256
-
257
- export function CampaignWorkspace({ listId }: { listId: string }) {
258
- const listQuery = useList(listId)
259
- const membersQuery = useListMembers({ listId })
260
- const artifactsQuery = useArtifacts({ ownerKind: 'list', ownerId: listId })
261
- const transitionMember = useTransitionListMember()
262
-
263
- const list = listQuery.data
264
- if (!list) return null
265
-
266
- const firstMember = membersQuery.data?.members[0]
267
-
268
- return (
269
- <Stack>
270
- <Group justify="space-between">
271
- <div>
272
- <Text fw={700}>{list.name}</Text>
273
- <Text size="sm" c="dimmed">{list.description}</Text>
274
- </div>
275
- <Badge>{list.stateKey}</Badge>
276
- </Group>
277
-
278
- <Card withBorder>
279
- <Text size="sm">Artifacts: {artifactsQuery.data?.artifacts.length ?? 0}</Text>
280
- </Card>
281
-
282
- {firstMember ? (
283
- <Button
284
- onClick={() =>
285
- transitionMember.mutate({
286
- memberId: firstMember.id,
287
- listId,
288
- pipelineKey: 'lead-gen',
289
- stageKey: 'prospecting',
290
- stateKey: 'verified'
291
- })
292
- }
293
- >
294
- Mark First Member Verified
295
- </Button>
296
- ) : null}
297
- </Stack>
298
- )
299
- }
300
- ```
301
-
302
- Use `useArtifacts({ ownerKind, ownerId })` for durable JSON artifacts like audits, research summaries, snapshots, exports, or model outputs.
303
-
304
- ## 4. Read and Mutate Lead-Gen Data in Workflows
305
-
306
- Inside deployed workflows, use worker adapters instead of browser hooks or direct database access:
307
-
308
- External projects should define workflow input/output schemas in `@shared/types`; the example below assumes those shared schemas already exist.
309
-
310
- ```ts
311
- // operations/src/sales/qualify-list.ts
312
- import type { WorkflowDefinition } from '@elevasis/sdk'
313
- import { acqDb, list } from '@elevasis/sdk/worker'
314
- import { qualifyListInputSchema, qualifyListOutputSchema } from '@shared/types'
315
- import { resourceDescriptors } from '@core/config/organization-model'
316
-
317
- export const qualifyListWorkflow: WorkflowDefinition = {
318
- config: {
319
- resource: resourceDescriptors.qualifyList,
320
- resourceId: resourceDescriptors.qualifyList.id,
321
- name: 'Qualify List',
322
- type: resourceDescriptors.qualifyList.kind,
323
- version: '1.0.0',
324
- status: 'dev',
325
- links: [{ nodeId: 'feature:sales.lead-gen', kind: 'operates-on' }]
326
- },
327
- contract: { inputSchema: qualifyListInputSchema, outputSchema: qualifyListOutputSchema },
328
- steps: {
329
- qualify: {
330
- id: 'qualify',
331
- name: 'Qualify',
332
- inputSchema: qualifyListInputSchema,
333
- outputSchema: qualifyListOutputSchema,
334
- next: null,
335
- handler: async (input, context) => {
336
- const config = await list.getConfig({ listId: input.listId })
337
- const contacts = await acqDb.listContacts({ listId: input.listId, limit: 100, offset: 0 })
338
-
339
- for (const contact of contacts.data) {
340
- await list.updateContactStage({
341
- listId: input.listId,
342
- contactId: contact.id,
343
- stage: 'verified',
344
- executionId: context.executionId
345
- })
346
- }
347
-
348
- await list.recordExecution({
349
- listId: input.listId,
350
- executionId: context.executionId,
351
- configSnapshot: config
352
- })
353
-
354
- return { touched: contacts.data.length }
355
- }
356
- }
357
- },
358
- entryPoint: 'qualify'
359
- }
360
- ```
361
-
362
- 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.
363
-
364
- Keep these boundaries straight:
365
-
366
- - Browser/UI code uses `@elevasis/ui/hooks`.
367
- - Workflow code uses `@elevasis/sdk/worker` adapters.
368
- - `organizationId` is injected by the platform dispatcher. Do not pass it from workflow code.
369
- - Persisted API shapes are owned by core/API contracts, not route-local TypeScript.
370
-
371
- ## 5. Customize Lead-Gen Semantics
372
-
373
- 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`.
374
-
375
- For feature toggles, labels, lifecycle semantics, or resource descriptors, update the project model:
376
-
377
- `core/config/organization-model.ts`
378
-
379
- The platform-side lead-gen stateful vocabulary is defined for:
380
-
381
- - `acq.list`
382
- - `acq.list-member`
383
- - `acq.list-company`
384
-
385
- 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.
386
-
387
- Changing persisted platform stages, adding new owner kinds, or extending artifact schema requires coordinated core/API/UI updates. Relabeling, route wrapping, sidebar changes, and project-owned rendering usually stay in the downstream project.
388
-
389
- ## Verify
390
-
391
- Run the checks for the surfaces you touched:
392
-
393
- ```bash
394
- pnpm -C ui run check
395
- pnpm -C operations run check
396
- pnpm -C operations exec elevasis-sdk check
397
- ```
398
-
399
- If you changed platform-level lead-gen contracts in the monorepo, the platform maintainer must also regenerate and verify scaffold output:
400
-
401
- ```bash
402
- pnpm scaffold:sync
403
- pnpm scaffold:verify
404
- ```
4
+ ---
5
+ title: Build and Extend Lead Gen
6
+ description: Map the lead-gen platform primitives available to SDK projects: shared UI pages, sidebar composition, data hooks, list/member state, artifacts, workflow adapters, contracts, and org-model extension boundaries.
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 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, System access, 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, list members, transitions, and derived actions live under `@elevasis/ui/hooks`.
27
+ - **Acquisition substrate:** `acq_lists`, `acq_companies`, `acq_contacts`, `acq_artifacts`, 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 System availability, labels, quick access, pipeline semantics, or resource descriptors | `core/config/organization-model.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`, `ListBuilderPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | Keep route files thin and put project-specific behavior in local feature modules. |
38
+ | Build a custom campaign/list workspace | `ListBuilderPage`, `useLists`, `useList`, `useListProgress`, `useListExecutions`, `useWorkflowExecution`, `useExecutionSSE` from `@elevasis/ui` | Use the shared builder when possible; otherwise compose hooks for platform data and workflow execution. |
39
+ | Render artifacts or list-member detail | `LeadGenListDetailPage`, `useArtifacts`, `useCreateArtifact`, `useListMember` | Artifacts are a substrate primitive. 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`, `ListBuilderPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | `@elevasis/ui/features/lead-gen` | Shared lead-gen pages you can route to or wrap |
49
+ | `ListActionsProvider`, `useListActions`, `ListBuilderWorkflow`, `ListBuilderRegistry`, `LeadGenActionKey` | `@elevasis/ui/features/lead-gen` | List Builder workflow registry, slot-based field contracts, and project-owned action wiring |
50
+ | `LeadGenRouteShell`, `LIST_TEMPLATE_OPTIONS`, `buildListConfig` | `@elevasis/ui/features/lead-gen` | Route shell helpers and list creation config helpers (contact/company detail surfaces are now `ContactDetailPage` / `CompanyDetailPage` from `@elevasis/ui/features/crm`) |
51
+ | `useLists`, `useList`, `useListsTelemetry`, `useListProgress`, `useListExecutions`, `useCreateList`, `useUpdateList`, `useUpdateListConfig`, `useDeleteList` | `@elevasis/ui/hooks` | Headless list and telemetry data access |
52
+ | `useWorkflowExecution`, `useExecutionSSE`, `useAddCompaniesToList`, `useRemoveCompaniesFromList`, `useAddContactsToList` | `@elevasis/ui/hooks` | List Builder workflow triggering, live execution tailing, and list membership mutations |
53
+ | `useCompanies`, `useCompany`, `useContacts`, `useContact` | `@elevasis/ui/hooks` | Acquisition company/contact data access |
54
+ | `useArtifacts`, `useCreateArtifact`, `useListMembers`, `useListMember` | `@elevasis/ui/hooks` | Lead-gen substrate data access |
55
+ | `useTransitionList`, `useTransitionListMember`, `useTransitionListCompany`, `useDeriveActions` | `@elevasis/ui/hooks` | Stateful transition mutations and contextual action derivation |
56
+ | `ElevasisUIProvider`, `ElevasisCoreProvider`, `useElevasisServices` | `@elevasis/ui/provider` | Provider setup, API access, organization context, and `listActions` registry injection |
57
+ | `acqDb`, `list` | `@elevasis/sdk/worker` | Workflow-side acquisition and list-scoped platform adapters |
58
+
59
+ Read the generated contracts before changing typed boundaries:
60
+
61
+ `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
62
+
63
+ Look for the **Lead Gen Platform Primitives** section. It includes list shapes, company/contact shapes, list config, telemetry, artifacts, list members, stateful pipeline definitions, and workflow adapter method maps.
64
+
65
+ ## 1. Extend Lead-Gen Navigation or Sidebar
66
+
67
+ For a simple nav addition, extend `LEAD_GEN_ITEMS` and override the lead-gen system manifest:
68
+
69
+ ```tsx
70
+ // ui/src/routes/__root.tsx
71
+ import { leadGenManifest, LEAD_GEN_ITEMS, LeadGenSidebar, LeadGenSidebarMiddle } from '@elevasis/ui/features/lead-gen'
72
+ import type { SystemModule } from '@elevasis/ui/provider'
73
+ import type { NavItem } from '@elevasis/ui/layout'
74
+ import { IconChartBar } from '@tabler/icons-react'
75
+
76
+ const customLeadGenItems: NavItem[] = [
77
+ ...LEAD_GEN_ITEMS,
78
+ { label: 'Reports', to: '/lead-gen/reports', icon: IconChartBar, exact: false }
79
+ ]
80
+
81
+ const CustomLeadGenSidebar = () => (
82
+ <LeadGenSidebar>
83
+ <LeadGenSidebarMiddle items={customLeadGenItems} />
84
+ </LeadGenSidebar>
85
+ )
86
+
87
+ export const customLeadGenManifest: SystemModule = {
88
+ ...leadGenManifest,
89
+ sidebar: CustomLeadGenSidebar
90
+ }
91
+ ```
92
+
93
+ Then replace `leadGenManifest` with `customLeadGenManifest` in the local `SYSTEM_MANIFESTS` array and add the matching route under `ui/src/routes/lead-gen/`.
94
+
95
+ For structural changes, compose `LeadGenSidebarTop`, `SubshellNavList`, and `SubshellSidebarSection`. The full sidebar decision tree lives in `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
96
+
97
+ ## 2. Wrap Shared Lead-Gen Pages
98
+
99
+ If the shared page is close, keep it and wrap it:
100
+
101
+ ```tsx
102
+ // ui/src/routes/lead-gen/lists.index.tsx
103
+ import { createFileRoute } from '@tanstack/react-router'
104
+ import { LeadGenListsPage } from '@elevasis/ui/features/lead-gen'
105
+ import { Stack } from '@mantine/core'
106
+ import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
107
+
108
+ export const Route = createFileRoute('/lead-gen/lists/')({
109
+ component: ListsRoute
110
+ })
111
+
112
+ function ListsRoute() {
113
+ return (
114
+ <Stack gap={0}>
115
+ <ProjectAnnouncementBanner context="lead-gen-lists" />
116
+ <LeadGenListsPage />
117
+ </Stack>
118
+ )
119
+ }
120
+ ```
121
+
122
+ For list detail routes, pass the route param into `LeadGenListDetailPage`:
123
+
124
+ ```tsx
125
+ import { createFileRoute } from '@tanstack/react-router'
126
+ import { LeadGenListDetailPage } from '@elevasis/ui/features/lead-gen'
127
+
128
+ export const Route = createFileRoute('/lead-gen/lists/$listId')({
129
+ component: ListDetailRoute
130
+ })
131
+
132
+ function ListDetailRoute() {
133
+ const { listId } = Route.useParams()
134
+ return <LeadGenListDetailPage listId={listId} />
135
+ }
136
+ ```
137
+
138
+ Use the same wrapping pattern for `LeadGenOverviewPage`, `LeadGenCompaniesPage`, and `LeadGenContactsPage`.
139
+
140
+ For the real-time List Builder workspace, add a thin route that passes the route param into `ListBuilderPage`:
141
+
142
+ ```tsx
143
+ import { createFileRoute } from '@tanstack/react-router'
144
+ import { ListBuilderPage } from '@elevasis/ui/features/lead-gen'
145
+
146
+ export const Route = createFileRoute('/lead-gen/list-builder/$listId')({
147
+ component: ListBuilderRoute
148
+ })
149
+
150
+ function ListBuilderRoute() {
151
+ const { listId } = Route.useParams()
152
+ return <ListBuilderPage listId={listId} />
153
+ }
154
+ ```
155
+
156
+ Register project workflow actions at the provider boundary. The shared UI owns the contract; each project owns workflow ids, action keys, default inputs, and field components.
157
+
158
+ Each registry entry declares a Zod `schema` and a `layout` of declarative field hints (`StepConfigLayout<Input>`). The shared `StepConfigForm` renders the layout, validates against the schema, and wires `value`/`onChange` for you — no per-action React components. The List Builder right column renders the form as `Configuration | Advanced | Runs` tabs with a sticky action footer; `RunWorkflowModal` renders the same form inline. Omit the `advanced:` section when a step has none.
159
+
160
+ Available field component variants: `textinput`, `textarea`, `numberinput`, `switch`, `segmented`, `select`, `multiselect`, `tags`, `json`. Field hints support `label`, `description`, `placeholder`, `min`/`max`/`step` (numbers), `options` (selects), and `when: (values) => boolean` for conditional visibility.
161
+
162
+ ```tsx
163
+ import * as z from 'zod'
164
+ import { type ListBuilderRegistry } from '@elevasis/ui/features/lead-gen'
165
+ import type { StepConfigLayout } from '@elevasis/ui/components/forms'
166
+ import { ElevasisUIProvider } from '@elevasis/ui/provider'
167
+ import { resourceDescriptors } from '@core/config/organization-model'
168
+
169
+ const companyCleanupInputSchema = z.object({
170
+ listId: z.string().uuid(),
171
+ targetDescription: z.string(),
172
+ dryRun: z.boolean().default(true),
173
+ batchSize: z.number().int().min(1).default(20),
174
+ mode: z.enum(['mock', 'live']).default('live')
175
+ })
176
+
177
+ type CompanyCleanupInput = z.infer<typeof companyCleanupInputSchema>
178
+ const companyCleanupResource = resourceDescriptors.companyCleanup
179
+
180
+ const companyCleanupLayout: StepConfigLayout<CompanyCleanupInput> = {
181
+ sections: [
182
+ {
183
+ id: 'configuration',
184
+ fields: [
185
+ {
186
+ path: 'targetDescription',
187
+ component: 'textarea',
188
+ label: 'Target description',
189
+ placeholder: 'independent veterinary clinics in Orange County'
190
+ },
191
+ { path: 'dryRun', component: 'switch', label: 'Dry run' }
192
+ ]
193
+ }
194
+ ],
195
+ advanced: {
196
+ id: 'advanced',
197
+ fields: [
198
+ { path: 'batchSize', component: 'numberinput', label: 'Batch size', min: 1 },
199
+ {
200
+ path: 'mode',
201
+ component: 'segmented',
202
+ options: [
203
+ { value: 'mock', label: 'Mock' },
204
+ { value: 'live', label: 'Live' }
205
+ ],
206
+ when: () => import.meta.env.DEV
207
+ }
208
+ ]
209
+ }
210
+ }
211
+
212
+ const listActions: ListBuilderRegistry = [
213
+ {
214
+ resourceId: companyCleanupResource.id,
215
+ workflowId: companyCleanupResource.id,
216
+ actionKey: 'lead-gen.company.cleanup',
217
+ label: 'Company Cleanup',
218
+ description: 'Normalize and clean company records for the list.',
219
+ category: 'utility',
220
+ stagesAffected: ['populated'],
221
+ schema: companyCleanupInputSchema,
222
+ layout: companyCleanupLayout,
223
+ defaultInput: (list) => ({
224
+ listId: list.id,
225
+ targetDescription: '',
226
+ dryRun: true,
227
+ batchSize: 20
228
+ })
229
+ }
230
+ ]
231
+
232
+ export function App() {
233
+ return (
234
+ <ElevasisUIProvider listActions={listActions} {...providerProps}>
235
+ {children}
236
+ </ElevasisUIProvider>
237
+ )
238
+ }
239
+ ```
240
+
241
+ Author the `schema` in a browser-safe sibling file (no Node-only imports) so it can be reused by the workflow runtime AND the UI form. See `apollo-import-schema.ts` in the platform for the canonical split pattern.
242
+
243
+ Per-section split rule (operator-grade vs power-user):
244
+
245
+ - **Configuration:** target/limit, dry-run toggle, mode (preview/finalize), and any field an operator changes between runs.
246
+ - **Advanced:** concurrency, batch size, timeouts, credential names, workflow chaining, dev-only mode toggles. Omit the `advanced:` key entirely when a step has none.
247
+ - **`defaultInput`:** seeds the form before the user touches anything; must include `listId: list.id`.
248
+
249
+ ## 3. Build a Custom Campaign Workspace
250
+
251
+ When the project needs custom layout or vertical-specific rendering, use the hooks directly:
252
+
253
+ ```tsx
254
+ import { Badge, Button, Card, Group, Stack, Text } from '@mantine/core'
255
+ import { useArtifacts, useList, useListMembers, useTransitionListMember } from '@elevasis/ui/hooks'
256
+
257
+ export function CampaignWorkspace({ listId }: { listId: string }) {
258
+ const listQuery = useList(listId)
259
+ const membersQuery = useListMembers({ listId })
260
+ const artifactsQuery = useArtifacts({ ownerKind: 'list', ownerId: listId })
261
+ const transitionMember = useTransitionListMember()
262
+
263
+ const list = listQuery.data
264
+ if (!list) return null
265
+
266
+ const firstMember = membersQuery.data?.members[0]
267
+
268
+ return (
269
+ <Stack>
270
+ <Group justify="space-between">
271
+ <div>
272
+ <Text fw={700}>{list.name}</Text>
273
+ <Text size="sm" c="dimmed">{list.description}</Text>
274
+ </div>
275
+ <Badge>{list.stateKey}</Badge>
276
+ </Group>
277
+
278
+ <Card withBorder>
279
+ <Text size="sm">Artifacts: {artifactsQuery.data?.artifacts.length ?? 0}</Text>
280
+ </Card>
281
+
282
+ {firstMember ? (
283
+ <Button
284
+ onClick={() =>
285
+ transitionMember.mutate({
286
+ memberId: firstMember.id,
287
+ listId,
288
+ pipelineKey: 'lead-gen',
289
+ stageKey: 'prospecting',
290
+ stateKey: 'verified'
291
+ })
292
+ }
293
+ >
294
+ Mark First Member Verified
295
+ </Button>
296
+ ) : null}
297
+ </Stack>
298
+ )
299
+ }
300
+ ```
301
+
302
+ Use `useArtifacts({ ownerKind, ownerId })` for durable JSON artifacts like audits, research summaries, snapshots, exports, or model outputs.
303
+
304
+ ## 4. Read and Mutate Lead-Gen Data in Workflows
305
+
306
+ Inside deployed workflows, use worker adapters instead of browser hooks or direct database access:
307
+
308
+ External projects should define workflow input/output schemas in `@shared/types`; the example below assumes those shared schemas already exist.
309
+
310
+ ```ts
311
+ // operations/src/sales/qualify-list.ts
312
+ import type { WorkflowDefinition } from '@elevasis/sdk'
313
+ import { acqDb, list } from '@elevasis/sdk/worker'
314
+ import { qualifyListInputSchema, qualifyListOutputSchema } from '@shared/types'
315
+ import { resourceDescriptors } from '@core/config/organization-model'
316
+
317
+ export const qualifyListWorkflow: WorkflowDefinition = {
318
+ config: {
319
+ resource: resourceDescriptors.qualifyList,
320
+ resourceId: resourceDescriptors.qualifyList.id,
321
+ name: 'Qualify List',
322
+ type: resourceDescriptors.qualifyList.kind,
323
+ version: '1.0.0',
324
+ status: 'dev',
325
+ },
326
+ contract: { inputSchema: qualifyListInputSchema, outputSchema: qualifyListOutputSchema },
327
+ steps: {
328
+ qualify: {
329
+ id: 'qualify',
330
+ name: 'Qualify',
331
+ inputSchema: qualifyListInputSchema,
332
+ outputSchema: qualifyListOutputSchema,
333
+ next: null,
334
+ handler: async (input, context) => {
335
+ const config = await list.getConfig({ listId: input.listId })
336
+ const contacts = await acqDb.listContacts({ listId: input.listId, limit: 100, offset: 0 })
337
+
338
+ for (const contact of contacts.data) {
339
+ await list.updateContactStage({
340
+ listId: input.listId,
341
+ contactId: contact.id,
342
+ stage: 'verified',
343
+ executionId: context.executionId
344
+ })
345
+ }
346
+
347
+ await list.recordExecution({
348
+ listId: input.listId,
349
+ executionId: context.executionId,
350
+ configSnapshot: config
351
+ })
352
+
353
+ return { touched: contacts.data.length }
354
+ }
355
+ }
356
+ },
357
+ entryPoint: 'qualify'
358
+ }
359
+ ```
360
+
361
+ 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.
362
+
363
+ Keep these boundaries straight:
364
+
365
+ - Browser/UI code uses `@elevasis/ui/hooks`.
366
+ - Workflow code uses `@elevasis/sdk/worker` adapters.
367
+ - `organizationId` is injected by the platform dispatcher. Do not pass it from workflow code.
368
+ - Persisted API shapes are owned by core/API contracts, not route-local TypeScript.
369
+
370
+ ## 5. Customize Lead-Gen Semantics
371
+
372
+ The lead-gen shell module key is `lead-gen`, the Organization OS System id is `sales.lead-gen`, and the action currently used by the shared manifest is `leadgen.lists.manage`.
373
+
374
+ For feature toggles, labels, lifecycle semantics, or resource descriptors, update the project model:
375
+
376
+ `core/config/organization-model.ts`
377
+
378
+ The platform-side lead-gen stateful vocabulary is defined for:
379
+
380
+ - `acq.list`
381
+ - `acq.list-member`
382
+ - `acq.list-company`
383
+
384
+ 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.
385
+
386
+ Changing persisted platform stages, adding new owner kinds, or extending artifact schema requires coordinated core/API/UI updates. Relabeling, route wrapping, sidebar changes, and project-owned rendering usually stay in the downstream project.
387
+
388
+ ## Verify
389
+
390
+ Run the checks for the surfaces you touched:
391
+
392
+ ```bash
393
+ pnpm -C ui run check
394
+ pnpm -C operations run check
395
+ pnpm -C operations exec elevasis-sdk check
396
+ ```
397
+
398
+ If you changed platform-level lead-gen contracts in the monorepo, the platform maintainer must also regenerate and verify scaffold output:
399
+
400
+ ```bash
401
+ pnpm scaffold:sync
402
+ pnpm scaffold:verify
403
+ ```