@elevasis/core 0.22.0 → 0.24.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 (244) hide show
  1. package/dist/index.d.ts +3214 -2501
  2. package/dist/index.js +3112 -1222
  3. package/dist/knowledge/index.d.ts +1108 -1264
  4. package/dist/knowledge/index.js +112 -9
  5. package/dist/organization-model/index.d.ts +3214 -2501
  6. package/dist/organization-model/index.js +3112 -1222
  7. package/dist/test-utils/index.d.ts +985 -1103
  8. package/dist/test-utils/index.js +2464 -1165
  9. package/package.json +5 -5
  10. package/src/README.md +14 -14
  11. package/src/__tests__/publish.test.ts +24 -24
  12. package/src/__tests__/template-core-compatibility.test.ts +9 -80
  13. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +2389 -2121
  14. package/src/_gen/__tests__/scaffold-contracts.test.ts +30 -30
  15. package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +217 -217
  16. package/src/auth/multi-tenancy/credentials/server/encryption.ts +69 -69
  17. package/src/auth/multi-tenancy/credentials/server/kek-loader.ts +37 -37
  18. package/src/auth/multi-tenancy/index.ts +26 -26
  19. package/src/auth/multi-tenancy/invitations/api-schemas.ts +104 -104
  20. package/src/auth/multi-tenancy/memberships/api-schemas.ts +143 -143
  21. package/src/auth/multi-tenancy/memberships/index.ts +26 -26
  22. package/src/auth/multi-tenancy/memberships/membership.ts +130 -130
  23. package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -194
  24. package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -136
  25. package/src/auth/multi-tenancy/permissions.test.ts +42 -42
  26. package/src/auth/multi-tenancy/permissions.ts +123 -123
  27. package/src/auth/multi-tenancy/role-management/api-schemas.ts +78 -78
  28. package/src/auth/multi-tenancy/role-management/index.ts +16 -16
  29. package/src/auth/multi-tenancy/theme-presets.ts +45 -45
  30. package/src/auth/multi-tenancy/types.ts +57 -57
  31. package/src/auth/multi-tenancy/users/api-schemas.ts +165 -165
  32. package/src/business/README.md +2 -2
  33. package/src/business/acquisition/activity-events.test.ts +250 -250
  34. package/src/business/acquisition/activity-events.ts +93 -93
  35. package/src/business/acquisition/api-schemas.test.ts +1883 -1843
  36. package/src/business/acquisition/api-schemas.ts +1493 -1500
  37. package/src/business/acquisition/build-templates.test.ts +240 -240
  38. package/src/business/acquisition/build-templates.ts +83 -41
  39. package/src/business/acquisition/crm-next-action.test.ts +262 -262
  40. package/src/business/acquisition/crm-next-action.ts +220 -220
  41. package/src/business/acquisition/crm-priority.test.ts +216 -216
  42. package/src/business/acquisition/crm-priority.ts +349 -349
  43. package/src/business/acquisition/crm-state-actions.test.ts +153 -151
  44. package/src/business/acquisition/deal-ownership.test.ts +351 -351
  45. package/src/business/acquisition/deal-ownership.ts +120 -120
  46. package/src/business/acquisition/derive-actions.test.ts +129 -104
  47. package/src/business/acquisition/derive-actions.ts +74 -84
  48. package/src/business/acquisition/index.ts +171 -170
  49. package/src/business/acquisition/ontology-validation.ts +309 -0
  50. package/src/business/acquisition/stateful.ts +30 -30
  51. package/src/business/acquisition/types.ts +396 -392
  52. package/src/business/clients/api-schemas.test.ts +115 -115
  53. package/src/business/clients/api-schemas.ts +158 -158
  54. package/src/business/clients/index.ts +1 -1
  55. package/src/business/crm/api-schemas.ts +40 -40
  56. package/src/business/crm/index.ts +1 -1
  57. package/src/business/deals/api-schemas.ts +87 -87
  58. package/src/business/deals/index.ts +1 -1
  59. package/src/business/index.ts +5 -5
  60. package/src/business/projects/types.ts +144 -144
  61. package/src/commands/queue/types/task.ts +15 -15
  62. package/src/execution/core/runner-types.ts +61 -61
  63. package/src/execution/core/sse-executions.ts +7 -7
  64. package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -10
  65. package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -16
  66. package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -4
  67. package/src/execution/engine/agent/core/types.ts +25 -25
  68. package/src/execution/engine/agent/index.ts +6 -6
  69. package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -24
  70. package/src/execution/engine/index.ts +443 -443
  71. package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +298 -298
  72. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -55
  73. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -107
  74. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -48
  75. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -99
  76. package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -1
  77. package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +363 -363
  78. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +162 -162
  79. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +316 -316
  80. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -18
  81. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -194
  82. package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -7
  83. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +204 -204
  84. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -105
  85. package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -428
  86. package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -2
  87. package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
  88. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1474 -1474
  89. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -103
  90. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.test.ts +88 -88
  91. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +141 -141
  92. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +76 -76
  93. package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -182
  94. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -310
  95. package/src/execution/engine/tools/integration/service.test.ts +239 -239
  96. package/src/execution/engine/tools/integration/service.ts +172 -172
  97. package/src/execution/engine/tools/integration/tool.ts +255 -255
  98. package/src/execution/engine/tools/lead-service-types.ts +1005 -1005
  99. package/src/execution/engine/tools/messages.ts +43 -43
  100. package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -7
  101. package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -6
  102. package/src/execution/engine/tools/platform/acquisition/list-tools.ts +6 -6
  103. package/src/execution/engine/tools/platform/acquisition/types.ts +280 -280
  104. package/src/execution/engine/tools/platform/email/types.ts +97 -97
  105. package/src/execution/engine/tools/registry.ts +704 -704
  106. package/src/execution/engine/tools/tool-maps.ts +831 -831
  107. package/src/execution/engine/tools/types.ts +234 -234
  108. package/src/execution/engine/workflow/types.ts +202 -202
  109. package/src/execution/external/__tests__/api-schemas.test.ts +127 -127
  110. package/src/execution/external/api-schemas.ts +40 -40
  111. package/src/execution/external/index.ts +1 -1
  112. package/src/index.ts +18 -18
  113. package/src/integrations/credentials/__tests__/api-schemas.test.ts +420 -420
  114. package/src/integrations/credentials/api-schemas.ts +146 -146
  115. package/src/integrations/credentials/schemas.ts +200 -200
  116. package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -7
  117. package/src/integrations/oauth/provider-registry.ts +74 -74
  118. package/src/integrations/oauth/server/credentials.ts +43 -43
  119. package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +327 -327
  120. package/src/integrations/webhook-endpoints/api-schemas.ts +103 -103
  121. package/src/integrations/webhook-endpoints/types.ts +58 -58
  122. package/src/knowledge/README.md +33 -32
  123. package/src/knowledge/__tests__/queries.test.ts +633 -541
  124. package/src/knowledge/format.ts +100 -99
  125. package/src/knowledge/index.ts +5 -5
  126. package/src/knowledge/published.ts +5 -5
  127. package/src/knowledge/queries.ts +274 -222
  128. package/src/operations/activities/api-schemas.ts +80 -80
  129. package/src/operations/activities/types.ts +64 -64
  130. package/src/organization-model/README.md +149 -109
  131. package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
  132. package/src/organization-model/__tests__/defaults.test.ts +168 -194
  133. package/src/organization-model/__tests__/domains/actions.test.ts +78 -0
  134. package/src/organization-model/__tests__/domains/customers.test.ts +48 -44
  135. package/src/organization-model/__tests__/domains/entities.test.ts +56 -0
  136. package/src/organization-model/__tests__/domains/goals.test.ts +110 -96
  137. package/src/organization-model/__tests__/domains/identity.test.ts +4 -3
  138. package/src/organization-model/__tests__/domains/navigation.test.ts +222 -166
  139. package/src/organization-model/__tests__/domains/offerings.test.ts +83 -88
  140. package/src/organization-model/__tests__/domains/policies.test.ts +323 -0
  141. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +30 -30
  142. package/src/organization-model/__tests__/domains/resources.test.ts +396 -175
  143. package/src/organization-model/__tests__/domains/roles.test.ts +463 -402
  144. package/src/organization-model/__tests__/domains/statuses.test.ts +13 -10
  145. package/src/organization-model/__tests__/domains/systems.test.ts +209 -193
  146. package/src/organization-model/__tests__/flatten-additive-merge.test.ts +362 -0
  147. package/src/organization-model/__tests__/foundation.test.ts +47 -75
  148. package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -0
  149. package/src/organization-model/__tests__/graph.test.ts +1336 -149
  150. package/src/organization-model/__tests__/icons.test.ts +10 -1
  151. package/src/organization-model/__tests__/knowledge.test.ts +418 -61
  152. package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -0
  153. package/src/organization-model/__tests__/migration-helpers.test.ts +591 -0
  154. package/src/organization-model/__tests__/prospecting-ssot.test.ts +103 -94
  155. package/src/organization-model/__tests__/recursive-system-schema.test.ts +549 -0
  156. package/src/organization-model/__tests__/resolve.test.ts +303 -42
  157. package/src/organization-model/__tests__/schema.test.ts +863 -153
  158. package/src/organization-model/__tests__/surface-projection.test.ts +284 -174
  159. package/src/organization-model/catalogs/lead-gen.ts +144 -0
  160. package/src/organization-model/content-kinds/config.ts +36 -0
  161. package/src/organization-model/content-kinds/index.ts +78 -0
  162. package/src/organization-model/content-kinds/pipeline.ts +68 -0
  163. package/src/organization-model/content-kinds/registry.ts +44 -0
  164. package/src/organization-model/content-kinds/status.ts +71 -0
  165. package/src/organization-model/content-kinds/template.ts +83 -0
  166. package/src/organization-model/content-kinds/types.ts +117 -0
  167. package/src/organization-model/contracts.ts +27 -17
  168. package/src/organization-model/defaults.ts +489 -107
  169. package/src/organization-model/domains/actions.ts +333 -0
  170. package/src/organization-model/domains/customers.ts +10 -7
  171. package/src/organization-model/domains/entities.ts +144 -0
  172. package/src/organization-model/domains/goals.ts +9 -6
  173. package/src/organization-model/domains/knowledge.ts +128 -54
  174. package/src/organization-model/domains/navigation.ts +139 -416
  175. package/src/organization-model/domains/offerings.ts +15 -10
  176. package/src/organization-model/domains/policies.ts +102 -0
  177. package/src/organization-model/domains/projects.ts +6 -40
  178. package/src/organization-model/domains/prospecting.ts +395 -514
  179. package/src/organization-model/domains/resources.ts +173 -81
  180. package/src/organization-model/domains/roles.ts +96 -93
  181. package/src/organization-model/domains/sales.test.ts +218 -218
  182. package/src/organization-model/domains/sales.ts +380 -589
  183. package/src/organization-model/domains/shared.ts +8 -8
  184. package/src/organization-model/domains/statuses.ts +298 -89
  185. package/src/organization-model/domains/systems.ts +240 -38
  186. package/src/organization-model/foundation.ts +35 -48
  187. package/src/organization-model/graph/build.ts +1035 -279
  188. package/src/organization-model/graph/index.ts +4 -4
  189. package/src/organization-model/graph/link.ts +10 -10
  190. package/src/organization-model/graph/schema.ts +77 -56
  191. package/src/organization-model/graph/types.ts +75 -56
  192. package/src/organization-model/helpers.ts +312 -59
  193. package/src/organization-model/icons.ts +78 -66
  194. package/src/organization-model/index.ts +129 -16
  195. package/src/organization-model/migration-helpers.ts +252 -0
  196. package/src/organization-model/ontology.ts +661 -0
  197. package/src/organization-model/organization-graph.mdx +110 -89
  198. package/src/organization-model/organization-model.mdx +226 -171
  199. package/src/organization-model/published.ts +295 -139
  200. package/src/organization-model/resolve.ts +139 -21
  201. package/src/organization-model/schema.ts +841 -301
  202. package/src/organization-model/surface-projection.ts +212 -218
  203. package/src/organization-model/types.ts +181 -90
  204. package/src/platform/api/types.ts +38 -38
  205. package/src/platform/constants/versions.ts +3 -3
  206. package/src/platform/index.ts +23 -23
  207. package/src/platform/registry/__tests__/command-view.test.ts +5 -7
  208. package/src/platform/registry/__tests__/resource-link.test.ts +35 -30
  209. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +17 -32
  210. package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -0
  211. package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2051
  212. package/src/platform/registry/__tests__/validation.test.ts +1347 -1343
  213. package/src/platform/registry/command-view.ts +10 -10
  214. package/src/platform/registry/index.ts +103 -103
  215. package/src/platform/registry/resource-link.ts +32 -32
  216. package/src/platform/registry/resource-registry.ts +890 -878
  217. package/src/platform/registry/serialization.ts +295 -295
  218. package/src/platform/registry/serialized-types.ts +166 -166
  219. package/src/platform/registry/stats-types.ts +68 -68
  220. package/src/platform/registry/types.ts +425 -425
  221. package/src/platform/registry/validation.ts +745 -743
  222. package/src/platform/utils/__tests__/validation.test.ts +1084 -1084
  223. package/src/platform/utils/validation.ts +425 -425
  224. package/src/projects/api-schemas.test.ts +39 -39
  225. package/src/projects/api-schemas.ts +291 -291
  226. package/src/reference/_generated/contracts.md +2389 -2121
  227. package/src/reference/glossary.md +76 -76
  228. package/src/scaffold-registry/__tests__/index.test.ts +206 -206
  229. package/src/scaffold-registry/__tests__/schema.test.ts +166 -166
  230. package/src/scaffold-registry/index.ts +392 -392
  231. package/src/scaffold-registry/schema.ts +243 -243
  232. package/src/server.ts +289 -289
  233. package/src/supabase/database.types.ts +3153 -3093
  234. package/src/test-utils/README.md +37 -37
  235. package/src/test-utils/entities.ts +108 -108
  236. package/src/test-utils/fixtures/memberships.ts +82 -82
  237. package/src/test-utils/index.ts +12 -12
  238. package/src/test-utils/organization-model.ts +65 -65
  239. package/src/test-utils/published.ts +6 -6
  240. package/src/test-utils/rls/RLSTestContext.ts +588 -588
  241. package/src/test-utils/test-utils.test.ts +44 -49
  242. package/src/organization-model/__tests__/domains/operations.test.ts +0 -203
  243. package/src/organization-model/domains/features.ts +0 -31
  244. package/src/organization-model/domains/operations.ts +0 -85
