@elevasis/core 0.22.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 (112) hide show
  1. package/dist/index.d.ts +2330 -2391
  2. package/dist/index.js +2322 -1147
  3. package/dist/knowledge/index.d.ts +702 -1136
  4. package/dist/knowledge/index.js +9 -9
  5. package/dist/organization-model/index.d.ts +2330 -2391
  6. package/dist/organization-model/index.js +2322 -1147
  7. package/dist/test-utils/index.d.ts +703 -1106
  8. package/dist/test-utils/index.js +1735 -1089
  9. package/package.json +1 -1
  10. package/src/__tests__/template-core-compatibility.test.ts +11 -79
  11. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +360 -98
  12. package/src/business/acquisition/api-schemas.test.ts +2 -2
  13. package/src/business/acquisition/api-schemas.ts +7 -9
  14. package/src/business/acquisition/build-templates.test.ts +4 -4
  15. package/src/business/acquisition/build-templates.ts +72 -30
  16. package/src/business/acquisition/crm-state-actions.test.ts +13 -11
  17. package/src/business/acquisition/types.ts +7 -3
  18. package/src/execution/engine/agent/core/types.ts +1 -1
  19. package/src/execution/engine/workflow/types.ts +2 -2
  20. package/src/knowledge/README.md +8 -7
  21. package/src/knowledge/__tests__/queries.test.ts +74 -73
  22. package/src/knowledge/format.ts +10 -9
  23. package/src/knowledge/index.ts +1 -1
  24. package/src/knowledge/published.ts +1 -1
  25. package/src/knowledge/queries.ts +26 -25
  26. package/src/organization-model/README.md +66 -26
  27. package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
  28. package/src/organization-model/__tests__/defaults.test.ts +72 -98
  29. package/src/organization-model/__tests__/domains/actions.test.ts +56 -0
  30. package/src/organization-model/__tests__/domains/customers.test.ts +299 -295
  31. package/src/organization-model/__tests__/domains/entities.test.ts +56 -0
  32. package/src/organization-model/__tests__/domains/goals.test.ts +493 -479
  33. package/src/organization-model/__tests__/domains/identity.test.ts +280 -279
  34. package/src/organization-model/__tests__/domains/navigation.test.ts +268 -212
  35. package/src/organization-model/__tests__/domains/offerings.test.ts +414 -419
  36. package/src/organization-model/__tests__/domains/policies.test.ts +323 -0
  37. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +271 -271
  38. package/src/organization-model/__tests__/domains/resources.test.ts +159 -37
  39. package/src/organization-model/__tests__/domains/roles.test.ts +147 -86
  40. package/src/organization-model/__tests__/domains/statuses.test.ts +246 -243
  41. package/src/organization-model/__tests__/domains/systems.test.ts +67 -51
  42. package/src/organization-model/__tests__/flatten-additive-merge.test.ts +361 -0
  43. package/src/organization-model/__tests__/foundation.test.ts +74 -102
  44. package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -0
  45. package/src/organization-model/__tests__/graph.test.ts +899 -71
  46. package/src/organization-model/__tests__/knowledge.test.ts +173 -52
  47. package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -0
  48. package/src/organization-model/__tests__/migration-helpers.test.ts +591 -0
  49. package/src/organization-model/__tests__/prospecting-ssot.test.ts +36 -27
  50. package/src/organization-model/__tests__/recursive-system-schema.test.ts +520 -0
  51. package/src/organization-model/__tests__/resolve.test.ts +174 -23
  52. package/src/organization-model/__tests__/schema.test.ts +291 -114
  53. package/src/organization-model/__tests__/surface-projection.test.ts +207 -97
  54. package/src/organization-model/catalogs/lead-gen.ts +144 -0
  55. package/src/organization-model/content-kinds/config.ts +36 -0
  56. package/src/organization-model/content-kinds/index.ts +74 -0
  57. package/src/organization-model/content-kinds/pipeline.ts +68 -0
  58. package/src/organization-model/content-kinds/registry.ts +44 -0
  59. package/src/organization-model/content-kinds/status.ts +71 -0
  60. package/src/organization-model/content-kinds/template.ts +83 -0
  61. package/src/organization-model/content-kinds/types.ts +117 -0
  62. package/src/organization-model/contracts.ts +13 -3
  63. package/src/organization-model/defaults.ts +488 -96
  64. package/src/organization-model/domains/actions.ts +239 -0
  65. package/src/organization-model/domains/customers.ts +78 -75
  66. package/src/organization-model/domains/entities.ts +144 -0
  67. package/src/organization-model/domains/goals.ts +83 -80
  68. package/src/organization-model/domains/knowledge.ts +74 -16
  69. package/src/organization-model/domains/navigation.ts +107 -384
  70. package/src/organization-model/domains/offerings.ts +71 -66
  71. package/src/organization-model/domains/policies.ts +102 -0
  72. package/src/organization-model/domains/projects.ts +14 -48
  73. package/src/organization-model/domains/prospecting.ts +62 -181
  74. package/src/organization-model/domains/resources.ts +81 -24
  75. package/src/organization-model/domains/roles.ts +13 -10
  76. package/src/organization-model/domains/sales.ts +10 -219
  77. package/src/organization-model/domains/shared.ts +57 -57
  78. package/src/organization-model/domains/statuses.ts +339 -130
  79. package/src/organization-model/domains/systems.ts +186 -29
  80. package/src/organization-model/foundation.ts +54 -67
  81. package/src/organization-model/graph/build.ts +682 -54
  82. package/src/organization-model/graph/link.ts +1 -1
  83. package/src/organization-model/graph/schema.ts +24 -9
  84. package/src/organization-model/graph/types.ts +20 -7
  85. package/src/organization-model/helpers.ts +231 -26
  86. package/src/organization-model/index.ts +116 -5
  87. package/src/organization-model/migration-helpers.ts +249 -0
  88. package/src/organization-model/organization-graph.mdx +16 -15
  89. package/src/organization-model/organization-model.mdx +89 -41
  90. package/src/organization-model/published.ts +120 -18
  91. package/src/organization-model/resolve.ts +117 -54
  92. package/src/organization-model/schema.ts +561 -140
  93. package/src/organization-model/surface-projection.ts +116 -122
  94. package/src/organization-model/types.ts +102 -21
  95. package/src/platform/constants/versions.ts +1 -1
  96. package/src/platform/registry/__tests__/command-view.test.ts +6 -8
  97. package/src/platform/registry/__tests__/resource-link.test.ts +13 -8
  98. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +16 -31
  99. package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -0
  100. package/src/platform/registry/__tests__/resource-registry.test.ts +9 -7
  101. package/src/platform/registry/__tests__/validation.test.ts +15 -11
  102. package/src/platform/registry/resource-registry.ts +20 -8
  103. package/src/platform/registry/serialization.ts +7 -7
  104. package/src/platform/registry/types.ts +3 -3
  105. package/src/platform/registry/validation.ts +17 -15
  106. package/src/reference/_generated/contracts.md +362 -99
  107. package/src/reference/glossary.md +18 -18
  108. package/src/supabase/database.types.ts +60 -0
  109. package/src/test-utils/test-utils.test.ts +1 -6
  110. package/src/organization-model/__tests__/domains/operations.test.ts +0 -203
  111. package/src/organization-model/domains/features.ts +0 -31
  112. 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])