@elevasis/sdk 1.21.0 → 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 (160) hide show
  1. package/dist/cli.cjs +951 -171
  2. package/dist/index.d.ts +632 -341
  3. package/dist/index.js +3102 -142
  4. package/dist/node/index.d.ts +1 -0
  5. package/dist/node/index.js +19 -1
  6. package/dist/test-utils/index.d.ts +313 -4
  7. package/dist/test-utils/index.js +3246 -281
  8. package/dist/worker/index.js +3041 -80
  9. package/package.json +3 -3
  10. package/reference/claude-config/hooks/post-edit-validate.mjs +98 -98
  11. package/reference/claude-config/hooks/scaffold-registry-reminder.mjs +188 -188
  12. package/reference/claude-config/hooks/tool-failure-recovery.mjs +73 -73
  13. package/reference/claude-config/registries/graph-skills.json +4 -4
  14. package/reference/claude-config/registries/knowledge-flags.json +0 -2
  15. package/reference/claude-config/rules/active-change-index.md +80 -80
  16. package/reference/claude-config/rules/agent-start-here.md +277 -277
  17. package/reference/claude-config/rules/deployment.md +57 -57
  18. package/reference/claude-config/rules/error-handling.md +56 -56
  19. package/reference/claude-config/rules/execution.md +40 -40
  20. package/reference/claude-config/rules/frontend.md +4 -4
  21. package/reference/claude-config/rules/observability.md +31 -31
  22. package/reference/claude-config/rules/operations.md +29 -17
  23. package/reference/claude-config/rules/organization-model.md +110 -84
  24. package/reference/claude-config/rules/organization-os.md +115 -113
  25. package/reference/claude-config/rules/package-taxonomy.md +33 -33
  26. package/reference/claude-config/rules/platform.md +42 -42
  27. package/reference/claude-config/rules/shared-types.md +49 -46
  28. package/reference/claude-config/rules/task-tracking.md +47 -47
  29. package/reference/claude-config/rules/ui.md +200 -200
  30. package/reference/claude-config/rules/vibe.md +235 -235
  31. package/reference/claude-config/scripts/statusline-command.js +18 -18
  32. package/reference/claude-config/settings.json +34 -34
  33. package/reference/claude-config/skills/deploy/{SKILL.md → skill.md} +156 -156
  34. package/reference/claude-config/skills/dsp/SKILL.md +66 -66
  35. package/reference/claude-config/skills/elevasis/SKILL.md +235 -235
  36. package/reference/claude-config/skills/explore/SKILL.md +6 -6
  37. package/reference/claude-config/skills/git-sync/SKILL.md +126 -126
  38. package/reference/claude-config/skills/knowledge/SKILL.md +314 -299
  39. package/reference/claude-config/skills/knowledge/operations/codify-level-a.md +100 -100
  40. package/reference/claude-config/skills/knowledge/operations/codify-level-b.md +159 -159
  41. package/reference/claude-config/skills/knowledge/operations/customers.md +109 -109
  42. package/reference/claude-config/skills/knowledge/operations/features.md +76 -76
  43. package/reference/claude-config/skills/knowledge/operations/goals.md +118 -118
  44. package/reference/claude-config/skills/knowledge/operations/identity.md +93 -93
  45. package/reference/claude-config/skills/knowledge/operations/labels.md +94 -94
  46. package/reference/claude-config/skills/knowledge/operations/offerings.md +109 -109
  47. package/reference/claude-config/skills/knowledge/operations/roles.md +99 -99
  48. package/reference/claude-config/skills/knowledge/operations/techStack.md +30 -30
  49. package/reference/claude-config/skills/project/SKILL.md +1088 -1088
  50. package/reference/claude-config/skills/run-ui/SKILL.md +73 -73
  51. package/reference/claude-config/skills/save/SKILL.md +3 -3
  52. package/reference/claude-config/skills/setup/SKILL.md +275 -275
  53. package/reference/claude-config/skills/status/SKILL.md +59 -59
  54. package/reference/claude-config/skills/submit-request/SKILL.md +180 -180
  55. package/reference/claude-config/skills/sync/SKILL.md +47 -47
  56. package/reference/claude-config/skills/tutorial/SKILL.md +259 -259
  57. package/reference/claude-config/skills/tutorial/progress-template.md +74 -74
  58. package/reference/claude-config/skills/tutorial/technical.md +1303 -1303
  59. package/reference/claude-config/skills/tutorial/vibe-coder.md +890 -890
  60. package/reference/claude-config/sync-notes/2026-04-22-git-sync-and-sync-notes.md +27 -27
  61. package/reference/claude-config/sync-notes/2026-04-22-lead-gen-deliverability-removal.md +30 -30
  62. package/reference/claude-config/sync-notes/2026-04-24-test-utils-and-template-tests.md +73 -73
  63. package/reference/claude-config/sync-notes/2026-04-24-ui-consolidation-and-sdk-cli-train.md +86 -86
  64. package/reference/claude-config/sync-notes/2026-04-25-auth-role-system-and-settings-roles.md +55 -55
  65. package/reference/claude-config/sync-notes/2026-04-27-crm-hitl-action-layer-cutover.md +97 -97
  66. package/reference/claude-config/sync-notes/2026-04-27-lead-gen-substrate-train.md +112 -112
  67. package/reference/claude-config/sync-notes/2026-04-29-crm-state-and-lead-gen-processing-status.md +93 -93
  68. package/reference/claude-config/sync-notes/2026-05-02-crm-ownership-next-action.md +58 -58
  69. package/reference/claude-config/sync-notes/2026-05-02-template-hardcode-workos-config.md +56 -56
  70. package/reference/claude-config/sync-notes/2026-05-04-elevasis-workspace.md +71 -71
  71. package/reference/claude-config/sync-notes/2026-05-04-knowledge-bundle.md +83 -83
  72. package/reference/claude-config/sync-notes/2026-05-04-template-skills-run-ui-and-tutorial.md +59 -59
  73. package/reference/claude-config/sync-notes/2026-05-05-list-builder.md +42 -42
  74. package/reference/claude-config/sync-notes/2026-05-06-crm-spine.md +60 -60
  75. package/reference/claude-config/sync-notes/2026-05-06-sdk-changes-release-train.md +37 -37
  76. package/reference/claude-config/sync-notes/2026-05-07-sdk-changes-release-train.md +34 -34
  77. package/reference/claude-config/sync-notes/2026-05-08-resource-governance-scaffold-guidance.md +38 -38
  78. package/reference/claude-config/sync-notes/2026-05-09-clients-domain.md +32 -32
  79. package/reference/claude-config/sync-notes/2026-05-09-command-system.md +33 -33
  80. package/reference/claude-config/sync-notes/2026-05-09-resource-governance-and-misc.md +69 -69
  81. package/reference/claude-config/sync-notes/2026-05-12-sdk-ready-release-train.md +30 -30
  82. package/reference/claude-config/sync-notes/2026-05-14-organization-model-ontology-refactor.md +42 -0
  83. package/reference/claude-config/sync-notes/README.md +43 -43
  84. package/reference/cli.mdx +808 -808
  85. package/reference/concepts.mdx +146 -146
  86. package/reference/deployment/api.mdx +297 -297
  87. package/reference/deployment/command-center.mdx +209 -209
  88. package/reference/deployment/index.mdx +195 -195
  89. package/reference/deployment/provided-features.mdx +107 -107
  90. package/reference/deployment/ui-execution.mdx +250 -250
  91. package/reference/examples/organization-model.ts +146 -83
  92. package/reference/framework/agent.mdx +156 -156
  93. package/reference/framework/index.mdx +195 -195
  94. package/reference/framework/interaction-guidance.mdx +182 -182
  95. package/reference/framework/memory.mdx +326 -326
  96. package/reference/framework/project-structure.mdx +282 -282
  97. package/reference/framework/tutorial-system.mdx +135 -135
  98. package/reference/getting-started.mdx +142 -142
  99. package/reference/index.mdx +106 -106
  100. package/reference/packages/core/src/README.md +14 -14
  101. package/reference/packages/core/src/business/README.md +2 -2
  102. package/reference/packages/core/src/knowledge/README.md +32 -32
  103. package/reference/packages/core/src/organization-model/README.md +149 -149
  104. package/reference/packages/core/src/test-utils/README.md +37 -37
  105. package/reference/packages/ui/src/api/README.md +18 -18
  106. package/reference/packages/ui/src/app/README.md +24 -24
  107. package/reference/packages/ui/src/auth/README.md +18 -18
  108. package/reference/packages/ui/src/components/README.md +24 -24
  109. package/reference/packages/ui/src/execution/README.md +16 -16
  110. package/reference/packages/ui/src/features/README.md +28 -28
  111. package/reference/packages/ui/src/graph/README.md +16 -16
  112. package/reference/packages/ui/src/hooks/README.md +23 -23
  113. package/reference/packages/ui/src/initialization/README.md +19 -19
  114. package/reference/packages/ui/src/knowledge/README.md +31 -31
  115. package/reference/packages/ui/src/organization/README.md +18 -18
  116. package/reference/packages/ui/src/profile/README.md +19 -19
  117. package/reference/packages/ui/src/provider/README.md +32 -32
  118. package/reference/packages/ui/src/router/README.md +18 -18
  119. package/reference/packages/ui/src/sse/README.md +13 -13
  120. package/reference/packages/ui/src/test-utils/README.md +7 -7
  121. package/reference/packages/ui/src/theme/README.md +23 -23
  122. package/reference/packages/ui/src/theme/presets/README.md +19 -19
  123. package/reference/packages/ui/src/types/README.md +16 -16
  124. package/reference/packages/ui/src/utils/README.md +18 -18
  125. package/reference/packages/ui/src/zustand/README.md +18 -18
  126. package/reference/platform-tools/adapters-integration.mdx +301 -301
  127. package/reference/platform-tools/adapters-platform.mdx +553 -553
  128. package/reference/platform-tools/index.mdx +217 -217
  129. package/reference/platform-tools/type-safety.mdx +82 -82
  130. package/reference/resources/index.mdx +349 -349
  131. package/reference/resources/patterns.mdx +449 -449
  132. package/reference/resources/types.mdx +116 -116
  133. package/reference/roadmap.mdx +165 -165
  134. package/reference/runtime.mdx +173 -173
  135. package/reference/scaffold/core/organization-graph.mdx +110 -90
  136. package/reference/scaffold/core/organization-model.mdx +226 -219
  137. package/reference/scaffold/index.mdx +67 -67
  138. package/reference/scaffold/operations/propagation-pipeline.md +77 -77
  139. package/reference/scaffold/operations/scaffold-maintenance.md +12 -12
  140. package/reference/scaffold/operations/workflow-recipes.md +138 -138
  141. package/reference/scaffold/recipes/add-a-feature.md +308 -88
  142. package/reference/scaffold/recipes/add-a-resource.md +134 -110
  143. package/reference/scaffold/recipes/customize-knowledge-browser.md +5 -5
  144. package/reference/scaffold/recipes/customize-organization-model.md +273 -138
  145. package/reference/scaffold/recipes/extend-a-base-entity.md +8 -8
  146. package/reference/scaffold/recipes/extend-crm.md +3 -3
  147. package/reference/scaffold/recipes/extend-lead-gen.md +400 -400
  148. package/reference/scaffold/recipes/gate-by-feature-or-admin.md +118 -118
  149. package/reference/scaffold/recipes/index.md +46 -46
  150. package/reference/scaffold/recipes/query-the-knowledge-graph.md +197 -170
  151. package/reference/scaffold/reference/contracts.md +2101 -2096
  152. package/reference/scaffold/reference/glossary.md +76 -76
  153. package/reference/scaffold/ui/composition-extensibility.mdx +233 -233
  154. package/reference/scaffold/ui/customization.md +243 -243
  155. package/reference/scaffold/ui/feature-flags-and-gating.md +46 -46
  156. package/reference/scaffold/ui/feature-shell.mdx +72 -72
  157. package/reference/scaffold/ui/recipes.md +221 -214
  158. package/reference/spine/spine-primer.md +96 -96
  159. package/reference/templates/index.mdx +47 -47
  160. package/reference/troubleshooting.mdx +223 -223
@@ -1,403 +1,403 @@
1
1
  <!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
2
2
  <!-- Regenerate: pnpm scaffold:sync -->
3
3
 
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
- ```
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
+ ```