@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
@@ -15,6 +15,7 @@ describe('CustomerSegmentSchema — positive parse', () => {
15
15
  it('accepts a fully-populated segment', () => {
16
16
  const result = CustomerSegmentSchema.safeParse({
17
17
  id: 'segment-smb-agencies',
18
+ order: 10,
18
19
  name: 'SMB Marketing Agencies',
19
20
  description: 'Small marketing agencies with 1–50 employees seeking automation.',
20
21
  jobsToBeDone: 'Automate client reporting and lead generation workflows.',
@@ -30,8 +31,8 @@ describe('CustomerSegmentSchema — positive parse', () => {
30
31
  expect(result.success).toBe(true)
31
32
  })
32
33
 
33
- it('accepts a minimal segment — only id required', () => {
34
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-minimal' })
34
+ it('accepts a minimal segment — only id and order required', () => {
35
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-minimal', order: 10 })
35
36
  expect(result.success).toBe(true)
36
37
  if (result.success) {
37
38
  expect(result.data.id).toBe('seg-minimal')
@@ -48,6 +49,7 @@ describe('CustomerSegmentSchema — positive parse', () => {
48
49
  it('trims whitespace from string fields', () => {
49
50
  const result = CustomerSegmentSchema.safeParse({
50
51
  id: ' seg-trim ',
52
+ order: 10,
51
53
  name: ' Trimmed Name ',
52
54
  valueProp: ' Trimmed value prop. '
53
55
  })
@@ -62,6 +64,7 @@ describe('CustomerSegmentSchema — positive parse', () => {
62
64
  it('accepts pains and gains as non-empty arrays', () => {
63
65
  const result = CustomerSegmentSchema.safeParse({
64
66
  id: 'seg-arrays',
67
+ order: 10,
65
68
  pains: ['Pain A', 'Pain B', 'Pain C'],
66
69
  gains: ['Gain X', 'Gain Y']
67
70
  })
@@ -79,7 +82,7 @@ describe('CustomerSegmentSchema — positive parse', () => {
79
82
 
80
83
  describe('CustomerSegmentSchema — default values', () => {
81
84
  it('pains defaults to empty array', () => {
82
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-defaults' })
85
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-defaults', order: 10 })
83
86
  expect(result.success).toBe(true)
84
87
  if (result.success) {
85
88
  expect(result.data.pains).toEqual([])
@@ -87,7 +90,7 @@ describe('CustomerSegmentSchema — default values', () => {
87
90
  })
88
91
 
89
92
  it('gains defaults to empty array', () => {
90
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-defaults' })
93
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-defaults', order: 10 })
91
94
  expect(result.success).toBe(true)
92
95
  if (result.success) {
93
96
  expect(result.data.gains).toEqual([])
@@ -95,7 +98,7 @@ describe('CustomerSegmentSchema — default values', () => {
95
98
  })
96
99
 
97
100
  it('firmographics defaults to empty object', () => {
98
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-defaults' })
101
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-defaults', order: 10 })
99
102
  expect(result.success).toBe(true)
100
103
  if (result.success) {
101
104
  expect(result.data.firmographics).toEqual({})
@@ -103,7 +106,7 @@ describe('CustomerSegmentSchema — default values', () => {
103
106
  })
104
107
 
105
108
  it('all string fields default to empty string', () => {
106
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-str-defaults' })
109
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-str-defaults', order: 10 })
107
110
  expect(result.success).toBe(true)
108
111
  if (result.success) {
109
112
  expect(result.data.name).toBe('')
@@ -120,42 +123,42 @@ describe('CustomerSegmentSchema — default values', () => {
120
123
 
121
124
  describe('CustomerSegmentSchema — negative parse', () => {
122
125
  it('rejects missing id', () => {
123
- const result = CustomerSegmentSchema.safeParse({ name: 'No ID Segment' })
126
+ const result = CustomerSegmentSchema.safeParse({ order: 10, name: 'No ID Segment' })
124
127
  expect(result.success).toBe(false)
125
128
  })
126
129
 
127
130
  it('rejects empty string id', () => {
128
- const result = CustomerSegmentSchema.safeParse({ id: '' })
131
+ const result = CustomerSegmentSchema.safeParse({ id: '', order: 10 })
129
132
  expect(result.success).toBe(false)
130
133
  })
131
134
 
132
135
  it('rejects whitespace-only id (trims to empty string)', () => {
133
- const result = CustomerSegmentSchema.safeParse({ id: ' ' })
136
+ const result = CustomerSegmentSchema.safeParse({ id: ' ', order: 10 })
134
137
  expect(result.success).toBe(false)
135
138
  })
136
139
 
137
140
  it('rejects id as a number', () => {
138
- const result = CustomerSegmentSchema.safeParse({ id: 42 })
141
+ const result = CustomerSegmentSchema.safeParse({ id: 42, order: 10 })
139
142
  expect(result.success).toBe(false)
140
143
  })
141
144
 
142
145
  it('rejects pains as a non-array value', () => {
143
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-bad-pains', pains: 'not an array' })
146
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-bad-pains', order: 10, pains: 'not an array' })
144
147
  expect(result.success).toBe(false)
145
148
  })
146
149
 
147
150
  it('rejects gains containing a non-string element', () => {
148
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-bad-gains', gains: [42, 'valid'] })
151
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-bad-gains', order: 10, gains: [42, 'valid'] })
149
152
  expect(result.success).toBe(false)
150
153
  })
151
154
 
152
155
  it('rejects name exceeding 200 characters', () => {
153
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-long-name', name: 'x'.repeat(201) })
156
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-long-name', order: 10, name: 'x'.repeat(201) })
154
157
  expect(result.success).toBe(false)
155
158
  })
156
159
 
157
160
  it('rejects description exceeding 2000 characters', () => {
158
- const result = CustomerSegmentSchema.safeParse({ id: 'seg-long-desc', description: 'x'.repeat(2001) })
161
+ const result = CustomerSegmentSchema.safeParse({ id: 'seg-long-desc', order: 10, description: 'x'.repeat(2001) })
159
162
  expect(result.success).toBe(false)
160
163
  })
161
164
  })
@@ -205,34 +208,34 @@ describe('FirmographicsSchema', () => {
205
208
  // ---------------------------------------------------------------------------
206
209
 
207
210
  describe('CustomersDomainSchema — structural', () => {
208
- it('accepts an empty segments array', () => {
209
- const result = CustomersDomainSchema.safeParse({ segments: [] })
211
+ it('accepts an empty segments record', () => {
212
+ const result = CustomersDomainSchema.safeParse({})
210
213
  expect(result.success).toBe(true)
211
214
  })
212
215
 
213
- it('defaults segments to empty array when key is omitted', () => {
216
+ it('defaults segments to empty record when omitted', () => {
214
217
  const result = CustomersDomainSchema.safeParse({})
215
218
  expect(result.success).toBe(true)
216
219
  if (result.success) {
217
- expect(result.data.segments).toEqual([])
220
+ expect(result.data).toEqual({})
218
221
  }
219
222
  })
220
223
 
221
224
  it('accepts multiple valid segments', () => {
222
225
  const result = CustomersDomainSchema.safeParse({
223
- segments: [
224
- { id: 'seg-a', name: 'Segment A' },
225
- { id: 'seg-b', name: 'Segment B', pains: ['Pain 1'] }
226
- ]
226
+ 'seg-a': { id: 'seg-a', order: 10, name: 'Segment A' },
227
+ 'seg-b': { id: 'seg-b', order: 20, name: 'Segment B', pains: ['Pain 1'] }
227
228
  })
228
229
  expect(result.success).toBe(true)
229
230
  if (result.success) {
230
- expect(result.data.segments).toHaveLength(2)
231
+ expect(Object.keys(result.data)).toHaveLength(2)
231
232
  }
232
233
  })
233
234
 
234
- it('rejects segments as a non-array value', () => {
235
- const result = CustomersDomainSchema.safeParse({ segments: 'not an array' })
235
+ it('rejects a record where entry id does not match map key', () => {
236
+ const result = CustomersDomainSchema.safeParse({
237
+ 'seg-a': { id: 'seg-b', order: 10, name: 'Mismatched' }
238
+ })
236
239
  expect(result.success).toBe(false)
237
240
  })
238
241
 
@@ -252,44 +255,45 @@ describe('CustomersDomainSchema — structural', () => {
252
255
  describe('resolveOrganizationModel — customers domain integration', () => {
253
256
  it('merges partial customers override and preserves empty segments default', () => {
254
257
  const model = resolveOrganizationModel({
255
- customers: { segments: [] }
258
+ customers: {}
256
259
  })
257
- expect(model.customers.segments).toEqual([])
260
+ expect(Object.keys(model.customers)).toHaveLength(0)
258
261
  })
259
262
 
260
263
  it('merges a customers override with populated segments into resolved model', () => {
261
264
  const model = resolveOrganizationModel({
262
265
  customers: {
263
- segments: [
264
- {
265
- id: 'seg-agencies',
266
- name: 'Agency Owners',
267
- pains: ['Too much manual work'],
268
- gains: ['More client capacity'],
269
- valueProp: 'Automate the repetitive parts of agency ops.'
270
- }
271
- ]
266
+ 'seg-agencies': {
267
+ id: 'seg-agencies',
268
+ order: 10,
269
+ name: 'Agency Owners',
270
+ pains: ['Too much manual work'],
271
+ gains: ['More client capacity'],
272
+ valueProp: 'Automate the repetitive parts of agency ops.'
273
+ }
272
274
  }
273
275
  })
274
- expect(model.customers.segments).toHaveLength(1)
275
- expect(model.customers.segments[0].id).toBe('seg-agencies')
276
- expect(model.customers.segments[0].pains).toEqual(['Too much manual work'])
277
- expect(model.customers.segments[0].description).toBe('')
276
+ expect(Object.keys(model.customers)).toHaveLength(1)
277
+ expect(model.customers['seg-agencies'].id).toBe('seg-agencies')
278
+ expect(model.customers['seg-agencies'].pains).toEqual(['Too much manual work'])
279
+ expect(model.customers['seg-agencies'].description).toBe('')
278
280
  })
279
281
 
280
282
  it('does not bleed customers changes into other top-level domains', () => {
281
283
  const model = resolveOrganizationModel({
282
- customers: { segments: [{ id: 'seg-isolated' }] }
284
+ customers: { 'seg-isolated': { id: 'seg-isolated', order: 10 } }
283
285
  })
284
286
  expect(model.identity).toBeDefined()
285
- expect(model.features).toBeDefined()
286
- expect(model.statuses).toBeDefined()
287
+ expect(model.systems).toBeDefined()
288
+ // Phase 4 (D1, D8): model.statuses and model.navigation removed from top-level OM.
289
+ // Verify other sibling domains are still intact instead.
290
+ expect(model.knowledge).toBeDefined()
287
291
  expect(model.navigation).toBeDefined()
288
292
  })
289
293
 
290
294
  it('omitting customers key entirely resolves to default empty segments', () => {
291
295
  const model = resolveOrganizationModel({})
292
296
  expect(model.customers).toBeDefined()
293
- expect(model.customers.segments).toEqual([])
297
+ expect(Object.keys(model.customers)).toHaveLength(0)
294
298
  })
295
299
  })
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ DEFAULT_ORGANIZATION_MODEL_ENTITIES,
4
+ EntitiesDomainSchema,
5
+ EntityLinkSchema,
6
+ EntitySchema
7
+ } from '../../domains/entities'
8
+
9
+ describe('entities domain', () => {
10
+ it('defaults the initial entity catalog with ordered entries', () => {
11
+ const domain = EntitiesDomainSchema.parse(DEFAULT_ORGANIZATION_MODEL_ENTITIES)
12
+
13
+ expect(Object.keys(domain)).toEqual([
14
+ 'crm.deal',
15
+ 'crm.contact',
16
+ 'leadgen.list',
17
+ 'leadgen.company',
18
+ 'leadgen.contact',
19
+ 'delivery.project',
20
+ 'delivery.milestone',
21
+ 'delivery.task'
22
+ ])
23
+ expect(Object.values(domain).map((entity) => entity.order)).toEqual([10, 20, 30, 40, 50, 60, 70, 80])
24
+ expect(domain['crm.deal']).toMatchObject({
25
+ id: 'crm.deal',
26
+ label: 'Deal',
27
+ ownedBySystemId: 'sales.crm'
28
+ })
29
+ })
30
+
31
+ it('enforces map key equals entity id', () => {
32
+ expect(() =>
33
+ EntitiesDomainSchema.parse({
34
+ 'crm.deal': {
35
+ id: 'crm.contact',
36
+ order: 10,
37
+ label: 'Deal',
38
+ ownedBySystemId: 'sales.crm'
39
+ }
40
+ })
41
+ ).toThrow(/Each entity entry id must match its map key/)
42
+ })
43
+
44
+ it('accepts declared entity links', () => {
45
+ expect(() => EntityLinkSchema.parse({ toEntity: 'crm.contact', kind: 'has-many' })).not.toThrow()
46
+ expect(() =>
47
+ EntitySchema.parse({
48
+ id: 'crm.deal',
49
+ order: 10,
50
+ label: 'Deal',
51
+ ownedBySystemId: 'sales.crm',
52
+ links: [{ toEntity: 'crm.contact', kind: 'belongs-to', via: 'contact_id', label: 'contact' }]
53
+ })
54
+ ).not.toThrow()
55
+ })
56
+ })
@@ -102,6 +102,7 @@ describe('ObjectiveSchema — positive parse', () => {
102
102
  it('accepts a fully-populated objective with measurable outcomes', () => {
103
103
  const result = ObjectiveSchema.safeParse({
104
104
  id: 'goal-grow-arr-q1-2026',
105
+ order: 10,
105
106
  description: 'Grow annual recurring revenue by 50% in Q1 2026',
106
107
  periodStart: '2026-01-01',
107
108
  periodEnd: '2026-03-31',
@@ -126,6 +127,7 @@ describe('ObjectiveSchema — positive parse', () => {
126
127
  it('accepts a minimal objective — no measurable outcomes', () => {
127
128
  const result = ObjectiveSchema.safeParse({
128
129
  id: 'goal-minimal',
130
+ order: 10,
129
131
  description: 'Establish brand presence in new market',
130
132
  periodStart: '2026-04-01',
131
133
  periodEnd: '2026-06-30'
@@ -139,6 +141,7 @@ describe('ObjectiveSchema — positive parse', () => {
139
141
  it('keyResults defaults to empty array when omitted', () => {
140
142
  const result = ObjectiveSchema.safeParse({
141
143
  id: 'goal-defaults',
144
+ order: 10,
142
145
  description: 'Launch new product line',
143
146
  periodStart: '2026-01-01',
144
147
  periodEnd: '2026-12-31'
@@ -157,6 +160,7 @@ describe('ObjectiveSchema — positive parse', () => {
157
160
  describe('ObjectiveSchema — negative parse', () => {
158
161
  it('rejects missing id', () => {
159
162
  const result = ObjectiveSchema.safeParse({
163
+ order: 10,
160
164
  description: 'No id provided',
161
165
  periodStart: '2026-01-01',
162
166
  periodEnd: '2026-12-31'
@@ -167,6 +171,7 @@ describe('ObjectiveSchema — negative parse', () => {
167
171
  it('rejects empty string id', () => {
168
172
  const result = ObjectiveSchema.safeParse({
169
173
  id: '',
174
+ order: 10,
170
175
  description: 'Empty id',
171
176
  periodStart: '2026-01-01',
172
177
  periodEnd: '2026-12-31'
@@ -177,6 +182,7 @@ describe('ObjectiveSchema — negative parse', () => {
177
182
  it('rejects missing description', () => {
178
183
  const result = ObjectiveSchema.safeParse({
179
184
  id: 'goal-no-desc',
185
+ order: 10,
180
186
  periodStart: '2026-01-01',
181
187
  periodEnd: '2026-12-31'
182
188
  })
@@ -186,6 +192,7 @@ describe('ObjectiveSchema — negative parse', () => {
186
192
  it('rejects empty string description', () => {
187
193
  const result = ObjectiveSchema.safeParse({
188
194
  id: 'goal-empty-desc',
195
+ order: 10,
189
196
  description: '',
190
197
  periodStart: '2026-01-01',
191
198
  periodEnd: '2026-12-31'
@@ -196,6 +203,7 @@ describe('ObjectiveSchema — negative parse', () => {
196
203
  it('rejects periodStart with wrong date format (non-ISO string)', () => {
197
204
  const result = ObjectiveSchema.safeParse({
198
205
  id: 'goal-bad-start',
206
+ order: 10,
199
207
  description: 'Bad date format',
200
208
  periodStart: '01/01/2026',
201
209
  periodEnd: '2026-12-31'
@@ -206,6 +214,7 @@ describe('ObjectiveSchema — negative parse', () => {
206
214
  it('rejects periodEnd with wrong date format (non-ISO string)', () => {
207
215
  const result = ObjectiveSchema.safeParse({
208
216
  id: 'goal-bad-end',
217
+ order: 10,
209
218
  description: 'Bad date format',
210
219
  periodStart: '2026-01-01',
211
220
  periodEnd: 'December 31, 2026'
@@ -216,6 +225,7 @@ describe('ObjectiveSchema — negative parse', () => {
216
225
  it('rejects periodStart as a Date object (must be ISO string)', () => {
217
226
  const result = ObjectiveSchema.safeParse({
218
227
  id: 'goal-date-obj',
228
+ order: 10,
219
229
  description: 'Date object not allowed',
220
230
  periodStart: new Date('2026-01-01'),
221
231
  periodEnd: '2026-12-31'
@@ -229,16 +239,16 @@ describe('ObjectiveSchema — negative parse', () => {
229
239
  // ---------------------------------------------------------------------------
230
240
 
231
241
  describe('GoalsDomainSchema — structural', () => {
232
- it('accepts an empty objectives array', () => {
233
- const result = GoalsDomainSchema.safeParse({ objectives: [] })
242
+ it('accepts an empty goals record', () => {
243
+ const result = GoalsDomainSchema.safeParse({})
234
244
  expect(result.success).toBe(true)
235
245
  })
236
246
 
237
- it('defaults objectives to empty array when key is omitted', () => {
247
+ it('defaults goals to empty record when key is omitted', () => {
238
248
  const result = GoalsDomainSchema.safeParse({})
239
249
  expect(result.success).toBe(true)
240
250
  if (result.success) {
241
- expect(result.data.objectives).toEqual([])
251
+ expect(result.data).toEqual({})
242
252
  }
243
253
  })
244
254
 
@@ -252,14 +262,24 @@ describe('GoalsDomainSchema — structural', () => {
252
262
 
253
263
  it('accepts multiple valid objectives', () => {
254
264
  const result = GoalsDomainSchema.safeParse({
255
- objectives: [
256
- { id: 'goal-a', description: 'First goal', periodStart: '2026-01-01', periodEnd: '2026-03-31' },
257
- { id: 'goal-b', description: 'Second goal', periodStart: '2026-04-01', periodEnd: '2026-06-30' }
258
- ]
265
+ 'goal-a': {
266
+ id: 'goal-a',
267
+ order: 10,
268
+ description: 'First goal',
269
+ periodStart: '2026-01-01',
270
+ periodEnd: '2026-03-31'
271
+ },
272
+ 'goal-b': {
273
+ id: 'goal-b',
274
+ order: 20,
275
+ description: 'Second goal',
276
+ periodStart: '2026-04-01',
277
+ periodEnd: '2026-06-30'
278
+ }
259
279
  })
260
280
  expect(result.success).toBe(true)
261
281
  if (result.success) {
262
- expect(result.data.objectives).toHaveLength(2)
282
+ expect(Object.keys(result.data)).toHaveLength(2)
263
283
  }
264
284
  })
265
285
  })
@@ -274,6 +294,7 @@ describe('ObjectiveSchema — plain-language field names (no OKR jargon exposed)
274
294
  it('parsed objective has expected field names including keyResults for tooling compat', () => {
275
295
  const result = ObjectiveSchema.safeParse({
276
296
  id: 'goal-plain',
297
+ order: 10,
277
298
  description: 'Expand into new markets',
278
299
  periodStart: '2026-01-01',
279
300
  periodEnd: '2026-12-31',
@@ -302,14 +323,13 @@ describe('resolveOrganizationModel — goals period-range validation', () => {
302
323
  expect(() =>
303
324
  resolveOrganizationModel({
304
325
  goals: {
305
- objectives: [
306
- {
307
- id: 'goal-valid',
308
- description: 'Valid date range',
309
- periodStart: '2026-01-01',
310
- periodEnd: '2026-12-31'
311
- }
312
- ]
326
+ 'goal-valid': {
327
+ id: 'goal-valid',
328
+ order: 10,
329
+ description: 'Valid date range',
330
+ periodStart: '2026-01-01',
331
+ periodEnd: '2026-12-31'
332
+ }
313
333
  }
314
334
  })
315
335
  ).not.toThrow()
@@ -319,14 +339,13 @@ describe('resolveOrganizationModel — goals period-range validation', () => {
319
339
  expect(() =>
320
340
  resolveOrganizationModel({
321
341
  goals: {
322
- objectives: [
323
- {
324
- id: 'goal-one-day',
325
- description: 'Single day range',
326
- periodStart: '2026-06-01',
327
- periodEnd: '2026-06-02'
328
- }
329
- ]
342
+ 'goal-one-day': {
343
+ id: 'goal-one-day',
344
+ order: 10,
345
+ description: 'Single day range',
346
+ periodStart: '2026-06-01',
347
+ periodEnd: '2026-06-02'
348
+ }
330
349
  }
331
350
  })
332
351
  ).not.toThrow()
@@ -336,14 +355,13 @@ describe('resolveOrganizationModel — goals period-range validation', () => {
336
355
  expect(() =>
337
356
  resolveOrganizationModel({
338
357
  goals: {
339
- objectives: [
340
- {
341
- id: 'goal-same-date',
342
- description: 'Same start and end',
343
- periodStart: '2026-03-01',
344
- periodEnd: '2026-03-01'
345
- }
346
- ]
358
+ 'goal-same-date': {
359
+ id: 'goal-same-date',
360
+ order: 10,
361
+ description: 'Same start and end',
362
+ periodStart: '2026-03-01',
363
+ periodEnd: '2026-03-01'
364
+ }
347
365
  }
348
366
  })
349
367
  ).toThrow()
@@ -353,14 +371,13 @@ describe('resolveOrganizationModel — goals period-range validation', () => {
353
371
  expect(() =>
354
372
  resolveOrganizationModel({
355
373
  goals: {
356
- objectives: [
357
- {
358
- id: 'goal-reversed',
359
- description: 'Reversed date range',
360
- periodStart: '2026-12-31',
361
- periodEnd: '2026-01-01'
362
- }
363
- ]
374
+ 'goal-reversed': {
375
+ id: 'goal-reversed',
376
+ order: 10,
377
+ description: 'Reversed date range',
378
+ periodStart: '2026-12-31',
379
+ periodEnd: '2026-01-01'
380
+ }
364
381
  }
365
382
  })
366
383
  ).toThrow()
@@ -371,14 +388,13 @@ describe('resolveOrganizationModel — goals period-range validation', () => {
371
388
  try {
372
389
  resolveOrganizationModel({
373
390
  goals: {
374
- objectives: [
375
- {
376
- id: 'goal-bad-range',
377
- description: 'Bad range for error message check',
378
- periodStart: '2026-06-01',
379
- periodEnd: '2026-01-01'
380
- }
381
- ]
391
+ 'goal-bad-range': {
392
+ id: 'goal-bad-range',
393
+ order: 10,
394
+ description: 'Bad range for error message check',
395
+ periodStart: '2026-06-01',
396
+ periodEnd: '2026-01-01'
397
+ }
382
398
  }
383
399
  })
384
400
  } catch (e) {
@@ -387,10 +403,10 @@ describe('resolveOrganizationModel — goals period-range validation', () => {
387
403
  expect(errorMessage).toContain('periodEnd')
388
404
  })
389
405
 
390
- it('passes when goals objectives array is empty (no ranges to validate)', () => {
406
+ it('passes when goals record is empty (no ranges to validate)', () => {
391
407
  expect(() =>
392
408
  resolveOrganizationModel({
393
- goals: { objectives: [] }
409
+ goals: {}
394
410
  })
395
411
  ).not.toThrow()
396
412
  })
@@ -401,79 +417,77 @@ describe('resolveOrganizationModel — goals period-range validation', () => {
401
417
  // ---------------------------------------------------------------------------
402
418
 
403
419
  describe('resolveOrganizationModel — goals domain integration', () => {
404
- it('omitting goals key entirely resolves to default empty objectives', () => {
420
+ it('omitting goals key entirely resolves to default empty goals', () => {
405
421
  const model = resolveOrganizationModel({})
406
422
  expect(model.goals).toBeDefined()
407
- expect(model.goals.objectives).toEqual([])
423
+ expect(Object.keys(model.goals)).toHaveLength(0)
408
424
  })
409
425
 
410
- it('merges partial goals override and preserves empty objectives default', () => {
411
- const model = resolveOrganizationModel({ goals: { objectives: [] } })
412
- expect(model.goals.objectives).toEqual([])
426
+ it('merges partial goals override and preserves empty goals default', () => {
427
+ const model = resolveOrganizationModel({ goals: {} })
428
+ expect(Object.keys(model.goals)).toHaveLength(0)
413
429
  })
414
430
 
415
431
  it('goals domain is registered after roles — both present in resolved model', () => {
416
432
  const model = resolveOrganizationModel({
417
- roles: { roles: [{ id: 'role-ceo', title: 'CEO' }] },
433
+ roles: { 'role-ceo': { id: 'role-ceo', order: 10, title: 'CEO' } },
418
434
  goals: {
419
- objectives: [
420
- {
421
- id: 'goal-grow',
422
- description: 'Grow the business',
423
- periodStart: '2026-01-01',
424
- periodEnd: '2026-12-31'
425
- }
426
- ]
435
+ 'goal-grow': {
436
+ id: 'goal-grow',
437
+ order: 10,
438
+ description: 'Grow the business',
439
+ periodStart: '2026-01-01',
440
+ periodEnd: '2026-12-31'
441
+ }
427
442
  }
428
443
  })
429
- expect(model.roles.roles).toHaveLength(1)
430
- expect(model.goals.objectives).toHaveLength(1)
431
- expect(model.goals.objectives[0].id).toBe('goal-grow')
444
+ expect(Object.keys(model.roles)).toHaveLength(1)
445
+ expect(Object.keys(model.goals)).toHaveLength(1)
446
+ expect(model.goals['goal-grow'].id).toBe('goal-grow')
432
447
  })
433
448
 
434
449
  it('does not bleed goals changes into other top-level domains', () => {
435
450
  const model = resolveOrganizationModel({
436
451
  goals: {
437
- objectives: [
438
- {
439
- id: 'goal-isolated',
440
- description: 'Should not affect other domains',
441
- periodStart: '2026-01-01',
442
- periodEnd: '2026-06-30'
443
- }
444
- ]
452
+ 'goal-isolated': {
453
+ id: 'goal-isolated',
454
+ order: 10,
455
+ description: 'Should not affect other domains',
456
+ periodStart: '2026-01-01',
457
+ periodEnd: '2026-06-30'
458
+ }
445
459
  }
446
460
  })
447
461
  expect(model.identity).toBeDefined()
448
462
  expect(model.customers).toBeDefined()
449
463
  expect(model.offerings).toBeDefined()
450
464
  expect(model.roles).toBeDefined()
451
- expect(model.features).toBeDefined()
465
+ expect(model.systems).toBeDefined()
452
466
  })
453
467
 
454
468
  it('resolved objective carries keyResults with defaulted currentValue', () => {
455
469
  const model = resolveOrganizationModel({
456
470
  goals: {
457
- objectives: [
458
- {
459
- id: 'goal-with-kr',
460
- description: 'Goal with measurable outcomes',
461
- periodStart: '2026-01-01',
462
- periodEnd: '2026-12-31',
463
- keyResults: [
464
- {
465
- id: 'kr-1',
466
- description: 'Hit revenue target',
467
- targetMetric: 'monthly revenue',
468
- targetValue: 50000
469
- }
470
- ]
471
- }
472
- ]
471
+ 'goal-with-kr': {
472
+ id: 'goal-with-kr',
473
+ order: 10,
474
+ description: 'Goal with measurable outcomes',
475
+ periodStart: '2026-01-01',
476
+ periodEnd: '2026-12-31',
477
+ keyResults: [
478
+ {
479
+ id: 'kr-1',
480
+ description: 'Hit revenue target',
481
+ targetMetric: 'monthly revenue',
482
+ targetValue: 50000
483
+ }
484
+ ]
485
+ }
473
486
  }
474
487
  })
475
- expect(model.goals.objectives[0].keyResults).toHaveLength(1)
476
- expect(model.goals.objectives[0].keyResults[0].currentValue).toBe(0)
477
- expect(model.goals.objectives[0].keyResults[0].targetValue).toBe(50000)
488
+ const objective = model.goals['goal-with-kr']
489
+ expect(objective.keyResults).toHaveLength(1)
490
+ expect(objective.keyResults[0].currentValue).toBe(0)
491
+ expect(objective.keyResults[0].targetValue).toBe(50000)
478
492
  })
479
493
  })