@elevasis/core 0.24.1 → 0.26.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 (82) hide show
  1. package/dist/index.d.ts +239 -86
  2. package/dist/index.js +474 -1346
  3. package/dist/knowledge/index.d.ts +57 -39
  4. package/dist/knowledge/index.js +1 -1
  5. package/dist/organization-model/index.d.ts +239 -86
  6. package/dist/organization-model/index.js +474 -1346
  7. package/dist/test-utils/index.d.ts +24 -31
  8. package/dist/test-utils/index.js +76 -1238
  9. package/package.json +1 -1
  10. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +108 -96
  11. package/src/business/acquisition/api-schemas.test.ts +70 -77
  12. package/src/business/acquisition/api-schemas.ts +21 -42
  13. package/src/business/acquisition/derive-actions.test.ts +11 -21
  14. package/src/business/acquisition/derive-actions.ts +61 -14
  15. package/src/business/acquisition/ontology-validation.ts +4 -4
  16. package/src/business/acquisition/types.ts +7 -8
  17. package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +10 -10
  18. package/src/knowledge/__tests__/queries.test.ts +960 -546
  19. package/src/knowledge/format.ts +322 -100
  20. package/src/knowledge/index.ts +18 -5
  21. package/src/knowledge/queries.ts +1004 -240
  22. package/src/organization-model/__tests__/content-kinds-registry.test.ts +35 -210
  23. package/src/organization-model/__tests__/defaults.test.ts +4 -4
  24. package/src/organization-model/__tests__/deprecate-helpers.test.ts +71 -0
  25. package/src/organization-model/__tests__/domains/actions.test.ts +12 -36
  26. package/src/organization-model/__tests__/domains/offerings.test.ts +13 -6
  27. package/src/organization-model/__tests__/domains/resources.test.ts +497 -350
  28. package/src/organization-model/__tests__/domains/systems.test.ts +6 -7
  29. package/src/organization-model/__tests__/flatten-additive-merge.test.ts +68 -80
  30. package/src/organization-model/__tests__/foundation.test.ts +81 -14
  31. package/src/organization-model/__tests__/graph.test.ts +662 -694
  32. package/src/organization-model/__tests__/knowledge.test.ts +31 -17
  33. package/src/organization-model/__tests__/lookup-helpers.test.ts +128 -438
  34. package/src/organization-model/__tests__/migration-helpers.test.ts +362 -591
  35. package/src/organization-model/__tests__/prospecting-ssot.test.ts +68 -103
  36. package/src/organization-model/__tests__/published-zero-leak.test.ts +17 -0
  37. package/src/organization-model/__tests__/recursive-system-schema.test.ts +159 -532
  38. package/src/organization-model/__tests__/resolve.test.ts +88 -49
  39. package/src/organization-model/__tests__/scaffolders.test.ts +93 -0
  40. package/src/organization-model/__tests__/schema.test.ts +65 -56
  41. package/src/organization-model/catalogs/lead-gen.ts +0 -103
  42. package/src/organization-model/defaults.ts +17 -702
  43. package/src/organization-model/domains/actions.ts +116 -333
  44. package/src/organization-model/domains/knowledge.ts +15 -7
  45. package/src/organization-model/domains/projects.ts +4 -4
  46. package/src/organization-model/domains/prospecting.ts +405 -395
  47. package/src/organization-model/domains/resources.ts +206 -135
  48. package/src/organization-model/domains/sales.ts +5 -5
  49. package/src/organization-model/domains/systems.ts +8 -23
  50. package/src/organization-model/graph/build.ts +223 -294
  51. package/src/organization-model/graph/schema.ts +2 -3
  52. package/src/organization-model/graph/types.ts +12 -14
  53. package/src/organization-model/helpers.ts +120 -141
  54. package/src/organization-model/icons.ts +1 -0
  55. package/src/organization-model/index.ts +107 -126
  56. package/src/organization-model/migration-helpers.ts +211 -249
  57. package/src/organization-model/ontology.ts +0 -60
  58. package/src/organization-model/organization-graph.mdx +4 -5
  59. package/src/organization-model/organization-model.mdx +1 -1
  60. package/src/organization-model/published.ts +251 -228
  61. package/src/organization-model/resolve.ts +4 -5
  62. package/src/organization-model/scaffolders/helpers.ts +84 -0
  63. package/src/organization-model/scaffolders/index.ts +19 -0
  64. package/src/organization-model/scaffolders/scaffoldKnowledgeNode.ts +48 -0
  65. package/src/organization-model/scaffolders/scaffoldOntologyRecord.ts +38 -0
  66. package/src/organization-model/scaffolders/scaffoldResource.ts +59 -0
  67. package/src/organization-model/scaffolders/scaffoldSystem.ts +110 -0
  68. package/src/organization-model/scaffolders/types.ts +81 -0
  69. package/src/organization-model/schema.ts +610 -704
  70. package/src/organization-model/types.ts +167 -161
  71. package/src/platform/constants/versions.ts +1 -1
  72. package/src/platform/registry/__tests__/validation.test.ts +23 -0
  73. package/src/platform/registry/validation.ts +13 -2
  74. package/src/reference/_generated/contracts.md +108 -96
  75. package/src/reference/glossary.md +71 -69
  76. package/src/organization-model/content-kinds/config.ts +0 -36
  77. package/src/organization-model/content-kinds/index.ts +0 -78
  78. package/src/organization-model/content-kinds/pipeline.ts +0 -68
  79. package/src/organization-model/content-kinds/registry.ts +0 -44
  80. package/src/organization-model/content-kinds/status.ts +0 -71
  81. package/src/organization-model/content-kinds/template.ts +0 -83
  82. package/src/organization-model/content-kinds/types.ts +0 -117
