@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.
- package/dist/index.d.ts +239 -86
- package/dist/index.js +474 -1346
- package/dist/knowledge/index.d.ts +57 -39
- package/dist/knowledge/index.js +1 -1
- package/dist/organization-model/index.d.ts +239 -86
- package/dist/organization-model/index.js +474 -1346
- package/dist/test-utils/index.d.ts +24 -31
- package/dist/test-utils/index.js +76 -1238
- package/package.json +1 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +108 -96
- package/src/business/acquisition/api-schemas.test.ts +70 -77
- package/src/business/acquisition/api-schemas.ts +21 -42
- package/src/business/acquisition/derive-actions.test.ts +11 -21
- package/src/business/acquisition/derive-actions.ts +61 -14
- package/src/business/acquisition/ontology-validation.ts +4 -4
- package/src/business/acquisition/types.ts +7 -8
- package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +10 -10
- package/src/knowledge/__tests__/queries.test.ts +960 -546
- package/src/knowledge/format.ts +322 -100
- package/src/knowledge/index.ts +18 -5
- package/src/knowledge/queries.ts +1004 -240
- package/src/organization-model/__tests__/content-kinds-registry.test.ts +35 -210
- package/src/organization-model/__tests__/defaults.test.ts +4 -4
- package/src/organization-model/__tests__/deprecate-helpers.test.ts +71 -0
- package/src/organization-model/__tests__/domains/actions.test.ts +12 -36
- package/src/organization-model/__tests__/domains/offerings.test.ts +13 -6
- package/src/organization-model/__tests__/domains/resources.test.ts +497 -350
- package/src/organization-model/__tests__/domains/systems.test.ts +6 -7
- package/src/organization-model/__tests__/flatten-additive-merge.test.ts +68 -80
- package/src/organization-model/__tests__/foundation.test.ts +81 -14
- package/src/organization-model/__tests__/graph.test.ts +662 -694
- package/src/organization-model/__tests__/knowledge.test.ts +31 -17
- package/src/organization-model/__tests__/lookup-helpers.test.ts +128 -438
- package/src/organization-model/__tests__/migration-helpers.test.ts +362 -591
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +68 -103
- package/src/organization-model/__tests__/published-zero-leak.test.ts +17 -0
- package/src/organization-model/__tests__/recursive-system-schema.test.ts +159 -532
- package/src/organization-model/__tests__/resolve.test.ts +88 -49
- package/src/organization-model/__tests__/scaffolders.test.ts +93 -0
- package/src/organization-model/__tests__/schema.test.ts +65 -56
- package/src/organization-model/catalogs/lead-gen.ts +0 -103
- package/src/organization-model/defaults.ts +17 -702
- package/src/organization-model/domains/actions.ts +116 -333
- package/src/organization-model/domains/knowledge.ts +15 -7
- package/src/organization-model/domains/projects.ts +4 -4
- package/src/organization-model/domains/prospecting.ts +405 -395
- package/src/organization-model/domains/resources.ts +206 -135
- package/src/organization-model/domains/sales.ts +5 -5
- package/src/organization-model/domains/systems.ts +8 -23
- package/src/organization-model/graph/build.ts +223 -294
- package/src/organization-model/graph/schema.ts +2 -3
- package/src/organization-model/graph/types.ts +12 -14
- package/src/organization-model/helpers.ts +120 -141
- package/src/organization-model/icons.ts +1 -0
- package/src/organization-model/index.ts +107 -126
- package/src/organization-model/migration-helpers.ts +211 -249
- package/src/organization-model/ontology.ts +0 -60
- package/src/organization-model/organization-graph.mdx +4 -5
- package/src/organization-model/organization-model.mdx +1 -1
- package/src/organization-model/published.ts +251 -228
- package/src/organization-model/resolve.ts +4 -5
- package/src/organization-model/scaffolders/helpers.ts +84 -0
- package/src/organization-model/scaffolders/index.ts +19 -0
- package/src/organization-model/scaffolders/scaffoldKnowledgeNode.ts +48 -0
- package/src/organization-model/scaffolders/scaffoldOntologyRecord.ts +38 -0
- package/src/organization-model/scaffolders/scaffoldResource.ts +59 -0
- package/src/organization-model/scaffolders/scaffoldSystem.ts +110 -0
- package/src/organization-model/scaffolders/types.ts +81 -0
- package/src/organization-model/schema.ts +610 -704
- package/src/organization-model/types.ts +167 -161
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/validation.test.ts +23 -0
- package/src/platform/registry/validation.ts +13 -2
- package/src/reference/_generated/contracts.md +108 -96
- package/src/reference/glossary.md +71 -69
- package/src/organization-model/content-kinds/config.ts +0 -36
- package/src/organization-model/content-kinds/index.ts +0 -78
- package/src/organization-model/content-kinds/pipeline.ts +0 -68
- package/src/organization-model/content-kinds/registry.ts +0 -44
- package/src/organization-model/content-kinds/status.ts +0 -71
- package/src/organization-model/content-kinds/template.ts +0 -83
- 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
|
|
14
|
-
expect(model.
|
|
15
|
-
expect(model.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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
|
|
316
|
+
it('projects legacy entities and actions while reading catalogs from authored ontology', () => {
|
|
317
317
|
const model = OrganizationModelSchema.parse(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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: '
|
|
345
|
-
|
|
358
|
+
source: 'system:dashboard.ontology',
|
|
359
|
+
systemPath: 'dashboard'
|
|
346
360
|
})
|
|
347
361
|
})
|
|
348
362
|
|
|
349
|
-
it('adds origin metadata to authored
|
|
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
|
-
|
|
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/
|
|
384
|
-
kind: '
|
|
385
|
-
source: '
|
|
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('
|
|
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
|
-
|
|
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: '
|
|
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
|
-
|
|
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
|
-
}
|