@elevasis/core 0.21.0 → 0.23.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 (132) hide show
  1. package/dist/index.d.ts +2518 -2169
  2. package/dist/index.js +2495 -1095
  3. package/dist/knowledge/index.d.ts +706 -1044
  4. package/dist/knowledge/index.js +9 -9
  5. package/dist/organization-model/index.d.ts +2518 -2169
  6. package/dist/organization-model/index.js +2495 -1095
  7. package/dist/test-utils/index.d.ts +826 -1014
  8. package/dist/test-utils/index.js +1894 -1032
  9. package/package.json +3 -3
  10. package/src/__tests__/template-core-compatibility.test.ts +11 -79
  11. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +852 -397
  12. package/src/auth/multi-tenancy/permissions.ts +20 -8
  13. package/src/business/README.md +2 -2
  14. package/src/business/acquisition/api-schemas.test.ts +175 -2
  15. package/src/business/acquisition/api-schemas.ts +132 -16
  16. package/src/business/acquisition/build-templates.test.ts +4 -4
  17. package/src/business/acquisition/build-templates.ts +72 -30
  18. package/src/business/acquisition/crm-state-actions.test.ts +13 -11
  19. package/src/business/acquisition/index.ts +12 -0
  20. package/src/business/acquisition/types.ts +7 -3
  21. package/src/business/clients/api-schemas.test.ts +115 -0
  22. package/src/business/clients/api-schemas.ts +158 -0
  23. package/src/business/clients/index.ts +1 -0
  24. package/src/business/deals/api-schemas.ts +8 -0
  25. package/src/business/index.ts +5 -2
  26. package/src/business/projects/types.ts +19 -0
  27. package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
  28. package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
  29. package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
  30. package/src/execution/engine/agent/core/types.ts +25 -15
  31. package/src/execution/engine/agent/index.ts +6 -4
  32. package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
  33. package/src/execution/engine/index.ts +3 -0
  34. package/src/execution/engine/workflow/types.ts +9 -2
  35. package/src/knowledge/README.md +8 -7
  36. package/src/knowledge/__tests__/queries.test.ts +74 -73
  37. package/src/knowledge/format.ts +10 -9
  38. package/src/knowledge/index.ts +1 -1
  39. package/src/knowledge/published.ts +1 -1
  40. package/src/knowledge/queries.ts +26 -25
  41. package/src/organization-model/README.md +73 -26
  42. package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
  43. package/src/organization-model/__tests__/defaults.test.ts +76 -96
  44. package/src/organization-model/__tests__/domains/actions.test.ts +56 -0
  45. package/src/organization-model/__tests__/domains/customers.test.ts +299 -295
  46. package/src/organization-model/__tests__/domains/entities.test.ts +56 -0
  47. package/src/organization-model/__tests__/domains/goals.test.ts +493 -479
  48. package/src/organization-model/__tests__/domains/identity.test.ts +280 -279
  49. package/src/organization-model/__tests__/domains/navigation.test.ts +268 -212
  50. package/src/organization-model/__tests__/domains/offerings.test.ts +414 -419
  51. package/src/organization-model/__tests__/domains/policies.test.ts +323 -0
  52. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +271 -271
  53. package/src/organization-model/__tests__/domains/resources.test.ts +310 -0
  54. package/src/organization-model/__tests__/domains/roles.test.ts +463 -347
  55. package/src/organization-model/__tests__/domains/statuses.test.ts +246 -243
  56. package/src/organization-model/__tests__/domains/systems.test.ts +209 -0
  57. package/src/organization-model/__tests__/flatten-additive-merge.test.ts +361 -0
  58. package/src/organization-model/__tests__/foundation.test.ts +74 -102
  59. package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -0
  60. package/src/organization-model/__tests__/graph.test.ts +899 -71
  61. package/src/organization-model/__tests__/knowledge.test.ts +209 -49
  62. package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -0
  63. package/src/organization-model/__tests__/migration-helpers.test.ts +591 -0
  64. package/src/organization-model/__tests__/prospecting-ssot.test.ts +36 -27
  65. package/src/organization-model/__tests__/recursive-system-schema.test.ts +520 -0
  66. package/src/organization-model/__tests__/resolve.test.ts +174 -23
  67. package/src/organization-model/__tests__/schema.test.ts +291 -114
  68. package/src/organization-model/__tests__/surface-projection.test.ts +207 -97
  69. package/src/organization-model/catalogs/lead-gen.ts +144 -0
  70. package/src/organization-model/content-kinds/config.ts +36 -0
  71. package/src/organization-model/content-kinds/index.ts +74 -0
  72. package/src/organization-model/content-kinds/pipeline.ts +68 -0
  73. package/src/organization-model/content-kinds/registry.ts +44 -0
  74. package/src/organization-model/content-kinds/status.ts +71 -0
  75. package/src/organization-model/content-kinds/template.ts +83 -0
  76. package/src/organization-model/content-kinds/types.ts +117 -0
  77. package/src/organization-model/contracts.ts +13 -3
  78. package/src/organization-model/defaults.ts +499 -86
  79. package/src/organization-model/domains/actions.ts +239 -0
  80. package/src/organization-model/domains/customers.ts +78 -75
  81. package/src/organization-model/domains/entities.ts +144 -0
  82. package/src/organization-model/domains/goals.ts +83 -80
  83. package/src/organization-model/domains/knowledge.ts +76 -17
  84. package/src/organization-model/domains/navigation.ts +107 -384
  85. package/src/organization-model/domains/offerings.ts +71 -66
  86. package/src/organization-model/domains/policies.ts +102 -0
  87. package/src/organization-model/domains/projects.ts +14 -48
  88. package/src/organization-model/domains/prospecting.ts +62 -181
  89. package/src/organization-model/domains/resources.ts +145 -0
  90. package/src/organization-model/domains/roles.ts +96 -55
  91. package/src/organization-model/domains/sales.ts +10 -219
  92. package/src/organization-model/domains/shared.ts +57 -57
  93. package/src/organization-model/domains/statuses.ts +339 -130
  94. package/src/organization-model/domains/systems.ts +203 -0
  95. package/src/organization-model/foundation.ts +54 -67
  96. package/src/organization-model/graph/build.ts +682 -54
  97. package/src/organization-model/graph/link.ts +1 -1
  98. package/src/organization-model/graph/schema.ts +24 -9
  99. package/src/organization-model/graph/types.ts +20 -7
  100. package/src/organization-model/helpers.ts +231 -26
  101. package/src/organization-model/icons.ts +1 -0
  102. package/src/organization-model/index.ts +118 -5
  103. package/src/organization-model/migration-helpers.ts +249 -0
  104. package/src/organization-model/organization-graph.mdx +16 -15
  105. package/src/organization-model/organization-model.mdx +111 -44
  106. package/src/organization-model/published.ts +172 -19
  107. package/src/organization-model/resolve.ts +117 -54
  108. package/src/organization-model/schema.ts +654 -112
  109. package/src/organization-model/surface-projection.ts +116 -122
  110. package/src/organization-model/types.ts +146 -20
  111. package/src/platform/api/types.ts +38 -35
  112. package/src/platform/constants/versions.ts +1 -1
  113. package/src/platform/registry/__tests__/command-view.test.ts +6 -8
  114. package/src/platform/registry/__tests__/resource-link.test.ts +13 -8
  115. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +16 -31
  116. package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -0
  117. package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2005
  118. package/src/platform/registry/__tests__/validation.test.ts +1347 -1086
  119. package/src/platform/registry/index.ts +14 -0
  120. package/src/platform/registry/resource-registry.ts +52 -2
  121. package/src/platform/registry/serialization.ts +241 -202
  122. package/src/platform/registry/serialized-types.ts +1 -0
  123. package/src/platform/registry/types.ts +411 -361
  124. package/src/platform/registry/validation.ts +745 -513
  125. package/src/projects/api-schemas.ts +290 -267
  126. package/src/reference/_generated/contracts.md +853 -397
  127. package/src/reference/glossary.md +23 -18
  128. package/src/supabase/database.types.ts +181 -0
  129. package/src/test-utils/test-utils.test.ts +1 -6
  130. package/src/organization-model/__tests__/domains/operations.test.ts +0 -203
  131. package/src/organization-model/domains/features.ts +0 -31
  132. package/src/organization-model/domains/operations.ts +0 -85