@@ -4,24 +4,69 @@ import { getSystem, listAllSystems } from '../helpers'
4
4
  import { defineOrganizationModel, resolveOrganizationModel, resolveOrganizationModelWithResources } from '../resolve'
5
5
 
6
6
  describe('organization-model resolve', () => {
7
- it('resolves the system defaults', () => {
8
- const model = resolveOrganizationModel()
9
-
10
- expect(model.version).toBe(1)
11
- expect('surfaces' in model).toBe(false)
12
- expect('navigationGroups' in model).toBe(false)
13
- expect(model.systems['dashboard']?.path).toBe('/')
14
- expect(model.systems['sales']?.path).toBe('/sales')
15
- expect(model.systems['sales.crm']?.path).toBe('/crm')
16
- expect(model.systems['admin']?.requiresAdmin).toBe(true)
17
- expect(model.systems['archive']?.devOnly).toBe(true)
18
- })
19
-
20
- it('resolves authored sidebar navigation with Dashboard above Business and /clients as the client route', () => {
21
- const model = resolveOrganizationModel()
22
- const primaryEntries = Object.entries(model.navigation.sidebar.primary).sort(
23
- ([leftId, left], [rightId, right]) =>
24
- (left.order ?? Number.MAX_SAFE_INTEGER) - (right.order ?? Number.MAX_SAFE_INTEGER) ||
7
+ it('resolves the system defaults', () => {
8
+ const model = resolveOrganizationModel()
9
+
10
+ expect(model.version).toBe(1)
11
+ expect('surfaces' in model).toBe(false)
12
+ expect('navigationGroups' in model).toBe(false)
13
+ expect(model.systems).toEqual({})
14
+ expect(model.navigation.sidebar.primary).toEqual({})
15
+ expect(model.navigation.sidebar.bottom).toEqual({})
16
+ })
17
+
18
+ it('resolves authored sidebar navigation with explicit tenant systems', () => {
19
+ const model = resolveOrganizationModel({
20
+ navigation: {
21
+ sidebar: {
22
+ primary: {
23
+ dashboard: {
24
+ type: 'surface',
25
+ label: 'Dashboard',
26
+ path: '/',
27
+ surfaceType: 'dashboard',
28
+ order: 10,
29
+ targets: { systems: ['dashboard'] }
30
+ },
31
+ business: {
32
+ type: 'group',
33
+ label: 'Business',
34
+ order: 20,
35
+ children: {
36
+ clients: {
37
+ type: 'surface',
38
+ label: 'Clients',
39
+ path: '/clients',
40
+ surfaceType: 'list',
41
+ icon: 'clients',
42
+ order: 10,
43
+ targets: { systems: ['clients'] }
44
+ }
45
+ }
46
+ }
47
+ },
48
+ bottom: {}
49
+ }
50
+ },
51
+ systems: {
52
+ dashboard: {
53
+ id: 'dashboard',
54
+ order: 10,
55
+ label: 'Dashboard',
56
+ lifecycle: 'active'
57
+ },
58
+ clients: {
59
+ id: 'clients',
60
+ order: 20,
61
+ label: 'Clients',
62
+ lifecycle: 'active',
63
+ icon: 'clients'
64
+ }
65
+ }
66
+ })
67
+ const primaryEntries = Object.entries(model.navigation.sidebar.primary).sort(
68
+ ([leftId, left], [rightId, right]) =>
69
+ (left.order ?? Number.MAX_SAFE_INTEGER) - (right.order ?? Number.MAX_SAFE_INTEGER) ||
25
70
  leftId.localeCompare(rightId)
26
71
  )
27
72
 
@@ -29,15 +74,17 @@ describe('organization-model resolve', () => {
29
74
  expect(primaryEntries[1]?.[0]).toBe('business')
30
75
 
31
76
  const business = model.navigation.sidebar.primary.business
32
- expect(business?.type).toBe('group')
33
- if (business?.type === 'group') {
34
- expect(business.children.clients).toMatchObject({
35
- type: 'surface',
36
- path: '/clients',
37
- targets: { systems: ['clients'] }
38
- })
39
- }
40
- })
77
+ expect(business?.type).toBe('group')
78
+ if (business?.type === 'group') {
79
+ expect(business.children.clients).toMatchObject({
80
+ type: 'surface',
81
+ path: '/clients',
82
+ icon: 'clients',
83
+ targets: { systems: ['clients'] }
84
+ })
85
+ }
86
+ expect(model.systems.clients?.icon).toBe('clients')
87
+ })
41
88
 
42
89
  it('adds tenant systems additively to the merged record', () => {
43
90
  const baseSize = Object.keys(resolveOrganizationModel().systems).length
@@ -115,20 +162,20 @@ describe('organization-model resolve', () => {
115
162
  })
116
163
 
117
164
  it('preserves sibling fields when overriding a nested property', () => {
118
- const model = resolveOrganizationModel({
119
- branding: {
120
- organizationName: 'OverriddenOrg'
121
- }
165
+ const model = resolveOrganizationModel({
166
+ branding: {
167
+ organizationName: 'OverriddenOrg'
168
+ }
122
169
  })
123
170
 
124
- expect(model.branding.organizationName).toBe('OverriddenOrg')
125
- expect(model.branding.productName).toBe('Elevasis')
126
- expect(Object.keys(model.systems).length).toBeGreaterThan(0)
127
- })
128
-
129
- it('overrides an existing system by id when the same key is supplied', () => {
130
- const model = resolveOrganizationModel({
131
- systems: {
171
+ expect(model.branding.organizationName).toBe('OverriddenOrg')
172
+ expect(model.branding.productName).toBe('Elevasis')
173
+ expect(model.systems).toEqual({})
174
+ })
175
+
176
+ it('overrides an existing system by id when the same key is supplied', () => {
177
+ const model = resolveOrganizationModel({
178
+ systems: {
132
179
  dashboard: {
133
180
  id: 'dashboard',
134
181
  order: 10,
@@ -281,15 +328,7 @@ describe('resolveOrganizationModelWithResources', () => {
281
328
  order: 910,
282
329
  label: 'CRM',
283
330
  enabled: true,
284
- config: { retryCount: 3 },
285
- content: {
286
- settings: {
287
- kind: 'config',
288
- type: 'kv',
289
- label: 'Settings',
290
- data: { entries: { retryCount: 1, fromContent: true } }
291
- }
292
- }
331
+ config: { retryCount: 3, fromConfig: true }
293
332
  }
294
333
  }
295
334
  }
@@ -315,6 +354,6 @@ describe('resolveOrganizationModelWithResources', () => {
315
354
  expect(resolved.systems.sys?.resources?.map((resource) => resource.id)).toEqual(['sys-workflow'])
316
355
  expect(resolved.systems['sys.crm']?.id).toBe('sys.crm')
317
356
  expect(resolved.systems['sys.crm']?.resources?.map((resource) => resource.id)).toEqual(['crm-workflow'])
318
- expect(resolved.systems['sys.crm']?.config).toEqual({ retryCount: 3, fromContent: true })
357
+ expect(resolved.systems['sys.crm']?.config).toEqual({ retryCount: 3, fromConfig: true })
319
358
  })
320
359
  })
@@ -0,0 +1,93 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { DEFAULT_ORGANIZATION_MODEL, scaffoldOrganizationModel, type OrganizationModel } from '..'
3
+
4
+ const BASE_MODEL: OrganizationModel = {
5
+ ...DEFAULT_ORGANIZATION_MODEL,
6
+ systems: {
7
+ sales: {
8
+ id: 'sales',
9
+ order: 10,
10
+ label: 'Sales',
11
+ lifecycle: 'active',
12
+ systems: {
13
+ crm: {
14
+ id: 'sales.crm',
15
+ order: 20,
16
+ label: 'CRM',
17
+ lifecycle: 'active'
18
+ }
19
+ }
20
+ }
21
+ },
22
+ resources: {},
23
+ knowledge: {}
24
+ }
25
+
26
+ describe('organization-model scaffolders', () => {
27
+ it('plans the minimal system scaffold without route files', () => {
28
+ const plan = scaffoldOrganizationModel(BASE_MODEL, {
29
+ intent: 'system',
30
+ id: 'sales.partners',
31
+ label: 'Partners',
32
+ description: 'Partner operations.',
33
+ noProject: true
34
+ })
35
+
36
+ expect(plan.intent).toBe('system')
37
+ expect(plan.id).toBe('sales.partners')
38
+ expect(plan.edits.map((edit) => edit.path)).toEqual(
39
+ expect.arrayContaining([
40
+ 'packages/elevasis-core/src/organization-model/systems.ts',
41
+ 'packages/elevasis-core/src/organization-model/navigation.ts',
42
+ 'packages/elevasis-core/src/organization-model/roles.ts',
43
+ 'packages/elevasis-core/src/organization-model/assembly.ts',
44
+ 'packages/elevasis-core/src/organization-model/knowledge.ts'
45
+ ])
46
+ )
47
+ expect(plan.writes.some((write) => write.path.endsWith('manifest.stub.ts'))).toBe(true)
48
+ expect(plan.writes.some((write) => write.path.includes('/routes/'))).toBe(false)
49
+ expect(plan.projectCommand).toBeUndefined()
50
+ })
51
+
52
+ it('defaults system scaffolds to a project chain unless skipped', () => {
53
+ const plan = scaffoldOrganizationModel(BASE_MODEL, {
54
+ intent: 'system',
55
+ id: 'sales.enablement',
56
+ label: 'Enablement'
57
+ })
58
+
59
+ expect(plan.projectCommand).toContain('project:create --link-om sales.enablement')
60
+ })
61
+
62
+ it('keeps ontology/resource/knowledge project chains opt-in', () => {
63
+ const ontology = scaffoldOrganizationModel(BASE_MODEL, {
64
+ intent: 'ontology',
65
+ id: 'deal-score',
66
+ systemPath: 'sales.crm',
67
+ kind: 'object'
68
+ })
69
+ const resource = scaffoldOrganizationModel(BASE_MODEL, {
70
+ intent: 'resource',
71
+ id: 'crm-score-workflow',
72
+ systemPath: 'sales.crm'
73
+ })
74
+ const knowledge = scaffoldOrganizationModel(BASE_MODEL, {
75
+ intent: 'knowledge',
76
+ id: 'knowledge.crm-scoring',
77
+ systemPath: 'sales.crm'
78
+ })
79
+
80
+ expect(ontology.projectCommand).toBeUndefined()
81
+ expect(resource.projectCommand).toBeUndefined()
82
+ expect(knowledge.projectCommand).toBeUndefined()
83
+ })
84
+
85
+ it('rejects duplicate system paths during planning', () => {
86
+ expect(() =>
87
+ scaffoldOrganizationModel(BASE_MODEL, {
88
+ intent: 'system',
89
+ id: 'sales.crm'
90
+ })
91
+ ).toThrow('system already exists: sales.crm')
92
+ })
93
+ })
@@ -313,21 +313,36 @@ describe('ontology contract validation', () => {
313
313
  ])
314
314
  })
315
315
 
316
- it('projects legacy entities, actions, and system content into compiled ontology indexes', () => {
316
+ it('projects legacy entities and actions while reading catalogs from authored ontology', () => {
317
317
  const model = OrganizationModelSchema.parse(
318
- makeMinimalModel({
319
- dashboard: {
320
- ...makeSystem('dashboard', '/'),
321
- content: {
322
- pipeline: {
323
- kind: 'schema',
324
- type: 'pipeline',
325
- label: 'Pipeline',
326
- data: { entityId: 'crm.deal' }
318
+ {
319
+ ...makeMinimalModel({
320
+ dashboard: {
321
+ ...makeSystem('dashboard', '/'),
322
+ ontology: {
323
+ catalogTypes: {
324
+ 'dashboard:catalog/pipeline': {
325
+ id: 'dashboard:catalog/pipeline',
326
+ kind: 'pipeline',
327
+ appliesTo: 'dashboard:object/crm.deal',
328
+ label: 'Pipeline',
329
+ entries: {
330
+ open: { label: 'Open', order: 10, semanticClass: 'open' }
331
+ }
332
+ }
333
+ }
327
334
  }
328
335
  }
336
+ }),
337
+ actions: {
338
+ send_reply: {
339
+ id: 'send_reply',
340
+ order: 10,
341
+ label: 'Send Reply',
342
+ affects: ['crm.deal']
343
+ }
329
344
  }
330
- })
345
+ }
331
346
  )
332
347
 
333
348
  const compilation = compileOrganizationOntology(model)
@@ -339,19 +354,27 @@ describe('ontology contract validation', () => {
339
354
  legacyId: 'crm.deal'
340
355
  })
341
356
  expect(compilation.ontology.actionTypes['dashboard:action/send_reply']?.legacyActionId).toBe('send_reply')
342
- expect(compilation.ontology.catalogTypes['dashboard:catalog/pipeline']).not.toHaveProperty('legacyContentId')
343
357
  expect(compilation.ontology.catalogTypes['dashboard:catalog/pipeline']?.origin).toMatchObject({
344
- source: 'legacy.system.content',
345
- legacyId: 'dashboard:pipeline'
358
+ source: 'system:dashboard.ontology',
359
+ systemPath: 'dashboard'
346
360
  })
347
361
  })
348
362
 
349
- it('adds origin metadata to authored and projected compiled ontology records without mutating source', () => {
363
+ it('adds origin metadata to authored compiled ontology records without mutating source', () => {
350
364
  const authoredObject = {
351
365
  id: 'dashboard:object/task',
352
366
  label: 'Task',
353
367
  ownerSystemId: 'dashboard'
354
368
  }
369
+ const authoredCatalog = {
370
+ id: 'dashboard:catalog/task-status',
371
+ label: 'Task Status',
372
+ kind: 'status-flow',
373
+ appliesTo: 'dashboard:object/task',
374
+ entries: {
375
+ planned: { label: 'Planned', order: 10 }
376
+ }
377
+ }
355
378
  const model = OrganizationModelSchema.parse({
356
379
  ...makeMinimalModel({
357
380
  dashboard: {
@@ -359,14 +382,9 @@ describe('ontology contract validation', () => {
359
382
  ontology: {
360
383
  objectTypes: {
361
384
  'dashboard:object/task': authoredObject
362
- }
363
- },
364
- content: {
365
- pipeline: {
366
- kind: 'schema',
367
- type: 'pipeline',
368
- label: 'Pipeline',
369
- data: { entityId: 'crm.deal' }
385
+ },
386
+ catalogTypes: {
387
+ 'dashboard:catalog/task-status': authoredCatalog
370
388
  }
371
389
  }
372
390
  }
@@ -380,13 +398,13 @@ describe('ontology contract validation', () => {
380
398
  source: 'system:dashboard.ontology',
381
399
  systemPath: 'dashboard'
382
400
  })
383
- expect(compilation.ontology.catalogTypes['dashboard:catalog/pipeline']?.origin).toMatchObject({
384
- kind: 'projected',
385
- source: 'legacy.system.content',
386
- systemPath: 'dashboard',
387
- legacyId: 'dashboard:pipeline'
401
+ expect(compilation.ontology.catalogTypes['dashboard:catalog/task-status']?.origin).toMatchObject({
402
+ kind: 'authored',
403
+ source: 'system:dashboard.ontology',
404
+ systemPath: 'dashboard'
388
405
  })
389
406
  expect(authoredObject).not.toHaveProperty('origin')
407
+ expect(authoredCatalog).not.toHaveProperty('origin')
390
408
  expect(model.systems.dashboard?.ontology?.objectTypes?.['dashboard:object/task']).not.toHaveProperty('origin')
391
409
  })
392
410
 
@@ -674,47 +692,31 @@ describe('system config contract', () => {
674
692
  expect(result.success).toBe(false)
675
693
  })
676
694
 
677
- it('projects bridge-era config:kv into effective resolved system config', () => {
695
+ it('resolves first-class system config without bridge-era config:kv content', () => {
678
696
  const model = OrganizationModelSchema.parse(
679
697
  makeMinimalModel({
680
698
  dashboard: {
681
699
  ...makeSystem('dashboard', '/'),
682
700
  config: {
683
701
  retries: 3,
684
- nested: { direct: true }
685
- },
686
- content: {
687
- settings: {
688
- kind: 'config',
689
- type: 'kv',
690
- label: 'Settings',
691
- data: { entries: { enabled: true, retries: 1, mode: 'bridge' } }
692
- }
702
+ mode: 'direct',
703
+ nested: { direct: true, enabled: true }
693
704
  }
694
705
  }
695
706
  })
696
707
  )
697
708
 
698
709
  expect(resolveSystemConfig(model, 'dashboard')).toEqual({
699
- enabled: true,
700
710
  retries: 3,
701
- mode: 'bridge',
702
- nested: { direct: true }
711
+ mode: 'direct',
712
+ nested: { direct: true, enabled: true }
703
713
  })
704
714
 
705
715
  const resolved = resolveOrganizationModelWithResources({
706
716
  systems: {
707
717
  dashboard: {
708
718
  ...makeSystem('dashboard', '/'),
709
- config: { retries: 3 },
710
- content: {
711
- settings: {
712
- kind: 'config',
713
- type: 'kv',
714
- label: 'Settings',
715
- data: { entries: { enabled: true, retries: 1 } }
716
- }
717
- }
719
+ config: { enabled: true, retries: 3 }
718
720
  }
719
721
  }
720
722
  })
@@ -780,8 +782,8 @@ describe('system action and policy validation', () => {
780
782
  sales: { id: 'sales', order: 10, label: 'Sales', enabled: true, lifecycle: 'active' },
781
783
  'sales.lead-gen': makeSystem('sales.lead-gen')
782
784
  }),
783
- systems: {
784
- sales: {
785
+ systems: {
786
+ sales: {
785
787
  id: 'sales',
786
788
  order: 10,
787
789
  label: 'Sales',
@@ -793,10 +795,17 @@ describe('system action and policy validation', () => {
793
795
  label: 'Lead Gen',
794
796
  lifecycle: 'active',
795
797
  actions: [{ actionId: 'lead-gen.company.source', intent: 'exposes' }],
796
- policies: ['policy.lead-gen.approval']
797
- }
798
- },
799
- policies: {
798
+ policies: ['policy.lead-gen.approval']
799
+ }
800
+ },
801
+ actions: {
802
+ 'lead-gen.company.source': {
803
+ id: 'lead-gen.company.source',
804
+ order: 10,
805
+ label: 'Source companies'
806
+ }
807
+ },
808
+ policies: {
800
809
  'policy.lead-gen.approval': {
801
810
  id: 'policy.lead-gen.approval',
802
811
  order: 10,
@@ -39,106 +39,3 @@ export interface LeadGenStageCatalogEntry {
39
39
  recordStageKey?: string
40
40
  }
41
41
 
42
- /**
43
- * Canonical lead-gen processing stage catalog.
44
- * Keys are the stage names written by workflow steps into processing_state jsonb.
45
- *
46
- * Ordered roughly by pipeline progression (prospecting -> outreach -> qualification).
47
- */
48
- export const LEAD_GEN_STAGE_CATALOG: Record<string, LeadGenStageCatalogEntry> = {
49
- // Prospecting - company population
50
- scraped: {
51
- key: 'scraped',
52
- label: 'Scraped',
53
- description: 'Company was scraped from a source directory (Apify actor run).',
54
- order: 1,
55
- entity: 'company'
56
- },
57
- populated: {
58
- key: 'populated',
59
- label: 'Companies found',
60
- description: 'Companies have been found and added to the lead-gen list.',
61
- order: 2,
62
- entity: 'company'
63
- },
64
- crawled: {
65
- key: 'crawled',
66
- label: 'Websites crawled',
67
- description:
68
- 'Company websites have been crawled (e.g. via Apify) and raw page content stored for downstream LLM analysis.',
69
- order: 2.5,
70
- entity: 'company'
71
- },
72
- extracted: {
73
- key: 'extracted',
74
- label: 'Websites analyzed',
75
- description: 'Company websites have been analyzed for business signals.',
76
- order: 3,
77
- entity: 'company'
78
- },
79
- enriched: {
80
- key: 'enriched',
81
- label: 'Enriched',
82
- description: 'Company or contact enriched with third-party data (e.g. Tomba, Anymailfinder).',
83
- order: 4,
84
- entity: 'company'
85
- },
86
- 'decision-makers-enriched': {
87
- key: 'decision-makers-enriched',
88
- label: 'Decision-makers found',
89
- description: 'Decision-maker contacts discovered and attached to a qualified company.',
90
- order: 6,
91
- entity: 'company',
92
- recordEntity: 'contact',
93
- recordStageKey: 'discovered'
94
- },
95
-
96
- // Prospecting - contact discovery
97
- discovered: {
98
- key: 'discovered',
99
- label: 'Decision-makers found',
100
- description: 'Decision-maker contact details have been found.',
101
- order: 5,
102
- entity: 'contact'
103
- },
104
- verified: {
105
- key: 'verified',
106
- label: 'Emails verified',
107
- description: 'Contact email addresses have been checked for deliverability.',
108
- order: 7,
109
- entity: 'contact'
110
- },
111
-
112
- // Qualification
113
- qualified: {
114
- key: 'qualified',
115
- label: 'Companies qualified',
116
- description: 'Companies have been scored against the qualification criteria.',
117
- order: 8,
118
- entity: 'company'
119
- },
120
-
121
- // Outreach
122
- personalized: {
123
- key: 'personalized',
124
- label: 'Personalized',
125
- description: 'Outreach message personalized for the contact (Instantly personalization workflow).',
126
- order: 9,
127
- entity: 'contact'
128
- },
129
- uploaded: {
130
- key: 'uploaded',
131
- label: 'Reviewed and exported',
132
- description: 'Approved records have been reviewed and exported for handoff.',
133
- order: 10,
134
- entity: 'company',
135
- additionalEntities: ['contact']
136
- },
137
- interested: {
138
- key: 'interested',
139
- label: 'Interested',
140
- description: 'Contact replied with a positive signal (Instantly reply-handler transition).',
141
- order: 11,
142
- entity: 'contact'
143
- }
144
- }