@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
@@ -1,272 +1,272 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { ResourceMappingSchema, TechStackEntrySchema } from '../../domains/shared'
3
- import { resolveOrganizationModel } from '../../resolve'
4
-
5
- // ---------------------------------------------------------------------------
6
- // Minimal valid resource mapping fixture (no techStack) for reuse.
7
- // ---------------------------------------------------------------------------
8
-
9
- const MINIMAL_MAPPING = {
10
- id: 'rm-hubspot-sync',
11
- label: 'HubSpot Sync',
12
- resourceId: 'hubspot-sync-workflow',
13
- resourceType: 'workflow' as const,
14
- featureIds: [],
15
- entityIds: [],
16
- surfaceIds: [],
17
- capabilityIds: []
18
- }
19
-
20
- // ---------------------------------------------------------------------------
21
- // Group 1: TechStackEntrySchema — positive parse
22
- // ---------------------------------------------------------------------------
23
-
24
- describe('TechStackEntrySchema — positive parse', () => {
25
- it('accepts a fully-populated tech stack entry', () => {
26
- const result = TechStackEntrySchema.safeParse({
27
- platform: 'HubSpot',
28
- purpose: 'Primary CRM and contact management',
29
- credentialStatus: 'configured',
30
- isSystemOfRecord: true
31
- })
32
- expect(result.success).toBe(true)
33
- if (result.success) {
34
- expect(result.data.platform).toBe('HubSpot')
35
- expect(result.data.credentialStatus).toBe('configured')
36
- expect(result.data.isSystemOfRecord).toBe(true)
37
- }
38
- })
39
-
40
- it('isSystemOfRecord defaults to false when omitted', () => {
41
- const result = TechStackEntrySchema.safeParse({
42
- platform: 'Stripe',
43
- purpose: 'Payment processing',
44
- credentialStatus: 'pending'
45
- })
46
- expect(result.success).toBe(true)
47
- if (result.success) {
48
- expect(result.data.isSystemOfRecord).toBe(false)
49
- }
50
- })
51
-
52
- it('accepts all four credentialStatus enum values', () => {
53
- const statuses = ['configured', 'pending', 'expired', 'missing'] as const
54
- for (const status of statuses) {
55
- const result = TechStackEntrySchema.safeParse({
56
- platform: 'SomePlatform',
57
- purpose: 'Some purpose',
58
- credentialStatus: status
59
- })
60
- expect(result.success).toBe(true)
61
- }
62
- })
63
- })
64
-
65
- // ---------------------------------------------------------------------------
66
- // Group 2: TechStackEntrySchema — negative parse
67
- // ---------------------------------------------------------------------------
68
-
69
- describe('TechStackEntrySchema — negative parse', () => {
70
- it('rejects invalid credentialStatus enum value', () => {
71
- const result = TechStackEntrySchema.safeParse({
72
- platform: 'HubSpot',
73
- purpose: 'CRM',
74
- credentialStatus: 'active'
75
- })
76
- expect(result.success).toBe(false)
77
- })
78
-
79
- it('rejects missing platform', () => {
80
- const result = TechStackEntrySchema.safeParse({
81
- purpose: 'CRM',
82
- credentialStatus: 'configured'
83
- })
84
- expect(result.success).toBe(false)
85
- })
86
-
87
- it('rejects empty string platform', () => {
88
- const result = TechStackEntrySchema.safeParse({
89
- platform: '',
90
- purpose: 'CRM',
91
- credentialStatus: 'configured'
92
- })
93
- expect(result.success).toBe(false)
94
- })
95
-
96
- it('rejects missing purpose', () => {
97
- const result = TechStackEntrySchema.safeParse({
98
- platform: 'HubSpot',
99
- credentialStatus: 'configured'
100
- })
101
- expect(result.success).toBe(false)
102
- })
103
-
104
- it('rejects empty string purpose', () => {
105
- const result = TechStackEntrySchema.safeParse({
106
- platform: 'HubSpot',
107
- purpose: '',
108
- credentialStatus: 'configured'
109
- })
110
- expect(result.success).toBe(false)
111
- })
112
-
113
- it('rejects missing credentialStatus', () => {
114
- const result = TechStackEntrySchema.safeParse({
115
- platform: 'HubSpot',
116
- purpose: 'CRM'
117
- })
118
- expect(result.success).toBe(false)
119
- })
120
- })
121
-
122
- // ---------------------------------------------------------------------------
123
- // Group 3: ResourceMappingSchema — backward compatibility (no techStack)
124
- // ---------------------------------------------------------------------------
125
-
126
- describe('ResourceMappingSchema — backward compatibility without techStack', () => {
127
- it('parses a mapping with no techStack key (legacy shape)', () => {
128
- const result = ResourceMappingSchema.safeParse(MINIMAL_MAPPING)
129
- expect(result.success).toBe(true)
130
- if (result.success) {
131
- expect(result.data.techStack).toBeUndefined()
132
- }
133
- })
134
-
135
- it('parses a mapping with techStack explicitly set to undefined', () => {
136
- const result = ResourceMappingSchema.safeParse({ ...MINIMAL_MAPPING, techStack: undefined })
137
- expect(result.success).toBe(true)
138
- if (result.success) {
139
- expect(result.data.techStack).toBeUndefined()
140
- }
141
- })
142
-
143
- it('all existing reference ID fields still default to empty arrays', () => {
144
- const result = ResourceMappingSchema.safeParse({
145
- id: 'rm-bare',
146
- label: 'Bare mapping',
147
- resourceId: 'bare-workflow',
148
- resourceType: 'workflow'
149
- })
150
- expect(result.success).toBe(true)
151
- if (result.success) {
152
- expect(result.data.featureIds).toEqual([])
153
- expect(result.data.entityIds).toEqual([])
154
- expect(result.data.surfaceIds).toEqual([])
155
- expect(result.data.capabilityIds).toEqual([])
156
- expect(result.data.techStack).toBeUndefined()
157
- }
158
- })
159
- })
160
-
161
- // ---------------------------------------------------------------------------
162
- // Group 4: ResourceMappingSchema — with techStack
163
- // ---------------------------------------------------------------------------
164
-
165
- describe('ResourceMappingSchema — with techStack', () => {
166
- it('parses a mapping with a full techStack object', () => {
167
- const result = ResourceMappingSchema.safeParse({
168
- ...MINIMAL_MAPPING,
169
- techStack: {
170
- platform: 'HubSpot',
171
- purpose: 'Contact and deal management',
172
- credentialStatus: 'configured',
173
- isSystemOfRecord: true
174
- }
175
- })
176
- expect(result.success).toBe(true)
177
- if (result.success) {
178
- expect(result.data.techStack).toBeDefined()
179
- expect(result.data.techStack?.platform).toBe('HubSpot')
180
- expect(result.data.techStack?.credentialStatus).toBe('configured')
181
- expect(result.data.techStack?.isSystemOfRecord).toBe(true)
182
- }
183
- })
184
-
185
- it('isSystemOfRecord defaults to false inside techStack when omitted', () => {
186
- const result = ResourceMappingSchema.safeParse({
187
- ...MINIMAL_MAPPING,
188
- techStack: {
189
- platform: 'Notion',
190
- purpose: 'Internal knowledge base',
191
- credentialStatus: 'pending'
192
- }
193
- })
194
- expect(result.success).toBe(true)
195
- if (result.success) {
196
- expect(result.data.techStack?.isSystemOfRecord).toBe(false)
197
- }
198
- })
199
-
200
- it('rejects a mapping with an invalid credentialStatus in techStack', () => {
201
- const result = ResourceMappingSchema.safeParse({
202
- ...MINIMAL_MAPPING,
203
- techStack: {
204
- platform: 'Stripe',
205
- purpose: 'Billing',
206
- credentialStatus: 'broken'
207
- }
208
- })
209
- expect(result.success).toBe(false)
210
- })
211
-
212
- it('rejects a mapping with techStack missing platform', () => {
213
- const result = ResourceMappingSchema.safeParse({
214
- ...MINIMAL_MAPPING,
215
- techStack: {
216
- purpose: 'Something',
217
- credentialStatus: 'configured'
218
- }
219
- })
220
- expect(result.success).toBe(false)
221
- })
222
- })
223
-
224
- // ---------------------------------------------------------------------------
225
- // Group 5: Multiple mappings with mixed techStack presence
226
- // ---------------------------------------------------------------------------
227
-
228
- describe('ResourceMappingSchema — mixed techStack presence across entries', () => {
229
- it('resolves a model with multiple mappings — some with techStack, some without', () => {
230
- const withStack = {
231
- ...MINIMAL_MAPPING,
232
- id: 'rm-with-stack',
233
- resourceId: 'workflow-with-stack',
234
- techStack: {
235
- platform: 'Intercom',
236
- purpose: 'Customer support',
237
- credentialStatus: 'expired' as const
238
- }
239
- }
240
- const withoutStack = {
241
- id: 'rm-without-stack',
242
- label: 'Plain mapping',
243
- resourceId: 'plain-workflow',
244
- resourceType: 'integration' as const,
245
- featureIds: [],
246
- entityIds: [],
247
- surfaceIds: [],
248
- capabilityIds: []
249
- }
250
-
251
- const withResult = ResourceMappingSchema.safeParse(withStack)
252
- const withoutResult = ResourceMappingSchema.safeParse(withoutStack)
253
-
254
- expect(withResult.success).toBe(true)
255
- expect(withoutResult.success).toBe(true)
256
-
257
- if (withResult.success) {
258
- expect(withResult.data.techStack?.credentialStatus).toBe('expired')
259
- }
260
- if (withoutResult.success) {
261
- expect(withoutResult.data.techStack).toBeUndefined()
262
- }
263
- })
264
- })
265
-
266
- // ---------------------------------------------------------------------------
267
- // Group 6: Integration via resolveOrganizationModel
268
- // ---------------------------------------------------------------------------
269
-
1
+ import { describe, expect, it } from 'vitest'
2
+ import { ResourceMappingSchema, TechStackEntrySchema } from '../../domains/shared'
3
+ import { resolveOrganizationModel } from '../../resolve'
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Minimal valid resource mapping fixture (no techStack) for reuse.
7
+ // ---------------------------------------------------------------------------
8
+
9
+ const MINIMAL_MAPPING = {
10
+ id: 'rm-hubspot-sync',
11
+ label: 'HubSpot Sync',
12
+ resourceId: 'hubspot-sync-workflow',
13
+ resourceType: 'workflow' as const,
14
+ systemIds: [],
15
+ entityIds: [],
16
+ surfaceIds: [],
17
+ actionIds: []
18
+ }
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Group 1: TechStackEntrySchema — positive parse
22
+ // ---------------------------------------------------------------------------
23
+
24
+ describe('TechStackEntrySchema — positive parse', () => {
25
+ it('accepts a fully-populated tech stack entry', () => {
26
+ const result = TechStackEntrySchema.safeParse({
27
+ platform: 'HubSpot',
28
+ purpose: 'Primary CRM and contact management',
29
+ credentialStatus: 'configured',
30
+ isSystemOfRecord: true
31
+ })
32
+ expect(result.success).toBe(true)
33
+ if (result.success) {
34
+ expect(result.data.platform).toBe('HubSpot')
35
+ expect(result.data.credentialStatus).toBe('configured')
36
+ expect(result.data.isSystemOfRecord).toBe(true)
37
+ }
38
+ })
39
+
40
+ it('isSystemOfRecord defaults to false when omitted', () => {
41
+ const result = TechStackEntrySchema.safeParse({
42
+ platform: 'Stripe',
43
+ purpose: 'Payment processing',
44
+ credentialStatus: 'pending'
45
+ })
46
+ expect(result.success).toBe(true)
47
+ if (result.success) {
48
+ expect(result.data.isSystemOfRecord).toBe(false)
49
+ }
50
+ })
51
+
52
+ it('accepts all four credentialStatus enum values', () => {
53
+ const statuses = ['configured', 'pending', 'expired', 'missing'] as const
54
+ for (const status of statuses) {
55
+ const result = TechStackEntrySchema.safeParse({
56
+ platform: 'SomePlatform',
57
+ purpose: 'Some purpose',
58
+ credentialStatus: status
59
+ })
60
+ expect(result.success).toBe(true)
61
+ }
62
+ })
63
+ })
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Group 2: TechStackEntrySchema — negative parse
67
+ // ---------------------------------------------------------------------------
68
+
69
+ describe('TechStackEntrySchema — negative parse', () => {
70
+ it('rejects invalid credentialStatus enum value', () => {
71
+ const result = TechStackEntrySchema.safeParse({
72
+ platform: 'HubSpot',
73
+ purpose: 'CRM',
74
+ credentialStatus: 'active'
75
+ })
76
+ expect(result.success).toBe(false)
77
+ })
78
+
79
+ it('rejects missing platform', () => {
80
+ const result = TechStackEntrySchema.safeParse({
81
+ purpose: 'CRM',
82
+ credentialStatus: 'configured'
83
+ })
84
+ expect(result.success).toBe(false)
85
+ })
86
+
87
+ it('rejects empty string platform', () => {
88
+ const result = TechStackEntrySchema.safeParse({
89
+ platform: '',
90
+ purpose: 'CRM',
91
+ credentialStatus: 'configured'
92
+ })
93
+ expect(result.success).toBe(false)
94
+ })
95
+
96
+ it('rejects missing purpose', () => {
97
+ const result = TechStackEntrySchema.safeParse({
98
+ platform: 'HubSpot',
99
+ credentialStatus: 'configured'
100
+ })
101
+ expect(result.success).toBe(false)
102
+ })
103
+
104
+ it('rejects empty string purpose', () => {
105
+ const result = TechStackEntrySchema.safeParse({
106
+ platform: 'HubSpot',
107
+ purpose: '',
108
+ credentialStatus: 'configured'
109
+ })
110
+ expect(result.success).toBe(false)
111
+ })
112
+
113
+ it('rejects missing credentialStatus', () => {
114
+ const result = TechStackEntrySchema.safeParse({
115
+ platform: 'HubSpot',
116
+ purpose: 'CRM'
117
+ })
118
+ expect(result.success).toBe(false)
119
+ })
120
+ })
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Group 3: ResourceMappingSchema — backward compatibility (no techStack)
124
+ // ---------------------------------------------------------------------------
125
+
126
+ describe('ResourceMappingSchema — backward compatibility without techStack', () => {
127
+ it('parses a mapping with no techStack key (legacy shape)', () => {
128
+ const result = ResourceMappingSchema.safeParse(MINIMAL_MAPPING)
129
+ expect(result.success).toBe(true)
130
+ if (result.success) {
131
+ expect(result.data.techStack).toBeUndefined()
132
+ }
133
+ })
134
+
135
+ it('parses a mapping with techStack explicitly set to undefined', () => {
136
+ const result = ResourceMappingSchema.safeParse({ ...MINIMAL_MAPPING, techStack: undefined })
137
+ expect(result.success).toBe(true)
138
+ if (result.success) {
139
+ expect(result.data.techStack).toBeUndefined()
140
+ }
141
+ })
142
+
143
+ it('all existing reference ID fields still default to empty arrays', () => {
144
+ const result = ResourceMappingSchema.safeParse({
145
+ id: 'rm-bare',
146
+ label: 'Bare mapping',
147
+ resourceId: 'bare-workflow',
148
+ resourceType: 'workflow'
149
+ })
150
+ expect(result.success).toBe(true)
151
+ if (result.success) {
152
+ expect(result.data.systemIds).toEqual([])
153
+ expect(result.data.entityIds).toEqual([])
154
+ expect(result.data.surfaceIds).toEqual([])
155
+ expect(result.data.actionIds).toEqual([])
156
+ expect(result.data.techStack).toBeUndefined()
157
+ }
158
+ })
159
+ })
160
+
161
+ // ---------------------------------------------------------------------------
162
+ // Group 4: ResourceMappingSchema — with techStack
163
+ // ---------------------------------------------------------------------------
164
+
165
+ describe('ResourceMappingSchema — with techStack', () => {
166
+ it('parses a mapping with a full techStack object', () => {
167
+ const result = ResourceMappingSchema.safeParse({
168
+ ...MINIMAL_MAPPING,
169
+ techStack: {
170
+ platform: 'HubSpot',
171
+ purpose: 'Contact and deal management',
172
+ credentialStatus: 'configured',
173
+ isSystemOfRecord: true
174
+ }
175
+ })
176
+ expect(result.success).toBe(true)
177
+ if (result.success) {
178
+ expect(result.data.techStack).toBeDefined()
179
+ expect(result.data.techStack?.platform).toBe('HubSpot')
180
+ expect(result.data.techStack?.credentialStatus).toBe('configured')
181
+ expect(result.data.techStack?.isSystemOfRecord).toBe(true)
182
+ }
183
+ })
184
+
185
+ it('isSystemOfRecord defaults to false inside techStack when omitted', () => {
186
+ const result = ResourceMappingSchema.safeParse({
187
+ ...MINIMAL_MAPPING,
188
+ techStack: {
189
+ platform: 'Notion',
190
+ purpose: 'Internal knowledge base',
191
+ credentialStatus: 'pending'
192
+ }
193
+ })
194
+ expect(result.success).toBe(true)
195
+ if (result.success) {
196
+ expect(result.data.techStack?.isSystemOfRecord).toBe(false)
197
+ }
198
+ })
199
+
200
+ it('rejects a mapping with an invalid credentialStatus in techStack', () => {
201
+ const result = ResourceMappingSchema.safeParse({
202
+ ...MINIMAL_MAPPING,
203
+ techStack: {
204
+ platform: 'Stripe',
205
+ purpose: 'Billing',
206
+ credentialStatus: 'broken'
207
+ }
208
+ })
209
+ expect(result.success).toBe(false)
210
+ })
211
+
212
+ it('rejects a mapping with techStack missing platform', () => {
213
+ const result = ResourceMappingSchema.safeParse({
214
+ ...MINIMAL_MAPPING,
215
+ techStack: {
216
+ purpose: 'Something',
217
+ credentialStatus: 'configured'
218
+ }
219
+ })
220
+ expect(result.success).toBe(false)
221
+ })
222
+ })
223
+
224
+ // ---------------------------------------------------------------------------
225
+ // Group 5: Multiple mappings with mixed techStack presence
226
+ // ---------------------------------------------------------------------------
227
+
228
+ describe('ResourceMappingSchema — mixed techStack presence across entries', () => {
229
+ it('resolves a model with multiple mappings — some with techStack, some without', () => {
230
+ const withStack = {
231
+ ...MINIMAL_MAPPING,
232
+ id: 'rm-with-stack',
233
+ resourceId: 'workflow-with-stack',
234
+ techStack: {
235
+ platform: 'Intercom',
236
+ purpose: 'Customer support',
237
+ credentialStatus: 'expired' as const
238
+ }
239
+ }
240
+ const withoutStack = {
241
+ id: 'rm-without-stack',
242
+ label: 'Plain mapping',
243
+ resourceId: 'plain-workflow',
244
+ resourceType: 'integration' as const,
245
+ systemIds: [],
246
+ entityIds: [],
247
+ surfaceIds: [],
248
+ actionIds: []
249
+ }
250
+
251
+ const withResult = ResourceMappingSchema.safeParse(withStack)
252
+ const withoutResult = ResourceMappingSchema.safeParse(withoutStack)
253
+
254
+ expect(withResult.success).toBe(true)
255
+ expect(withoutResult.success).toBe(true)
256
+
257
+ if (withResult.success) {
258
+ expect(withResult.data.techStack?.credentialStatus).toBe('expired')
259
+ }
260
+ if (withoutResult.success) {
261
+ expect(withoutResult.data.techStack).toBeUndefined()
262
+ }
263
+ })
264
+ })
265
+
266
+ // ---------------------------------------------------------------------------
267
+ // Group 6: Integration via resolveOrganizationModel
268
+ // ---------------------------------------------------------------------------
269
+
270
270
  describe('resolveOrganizationModel resource mapping removal', () => {
271
271
  it('resolved model validates without resourceMappings', () => {
272
272
  expect(() => resolveOrganizationModel({})).not.toThrow()
@@ -280,10 +280,10 @@ describe('resolveOrganizationModel resource mapping removal', () => {
280
280
  label: 'Legacy mapping',
281
281
  resourceId: 'legacy-workflow',
282
282
  resourceType: 'workflow',
283
- featureIds: [],
283
+ systemIds: [],
284
284
  entityIds: [],
285
285
  surfaceIds: [],
286
- capabilityIds: []
286
+ actionIds: []
287
287
  }
288
288
  ]
289
289
  } as Parameters<typeof resolveOrganizationModel>[0])