@@ -0,0 +1,323 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ PoliciesDomainSchema,
4
+ PolicyApplicabilitySchema,
5
+ PolicyEffectSchema,
6
+ PolicyPredicateSchema,
7
+ PolicySchema,
8
+ PolicyTriggerSchema
9
+ } from '../../domains/policies'
10
+
11
+ describe('policies domain', () => {
12
+ it('accepts structured trigger predicate action and applicability contracts', () => {
13
+ const policy = PolicySchema.parse({
14
+ id: 'policy.lead-gen.approval',
15
+ order: 10,
16
+ label: 'Lead Gen Approval',
17
+ trigger: {
18
+ kind: 'action-invocation',
19
+ actionId: 'lead-gen.export.list'
20
+ },
21
+ predicate: {
22
+ kind: 'threshold',
23
+ metric: 'lead-gen.export.count',
24
+ operator: 'gte',
25
+ value: 100
26
+ },
27
+ actions: [{ kind: 'require-approval', roleId: 'role.sales-owner' }],
28
+ appliesTo: {
29
+ systemIds: ['sales.lead-gen'],
30
+ actionIds: ['lead-gen.export.list']
31
+ }
32
+ })
33
+
34
+ expect(policy.lifecycle).toBe('active')
35
+ expect(policy.appliesTo.systemIds).toEqual(['sales.lead-gen'])
36
+ })
37
+
38
+ it('defaults to an empty policies domain', () => {
39
+ expect(PoliciesDomainSchema.parse({})).toEqual({})
40
+ })
41
+
42
+ describe('PolicyEffect discrimination', () => {
43
+ it('parses require-approval with roleId', () => {
44
+ const result = PolicyEffectSchema.parse({ kind: 'require-approval', roleId: 'role.sales-owner' })
45
+ expect(result).toMatchObject({ kind: 'require-approval', roleId: 'role.sales-owner' })
46
+ })
47
+
48
+ it('parses require-approval without roleId (roleId is optional)', () => {
49
+ const result = PolicyEffectSchema.parse({ kind: 'require-approval' })
50
+ expect(result).toMatchObject({ kind: 'require-approval' })
51
+ expect((result as { roleId?: unknown }).roleId).toBeUndefined()
52
+ })
53
+
54
+ it('parses invoke-action with required actionId', () => {
55
+ const result = PolicyEffectSchema.parse({ kind: 'invoke-action', actionId: 'crm.deal.update' })
56
+ expect(result).toMatchObject({ kind: 'invoke-action', actionId: 'crm.deal.update' })
57
+ })
58
+
59
+ it('parses notify-role with required roleId', () => {
60
+ const result = PolicyEffectSchema.parse({ kind: 'notify-role', roleId: 'role.sales-owner' })
61
+ expect(result).toMatchObject({ kind: 'notify-role', roleId: 'role.sales-owner' })
62
+ })
63
+
64
+ it('parses block with no extra fields', () => {
65
+ const result = PolicyEffectSchema.parse({ kind: 'block' })
66
+ expect(result).toEqual({ kind: 'block' })
67
+ })
68
+
69
+ it('strips extra fields on require-approval (Zod default strip behavior)', () => {
70
+ const result = PolicyEffectSchema.safeParse({
71
+ kind: 'require-approval',
72
+ actionId: 'crm.deal.update'
73
+ })
74
+ expect(result.success).toBe(true)
75
+ if (result.success) {
76
+ expect((result.data as { actionId?: unknown }).actionId).toBeUndefined()
77
+ }
78
+ })
79
+
80
+ it('rejects invoke-action without actionId', () => {
81
+ const result = PolicyEffectSchema.safeParse({ kind: 'invoke-action' })
82
+ expect(result.success).toBe(false)
83
+ })
84
+
85
+ it('rejects notify-role without roleId', () => {
86
+ const result = PolicyEffectSchema.safeParse({ kind: 'notify-role' })
87
+ expect(result.success).toBe(false)
88
+ })
89
+ })
90
+
91
+ describe('PolicyTrigger discrimination', () => {
92
+ it('parses event with eventId in kebab-colon format', () => {
93
+ const result = PolicyTriggerSchema.parse({ kind: 'event', eventId: 'workflow-id:processed' })
94
+ expect(result).toMatchObject({ kind: 'event', eventId: 'workflow-id:processed' })
95
+ })
96
+
97
+ it('parses action-invocation with actionId', () => {
98
+ const result = PolicyTriggerSchema.parse({ kind: 'action-invocation', actionId: 'crm.deal.update' })
99
+ expect(result).toMatchObject({ kind: 'action-invocation', actionId: 'crm.deal.update' })
100
+ })
101
+
102
+ it('parses schedule with valid short cron string', () => {
103
+ const result = PolicyTriggerSchema.parse({ kind: 'schedule', cron: '0 9 * * *' })
104
+ expect(result).toMatchObject({ kind: 'schedule', cron: '0 9 * * *' })
105
+ })
106
+
107
+ it('parses manual with no fields', () => {
108
+ const result = PolicyTriggerSchema.parse({ kind: 'manual' })
109
+ expect(result).toEqual({ kind: 'manual' })
110
+ })
111
+
112
+ it('rejects event without eventId', () => {
113
+ const result = PolicyTriggerSchema.safeParse({ kind: 'event' })
114
+ expect(result.success).toBe(false)
115
+ })
116
+
117
+ it('rejects schedule with empty cron (fails min(1))', () => {
118
+ const result = PolicyTriggerSchema.safeParse({ kind: 'schedule', cron: '' })
119
+ expect(result.success).toBe(false)
120
+ })
121
+
122
+ it('rejects schedule with oversized cron (>120 chars)', () => {
123
+ const result = PolicyTriggerSchema.safeParse({ kind: 'schedule', cron: 'x'.repeat(121) })
124
+ expect(result.success).toBe(false)
125
+ })
126
+
127
+ it('rejects action-invocation without actionId', () => {
128
+ const result = PolicyTriggerSchema.safeParse({ kind: 'action-invocation' })
129
+ expect(result.success).toBe(false)
130
+ })
131
+ })
132
+
133
+ describe('PolicyPredicate discrimination', () => {
134
+ it('parses always with no fields', () => {
135
+ const result = PolicyPredicateSchema.parse({ kind: 'always' })
136
+ expect(result).toEqual({ kind: 'always' })
137
+ })
138
+
139
+ it('parses expression with valid expression string', () => {
140
+ const result = PolicyPredicateSchema.parse({ kind: 'expression', expression: 'deal.value > 10000' })
141
+ expect(result).toMatchObject({ kind: 'expression', expression: 'deal.value > 10000' })
142
+ })
143
+
144
+ it('parses threshold with metric, operator, and value', () => {
145
+ const result = PolicyPredicateSchema.parse({
146
+ kind: 'threshold',
147
+ metric: 'lead-gen.export.count',
148
+ operator: 'lt',
149
+ value: 50
150
+ })
151
+ expect(result).toMatchObject({ kind: 'threshold', metric: 'lead-gen.export.count', operator: 'lt', value: 50 })
152
+ })
153
+
154
+ it('parses threshold with all operator values', () => {
155
+ for (const operator of ['lt', 'lte', 'eq', 'gte', 'gt'] as const) {
156
+ const result = PolicyPredicateSchema.parse({ kind: 'threshold', metric: 'some.metric', operator, value: 0 })
157
+ expect(result).toMatchObject({ operator })
158
+ }
159
+ })
160
+
161
+ it('rejects expression with empty string', () => {
162
+ const result = PolicyPredicateSchema.safeParse({ kind: 'expression', expression: '' })
163
+ expect(result.success).toBe(false)
164
+ })
165
+
166
+ it('rejects expression longer than 2000 chars', () => {
167
+ const result = PolicyPredicateSchema.safeParse({ kind: 'expression', expression: 'x'.repeat(2001) })
168
+ expect(result.success).toBe(false)
169
+ })
170
+
171
+ it('rejects threshold without operator', () => {
172
+ const result = PolicyPredicateSchema.safeParse({ kind: 'threshold', metric: 'some.metric', value: 10 })
173
+ expect(result.success).toBe(false)
174
+ })
175
+
176
+ it('rejects threshold with invalid operator', () => {
177
+ const result = PolicyPredicateSchema.safeParse({
178
+ kind: 'threshold',
179
+ metric: 'some.metric',
180
+ operator: '!=',
181
+ value: 10
182
+ })
183
+ expect(result.success).toBe(false)
184
+ })
185
+ })
186
+
187
+ describe('PolicyApplicability scoping', () => {
188
+ it('parses empty object and defaults all four fields to empty arrays', () => {
189
+ const result = PolicyApplicabilitySchema.parse({})
190
+ expect(result).toEqual({ systemIds: [], actionIds: [], resourceIds: [], roleIds: [] })
191
+ })
192
+
193
+ it('parses with only systemIds populated, others default to empty', () => {
194
+ const result = PolicyApplicabilitySchema.parse({ systemIds: ['sales.lead-gen'] })
195
+ expect(result).toEqual({ systemIds: ['sales.lead-gen'], actionIds: [], resourceIds: [], roleIds: [] })
196
+ })
197
+
198
+ it('parses with all four fields populated', () => {
199
+ const result = PolicyApplicabilitySchema.parse({
200
+ systemIds: ['sales.lead-gen'],
201
+ actionIds: ['crm.deal.update'],
202
+ resourceIds: ['leadgen-workflow'],
203
+ roleIds: ['role.sales-owner']
204
+ })
205
+ expect(result).toEqual({
206
+ systemIds: ['sales.lead-gen'],
207
+ actionIds: ['crm.deal.update'],
208
+ resourceIds: ['leadgen-workflow'],
209
+ roleIds: ['role.sales-owner']
210
+ })
211
+ })
212
+
213
+ it('parses with multiple IDs in one field', () => {
214
+ const result = PolicyApplicabilitySchema.parse({ systemIds: ['s1', 's2'] })
215
+ expect(result.systemIds).toEqual(['s1', 's2'])
216
+ })
217
+ })
218
+
219
+ describe('Policy lifecycle and order', () => {
220
+ const basePolicy = {
221
+ id: 'policy-a',
222
+ order: 10,
223
+ label: 'Policy A',
224
+ trigger: { kind: 'manual' },
225
+ actions: [{ kind: 'block' }]
226
+ }
227
+
228
+ it('defaults lifecycle to active when omitted', () => {
229
+ const result = PolicySchema.parse(basePolicy)
230
+ expect(result.lifecycle).toBe('active')
231
+ })
232
+
233
+ it('parses all 5 lifecycle values', () => {
234
+ for (const lifecycle of ['draft', 'beta', 'active', 'deprecated', 'archived'] as const) {
235
+ const result = PolicySchema.parse({ ...basePolicy, lifecycle })
236
+ expect(result.lifecycle).toBe(lifecycle)
237
+ }
238
+ })
239
+
240
+ it('rejects invalid lifecycle value', () => {
241
+ const result = PolicySchema.safeParse({ ...basePolicy, lifecycle: 'pending' })
242
+ expect(result.success).toBe(false)
243
+ })
244
+
245
+ it('accepts arbitrary order numbers including negative', () => {
246
+ for (const order of [10, 25, -5, 1000]) {
247
+ const result = PolicySchema.parse({ ...basePolicy, order })
248
+ expect(result.order).toBe(order)
249
+ }
250
+ })
251
+ })
252
+
253
+ describe('PoliciesDomainSchema key-id consistency', () => {
254
+ const makePolicy = (id: string) => ({
255
+ id,
256
+ order: 10,
257
+ label: 'Test Policy',
258
+ trigger: { kind: 'manual' },
259
+ actions: [{ kind: 'block' }]
260
+ })
261
+
262
+ it('parses a single policy where key matches id', () => {
263
+ const result = PoliciesDomainSchema.parse({ 'policy-1': makePolicy('policy-1') })
264
+ expect(result['policy-1']).toMatchObject({ id: 'policy-1' })
265
+ })
266
+
267
+ it('parses multiple policies where all keys match their ids', () => {
268
+ const result = PoliciesDomainSchema.parse({
269
+ 'policy-1': makePolicy('policy-1'),
270
+ 'policy-2': makePolicy('policy-2')
271
+ })
272
+ expect(Object.keys(result)).toHaveLength(2)
273
+ expect(result['policy-2']).toMatchObject({ id: 'policy-2' })
274
+ })
275
+
276
+ it('rejects a policy where key does not match id', () => {
277
+ const result = PoliciesDomainSchema.safeParse({ 'key-name': makePolicy('different-id') })
278
+ expect(result.success).toBe(false)
279
+ })
280
+ })
281
+
282
+ describe('actions array constraints', () => {
283
+ const basePolicy = {
284
+ id: 'policy-b',
285
+ order: 10,
286
+ label: 'Policy B',
287
+ trigger: { kind: 'manual' }
288
+ }
289
+
290
+ it('parses a single effect in the array', () => {
291
+ const result = PolicySchema.parse({ ...basePolicy, actions: [{ kind: 'block' }] })
292
+ expect(result.actions).toHaveLength(1)
293
+ })
294
+
295
+ it('parses multiple effects of the same kind', () => {
296
+ const result = PolicySchema.parse({
297
+ ...basePolicy,
298
+ actions: [
299
+ { kind: 'notify-role', roleId: 'role.sales-owner' },
300
+ { kind: 'notify-role', roleId: 'role.ops-lead' }
301
+ ]
302
+ })
303
+ expect(result.actions).toHaveLength(2)
304
+ })
305
+
306
+ it('parses multiple effects of different kinds', () => {
307
+ const result = PolicySchema.parse({
308
+ ...basePolicy,
309
+ actions: [
310
+ { kind: 'require-approval', roleId: 'role.sales-owner' },
311
+ { kind: 'invoke-action', actionId: 'crm.deal.update' },
312
+ { kind: 'notify-role', roleId: 'role.ops-lead' }
313
+ ]
314
+ })
315
+ expect(result.actions).toHaveLength(3)
316
+ })
317
+
318
+ it('rejects empty actions array (min(1) violation)', () => {
319
+ const result = PolicySchema.safeParse({ ...basePolicy, actions: [] })
320
+ expect(result.success).toBe(false)
321
+ })
322
+ })
323
+ })