@@ -1,174 +1,284 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
3
- import {
4
- projectOrganizationSurfaces,
5
- validateOrganizationSurfaceProjection
6
- } from '../surface-projection'
7
- import type { OrganizationModel, OrganizationModelFeature, OrganizationModelSurface } from '../types'
8
-
9
- function makeFeature(
10
- id: string,
11
- overrides: Partial<OrganizationModelFeature> = {}
12
- ): OrganizationModelFeature {
13
- return {
14
- id,
15
- label: id,
16
- enabled: true,
17
- ...overrides
18
- }
19
- }
20
-
21
- function makeSurface(
22
- id: string,
23
- overrides: Partial<OrganizationModelSurface> = {}
24
- ): OrganizationModelSurface {
25
- return {
26
- id,
27
- label: id,
28
- path: `/${id.replaceAll('.', '/')}`,
29
- surfaceType: 'page',
30
- enabled: true,
31
- featureIds: [],
32
- entityIds: [],
33
- resourceIds: [],
34
- capabilityIds: [],
35
- ...overrides
36
- }
37
- }
38
-
39
- function makeModel(
40
- features: OrganizationModelFeature[],
41
- surfaces: OrganizationModelSurface[],
42
- navigation: Partial<OrganizationModel['navigation']> = {}
43
- ): OrganizationModel {
44
- return {
45
- ...DEFAULT_ORGANIZATION_MODEL,
46
- features,
47
- navigation: {
48
- surfaces,
49
- groups: [],
50
- ...navigation
51
- }
52
- }
53
- }
54
-
55
- describe('projectOrganizationSurfaces', () => {
56
- it('projects navigation surfaces into data-only DTOs with inherited flags', () => {
57
- const model = makeModel(
58
- [
59
- makeFeature('sales', { requiresAdmin: true }),
60
- makeFeature('sales.crm', { devOnly: true })
61
- ],
62
- [
63
- makeSurface('crm.pipeline', {
64
- label: 'Pipeline',
65
- path: '/crm/pipeline',
66
- surfaceType: 'graph',
67
- featureId: 'sales.crm',
68
- featureIds: ['sales.crm'],
69
- entityIds: ['crm.deal'],
70
- resourceIds: ['workflow.crm-sync'],
71
- capabilityIds: ['crm.pipeline.manage']
72
- })
73
- ]
74
- )
75
-
76
- expect(projectOrganizationSurfaces(model)).toEqual([
77
- {
78
- id: 'crm.pipeline',
79
- label: 'Pipeline',
80
- path: '/crm/pipeline',
81
- surfaceType: 'graph',
82
- featureId: 'sales.crm',
83
- featureIds: ['sales.crm'],
84
- entityIds: ['crm.deal'],
85
- resourceIds: ['workflow.crm-sync'],
86
- capabilityIds: ['crm.pipeline.manage'],
87
- enabled: true,
88
- devOnly: true,
89
- requiresAdmin: true
90
- }
91
- ])
92
- })
93
-
94
- it('canonicalizes legacy feature aliases', () => {
95
- const model = makeModel(
96
- [
97
- makeFeature('sales'),
98
- makeFeature('sales.crm'),
99
- makeFeature('sales.lead-gen'),
100
- makeFeature('monitoring'),
101
- makeFeature('monitoring.submitted-requests')
102
- ],
103
- [
104
- makeSurface('legacy.sales', {
105
- featureId: 'crm',
106
- featureIds: ['crm', 'lead-gen', 'submitted-requests']
107
- })
108
- ]
109
- )
110
-
111
- expect(projectOrganizationSurfaces(model)[0]).toMatchObject({
112
- featureId: 'sales.crm',
113
- featureIds: ['sales.crm', 'sales.lead-gen', 'monitoring.submitted-requests']
114
- })
115
- })
116
-
117
- it('disables projected surfaces when feature lineage is disabled', () => {
118
- const model = makeModel(
119
- [
120
- makeFeature('sales', { enabled: false }),
121
- makeFeature('sales.crm')
122
- ],
123
- [
124
- makeSurface('crm.pipeline', {
125
- enabled: true,
126
- featureId: 'sales.crm',
127
- featureIds: ['sales.crm']
128
- })
129
- ]
130
- )
131
-
132
- expect(projectOrganizationSurfaces(model)[0].enabled).toBe(false)
133
- })
134
- })
135
-
136
- describe('validateOrganizationSurfaceProjection', () => {
137
- it('returns issue codes for duplicate and unknown surface references', () => {
138
- const model = makeModel(
139
- [makeFeature('sales'), makeFeature('sales.crm')],
140
- [
141
- makeSurface('crm.pipeline', {
142
- path: '/crm/pipeline',
143
- featureId: 'sales.crm',
144
- featureIds: ['sales.crm']
145
- }),
146
- makeSurface('crm.pipeline', {
147
- path: '/crm/pipeline/',
148
- featureId: 'missing.primary',
149
- featureIds: ['missing.related']
150
- })
151
- ],
152
- {
153
- defaultSurfaceId: 'missing.default',
154
- groups: [
155
- {
156
- id: 'primary',
157
- label: 'Primary',
158
- placement: 'primary',
159
- surfaceIds: ['missing.group']
160
- }
161
- ]
162
- }
163
- )
164
-
165
- expect(validateOrganizationSurfaceProjection(model).map((issue) => issue.code)).toEqual([
166
- 'duplicate-surface-id',
167
- 'duplicate-surface-path',
168
- 'unknown-surface-feature',
169
- 'unknown-surface-feature-reference',
170
- 'unknown-default-surface',
171
- 'unknown-group-surface'
172
- ])
173
- })
174
- })
1
+ import { describe, expect, it } from 'vitest'
2
+ import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
3
+ import { ancestorsOf, childrenOf, topLevel } from '../helpers'
4
+ import { projectOrganizationSurfaces, validateOrganizationSurfaceProjection } from '../surface-projection'
5
+ import type {
6
+ OrganizationModel,
7
+ OrganizationModelSidebarNode,
8
+ OrganizationModelSidebarSurfaceNode,
9
+ OrganizationModelSystemEntry
10
+ } from '../types'
11
+
12
+ function makeSystem(id: string, overrides: Partial<OrganizationModelSystemEntry> = {}): OrganizationModelSystemEntry {
13
+ return {
14
+ id,
15
+ order: 10,
16
+ label: id,
17
+ lifecycle: 'active',
18
+ enabled: true,
19
+ ...overrides
20
+ }
21
+ }
22
+
23
+ function makeSurface(
24
+ label: string,
25
+ overrides: Partial<OrganizationModelSidebarSurfaceNode> = {}
26
+ ): OrganizationModelSidebarSurfaceNode {
27
+ return {
28
+ type: 'surface',
29
+ label,
30
+ path: `/${label.toLowerCase().replaceAll(' ', '-')}`,
31
+ surfaceType: 'page',
32
+ order: 10,
33
+ targets: {},
34
+ ...overrides
35
+ }
36
+ }
37
+
38
+ function makeModel(
39
+ systems: OrganizationModelSystemEntry[],
40
+ primary: Record<string, OrganizationModelSidebarNode>,
41
+ bottom: Record<string, OrganizationModelSidebarNode> = {}
42
+ ): OrganizationModel {
43
+ return {
44
+ ...DEFAULT_ORGANIZATION_MODEL,
45
+ systems: Object.fromEntries(
46
+ systems.map((system, index) => [system.id, { ...system, order: system.order ?? (index + 1) * 10 }])
47
+ ),
48
+ navigation: {
49
+ sidebar: {
50
+ primary,
51
+ bottom
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ describe('projectOrganizationSurfaces', () => {
58
+ it('projects recursive sidebar surface leaves into data-only DTOs with inherited flags', () => {
59
+ const model = makeModel(
60
+ [
61
+ makeSystem('sales', { requiresAdmin: true }),
62
+ makeSystem('sales.crm', { parentSystemId: 'sales', devOnly: true })
63
+ ],
64
+ {
65
+ business: {
66
+ type: 'group',
67
+ label: 'Business',
68
+ order: 20,
69
+ children: {
70
+ 'crm.pipeline': makeSurface('Pipeline', {
71
+ path: '/crm/pipeline',
72
+ surfaceType: 'graph',
73
+ icon: 'crm',
74
+ targets: {
75
+ systems: ['sales.crm'],
76
+ entities: ['crm.deal'],
77
+ resources: ['workflow.crm-sync'],
78
+ actions: ['crm.pipeline.manage']
79
+ }
80
+ })
81
+ }
82
+ }
83
+ }
84
+ )
85
+
86
+ expect(projectOrganizationSurfaces(model)).toEqual([
87
+ {
88
+ id: 'crm.pipeline',
89
+ label: 'Pipeline',
90
+ path: '/crm/pipeline',
91
+ surfaceType: 'graph',
92
+ icon: 'crm',
93
+ order: 10,
94
+ systemIds: ['sales.crm'],
95
+ entityIds: ['crm.deal'],
96
+ resourceIds: ['workflow.crm-sync'],
97
+ actionIds: ['crm.pipeline.manage'],
98
+ enabled: true,
99
+ devOnly: true,
100
+ requiresAdmin: true
101
+ }
102
+ ])
103
+ })
104
+
105
+ it('sorts sidebar records by order before projecting leaves', () => {
106
+ const model = makeModel([makeSystem('a'), makeSystem('b'), makeSystem('c')], {
107
+ third: makeSurface('Third', { order: 30, targets: { systems: ['c'] } }),
108
+ first: makeSurface('First', { order: 10, targets: { systems: ['a'] } }),
109
+ second: makeSurface('Second', { order: 20, targets: { systems: ['b'] } })
110
+ })
111
+
112
+ expect(projectOrganizationSurfaces(model).map((surface) => surface.id)).toEqual(['first', 'second', 'third'])
113
+ })
114
+
115
+ it('uses canonical semantic system IDs directly', () => {
116
+ const model = makeModel(
117
+ [
118
+ makeSystem('revenue'),
119
+ makeSystem('revenue.crm'),
120
+ makeSystem('revenue.lead-gen'),
121
+ makeSystem('delivery'),
122
+ makeSystem('delivery.projects')
123
+ ],
124
+ {
125
+ 'canonical.sales': makeSurface('Canonical Sales', {
126
+ targets: { systems: ['revenue.crm', 'revenue.lead-gen', 'delivery.projects'] }
127
+ })
128
+ }
129
+ )
130
+
131
+ expect(projectOrganizationSurfaces(model)[0]).toMatchObject({
132
+ systemIds: ['revenue.crm', 'revenue.lead-gen', 'delivery.projects']
133
+ })
134
+ })
135
+
136
+ it('disables projected surfaces when system lineage is disabled', () => {
137
+ const model = makeModel(
138
+ [makeSystem('sales', { enabled: false }), makeSystem('sales.crm', { parentSystemId: 'sales' })],
139
+ {
140
+ 'crm.pipeline': makeSurface('Pipeline', {
141
+ targets: { systems: ['sales.crm'] }
142
+ })
143
+ }
144
+ )
145
+
146
+ expect(projectOrganizationSurfaces(model)[0]?.enabled).toBe(false)
147
+ })
148
+ })
149
+
150
+ describe('validateOrganizationSurfaceProjection', () => {
151
+ it('returns issue codes for duplicate sidebar leaf ids, duplicate paths, and unknown system targets', () => {
152
+ const model = makeModel(
153
+ [makeSystem('sales'), makeSystem('sales.crm')],
154
+ {
155
+ business: {
156
+ type: 'group',
157
+ label: 'Business',
158
+ children: {
159
+ 'crm.pipeline': makeSurface('Pipeline', {
160
+ path: '/crm/pipeline',
161
+ targets: { systems: ['sales.crm'] }
162
+ })
163
+ }
164
+ }
165
+ },
166
+ {
167
+ 'crm.pipeline': makeSurface('Pipeline Duplicate Id', {
168
+ path: '/crm/pipeline/',
169
+ targets: { systems: ['missing.related'] }
170
+ })
171
+ }
172
+ )
173
+
174
+ expect(validateOrganizationSurfaceProjection(model).map((issue) => issue.code)).toEqual([
175
+ 'duplicate-surface-id',
176
+ 'duplicate-surface-path',
177
+ 'unknown-surface-system'
178
+ ])
179
+ })
180
+
181
+ it('emits unknown-surface-system with surfaceId and systemId for unrecognised system references', () => {
182
+ const model = makeModel([makeSystem('exists')], {
183
+ 'test.surface': makeSurface('Test', {
184
+ targets: { systems: ['exists', 'does.not.exist'] }
185
+ })
186
+ })
187
+
188
+ const issues = validateOrganizationSurfaceProjection(model)
189
+ expect(issues).toHaveLength(1)
190
+ expect(issues[0]).toMatchObject({
191
+ code: 'unknown-surface-system',
192
+ surfaceId: 'test.surface',
193
+ systemId: 'does.not.exist'
194
+ })
195
+ })
196
+ })
197
+
198
+ describe('projectOrganizationSurfaces - flag inheritance', () => {
199
+ it('inherits requiresAdmin transitively from grandparent via parentSystemId chain', () => {
200
+ const model = makeModel(
201
+ [
202
+ makeSystem('gp', { requiresAdmin: true }),
203
+ makeSystem('gp.parent', { parentSystemId: 'gp' }),
204
+ makeSystem('gp.parent.leaf', { parentSystemId: 'gp.parent' })
205
+ ],
206
+ { 'test.surface': makeSurface('Test', { targets: { systems: ['gp.parent.leaf'] } }) }
207
+ )
208
+
209
+ expect(projectOrganizationSurfaces(model)[0]).toMatchObject({ requiresAdmin: true })
210
+ })
211
+
212
+ it('computes devOnly from lifecycle beta even when devOnly is not set', () => {
213
+ const model = makeModel([makeSystem('beta-sys', { lifecycle: 'beta' })], {
214
+ 'test.surface': makeSurface('Test', { targets: { systems: ['beta-sys'] } })
215
+ })
216
+
217
+ expect(projectOrganizationSurfaces(model)[0]).toMatchObject({ devOnly: true })
218
+ })
219
+
220
+ it('preserves explicit sidebar devOnly and requiresAdmin flags', () => {
221
+ const model = makeModel([makeSystem('active-sys', { enabled: true, lifecycle: 'active' })], {
222
+ 'test.surface': makeSurface('Test', {
223
+ devOnly: true,
224
+ requiresAdmin: true,
225
+ targets: { systems: ['active-sys'] }
226
+ })
227
+ })
228
+
229
+ expect(projectOrganizationSurfaces(model)[0]).toMatchObject({ devOnly: true, requiresAdmin: true })
230
+ })
231
+
232
+ it('disables surface and preserves both systemIds when any referenced system is disabled', () => {
233
+ const model = makeModel([makeSystem('sys-a', { enabled: true }), makeSystem('sys-b', { enabled: false })], {
234
+ 'test.surface': makeSurface('Test', { targets: { systems: ['sys-a', 'sys-b'] } })
235
+ })
236
+
237
+ const projection = projectOrganizationSurfaces(model)[0]
238
+ expect(projection?.enabled).toBe(false)
239
+ expect(projection?.systemIds).toEqual(['sys-a', 'sys-b'])
240
+ })
241
+
242
+ it('disables surface for deprecated lifecycle and archived lifecycle even when enabled is true', () => {
243
+ for (const lifecycle of ['deprecated', 'archived'] as const) {
244
+ const model = makeModel([makeSystem('gated-sys', { lifecycle, enabled: true })], {
245
+ 'test.surface': makeSurface('Test', { targets: { systems: ['gated-sys'] } })
246
+ })
247
+
248
+ expect(projectOrganizationSurfaces(model)[0]?.enabled, `lifecycle: ${lifecycle}`).toBe(false)
249
+ }
250
+ })
251
+
252
+ it('filters unknown system references from projection systemIds', () => {
253
+ const model = makeModel([makeSystem('exists')], {
254
+ 'test.surface': makeSurface('Test', { targets: { systems: ['exists', 'does.not.exist'] } })
255
+ })
256
+
257
+ expect(projectOrganizationSurfaces(model)[0]?.systemIds).toEqual(['exists'])
258
+ })
259
+ })
260
+
261
+ describe('helpers - hierarchy traversal', () => {
262
+ const systems: Record<string, OrganizationModelSystemEntry> = {
263
+ a: makeSystem('a'),
264
+ 'a.b': makeSystem('a.b'),
265
+ 'a.b.c': makeSystem('a.b.c'),
266
+ x: makeSystem('x')
267
+ }
268
+
269
+ it('ancestorsOf returns all path-segment ancestors including self in root-to-leaf order', () => {
270
+ expect(ancestorsOf(systems, 'a.b.c').map((s) => s.id)).toEqual(['a', 'a.b', 'a.b.c'])
271
+ })
272
+
273
+ it('childrenOf returns only direct children, not grandchildren', () => {
274
+ expect(childrenOf(systems, 'a').map((s) => s.id)).toEqual(['a.b'])
275
+ })
276
+
277
+ it('topLevel returns only root-level systems with no dot in their id', () => {
278
+ expect(
279
+ topLevel(systems)
280
+ .map((s) => s.id)
281
+ .sort()
282
+ ).toEqual(['a', 'x'])
283
+ })
284
+ })
@@ -0,0 +1,144 @@
1
+ // ============================================================================
2
+ // Lead-Gen Stage Catalog (OM Spine processing-state model)
3
+ //
4
+ // Canonical set of processing stage keys for acq_companies.processing_state and
5
+ // acq_contacts.processing_state. These keys coordinate build templates, workflow
6
+ // factory validation, API filters, and UI progress projections.
7
+ //
8
+ // State is sparse: absent keys mean "not attempted"; present keys hold terminal
9
+ // status entries such as success, no_result, skipped, or error.
10
+ //
11
+ // Historical sources:
12
+ // ACQ_LIST_MEMBERS_LEAD_GEN_PIPELINE -> personalized, uploaded, interested,
13
+ // discovered, verified
14
+ // ACQ_LIST_COMPANIES_LEAD_GEN_PIPELINE -> populated, extracted, qualified
15
+ // Design plan hint (lead-gen-domain-cleanup.mdx section 4) -> scraped, enriched
16
+ //
17
+ // ============================================================================
18
+
19
+ /** One entry in the lead-gen stage catalog. */
20
+ export interface LeadGenStageCatalogEntry {
21
+ /** Matches the status key written into processing_state jsonb (e.g. 'scraped'). */
22
+ key: string
23
+ /** Human-readable label for UI display. */
24
+ label: string
25
+ /** Short description of what this stage represents. */
26
+ description: string
27
+ /** Canonical pipeline order for UI sorting. Lower = earlier in the funnel. */
28
+ order: number
29
+ /** Which entity's processing_state jsonb carries this stage status. */
30
+ entity: 'company' | 'contact'
31
+ /** Additional entities allowed to write/read this processing_state key. */
32
+ additionalEntities?: Array<'company' | 'contact'>
33
+ /**
34
+ * Optional read-side override for Records views when a company-scoped step
35
+ * produces records on a different entity.
36
+ */
37
+ recordEntity?: 'company' | 'contact'
38
+ /** Stage key to read from recordEntity.processing_state for Records views. */
39
+ recordStageKey?: string
40
+ }
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
+ }
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod'
2
+ import { defineContentType } from './registry'
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // config kinds (Phase 3, Wave 2A)
6
+ // ---------------------------------------------------------------------------
7
+ //
8
+ // `config:kv` — a flat key-value configuration store co-located with a system.
9
+ // Per L15: single starter config type; feature-flag semantics are
10
+ // a usage pattern over kv, not a separate type.
11
+ //
12
+ // Per L15: `config` is a first-class registered meta-kind. Scope: NEW
13
+ // tenant-defined settings co-located with a system. Strongly-typed platform
14
+ // fields (system.ui.devOnly, system.ui.requiresAdmin, etc.) stay where they
15
+ // are and are NOT hoisting destinations for existing typed fields.
16
+
17
+ const ConfigKvPayloadSchema = z.object({
18
+ /**
19
+ * Flat key-value entries. Values are JSON primitives.
20
+ * Keys are short identifiers (e.g. 'maxBatchSize', 'featureEnabled').
21
+ */
22
+ entries: z
23
+ .record(z.string().trim().min(1).max(200), z.union([z.string(), z.number(), z.boolean(), z.null()]))
24
+ .meta({ label: 'Entries', hint: 'Key-value configuration entries (string, number, boolean, or null values)' })
25
+ })
26
+
27
+ export type ConfigKvPayload = z.infer<typeof ConfigKvPayloadSchema>
28
+
29
+ export const configKvKind = defineContentType({
30
+ kind: 'config',
31
+ type: 'kv',
32
+ label: 'Key-Value Config',
33
+ description: 'A flat key-value configuration store co-located with a system. Values are JSON primitives.',
34
+ payloadSchema: ConfigKvPayloadSchema,
35
+ parentTypes: []
36
+ })