@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.
- package/dist/index.d.ts +3214 -2501
- package/dist/index.js +3112 -1222
- package/dist/knowledge/index.d.ts +1108 -1264
- package/dist/knowledge/index.js +112 -9
- package/dist/organization-model/index.d.ts +3214 -2501
- package/dist/organization-model/index.js +3112 -1222
- package/dist/test-utils/index.d.ts +985 -1103
- package/dist/test-utils/index.js +2464 -1165
- package/package.json +5 -5
- package/src/README.md +14 -14
- package/src/__tests__/publish.test.ts +24 -24
- package/src/__tests__/template-core-compatibility.test.ts +9 -80
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +2389 -2121
- package/src/_gen/__tests__/scaffold-contracts.test.ts +30 -30
- package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +217 -217
- package/src/auth/multi-tenancy/credentials/server/encryption.ts +69 -69
- package/src/auth/multi-tenancy/credentials/server/kek-loader.ts +37 -37
- package/src/auth/multi-tenancy/index.ts +26 -26
- package/src/auth/multi-tenancy/invitations/api-schemas.ts +104 -104
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +143 -143
- package/src/auth/multi-tenancy/memberships/index.ts +26 -26
- package/src/auth/multi-tenancy/memberships/membership.ts +130 -130
- package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -194
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -136
- package/src/auth/multi-tenancy/permissions.test.ts +42 -42
- package/src/auth/multi-tenancy/permissions.ts +123 -123
- package/src/auth/multi-tenancy/role-management/api-schemas.ts +78 -78
- package/src/auth/multi-tenancy/role-management/index.ts +16 -16
- package/src/auth/multi-tenancy/theme-presets.ts +45 -45
- package/src/auth/multi-tenancy/types.ts +57 -57
- package/src/auth/multi-tenancy/users/api-schemas.ts +165 -165
- package/src/business/README.md +2 -2
- package/src/business/acquisition/activity-events.test.ts +250 -250
- package/src/business/acquisition/activity-events.ts +93 -93
- package/src/business/acquisition/api-schemas.test.ts +1883 -1843
- package/src/business/acquisition/api-schemas.ts +1493 -1500
- package/src/business/acquisition/build-templates.test.ts +240 -240
- package/src/business/acquisition/build-templates.ts +83 -41
- package/src/business/acquisition/crm-next-action.test.ts +262 -262
- package/src/business/acquisition/crm-next-action.ts +220 -220
- package/src/business/acquisition/crm-priority.test.ts +216 -216
- package/src/business/acquisition/crm-priority.ts +349 -349
- package/src/business/acquisition/crm-state-actions.test.ts +153 -151
- package/src/business/acquisition/deal-ownership.test.ts +351 -351
- package/src/business/acquisition/deal-ownership.ts +120 -120
- package/src/business/acquisition/derive-actions.test.ts +129 -104
- package/src/business/acquisition/derive-actions.ts +74 -84
- package/src/business/acquisition/index.ts +171 -170
- package/src/business/acquisition/ontology-validation.ts +309 -0
- package/src/business/acquisition/stateful.ts +30 -30
- package/src/business/acquisition/types.ts +396 -392
- package/src/business/clients/api-schemas.test.ts +115 -115
- package/src/business/clients/api-schemas.ts +158 -158
- package/src/business/clients/index.ts +1 -1
- package/src/business/crm/api-schemas.ts +40 -40
- package/src/business/crm/index.ts +1 -1
- package/src/business/deals/api-schemas.ts +87 -87
- package/src/business/deals/index.ts +1 -1
- package/src/business/index.ts +5 -5
- package/src/business/projects/types.ts +144 -144
- package/src/commands/queue/types/task.ts +15 -15
- package/src/execution/core/runner-types.ts +61 -61
- package/src/execution/core/sse-executions.ts +7 -7
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -10
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -16
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -4
- package/src/execution/engine/agent/core/types.ts +25 -25
- package/src/execution/engine/agent/index.ts +6 -6
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -24
- package/src/execution/engine/index.ts +443 -443
- package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +298 -298
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -55
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -107
- package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -48
- package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -99
- package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -1
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +363 -363
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +162 -162
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +316 -316
- package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -18
- package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -194
- package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -7
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +204 -204
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -105
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -428
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -2
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1474 -1474
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -103
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.test.ts +88 -88
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +141 -141
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +76 -76
- package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -182
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -310
- package/src/execution/engine/tools/integration/service.test.ts +239 -239
- package/src/execution/engine/tools/integration/service.ts +172 -172
- package/src/execution/engine/tools/integration/tool.ts +255 -255
- package/src/execution/engine/tools/lead-service-types.ts +1005 -1005
- package/src/execution/engine/tools/messages.ts +43 -43
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -7
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -6
- package/src/execution/engine/tools/platform/acquisition/list-tools.ts +6 -6
- package/src/execution/engine/tools/platform/acquisition/types.ts +280 -280
- package/src/execution/engine/tools/platform/email/types.ts +97 -97
- package/src/execution/engine/tools/registry.ts +704 -704
- package/src/execution/engine/tools/tool-maps.ts +831 -831
- package/src/execution/engine/tools/types.ts +234 -234
- package/src/execution/engine/workflow/types.ts +202 -202
- package/src/execution/external/__tests__/api-schemas.test.ts +127 -127
- package/src/execution/external/api-schemas.ts +40 -40
- package/src/execution/external/index.ts +1 -1
- package/src/index.ts +18 -18
- package/src/integrations/credentials/__tests__/api-schemas.test.ts +420 -420
- package/src/integrations/credentials/api-schemas.ts +146 -146
- package/src/integrations/credentials/schemas.ts +200 -200
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -7
- package/src/integrations/oauth/provider-registry.ts +74 -74
- package/src/integrations/oauth/server/credentials.ts +43 -43
- package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +327 -327
- package/src/integrations/webhook-endpoints/api-schemas.ts +103 -103
- package/src/integrations/webhook-endpoints/types.ts +58 -58
- package/src/knowledge/README.md +33 -32
- package/src/knowledge/__tests__/queries.test.ts +633 -541
- package/src/knowledge/format.ts +100 -99
- package/src/knowledge/index.ts +5 -5
- package/src/knowledge/published.ts +5 -5
- package/src/knowledge/queries.ts +274 -222
- package/src/operations/activities/api-schemas.ts +80 -80
- package/src/operations/activities/types.ts +64 -64
- package/src/organization-model/README.md +149 -109
- package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
- package/src/organization-model/__tests__/defaults.test.ts +168 -194
- package/src/organization-model/__tests__/domains/actions.test.ts +78 -0
- package/src/organization-model/__tests__/domains/customers.test.ts +48 -44
- package/src/organization-model/__tests__/domains/entities.test.ts +56 -0
- package/src/organization-model/__tests__/domains/goals.test.ts +110 -96
- package/src/organization-model/__tests__/domains/identity.test.ts +4 -3
- package/src/organization-model/__tests__/domains/navigation.test.ts +222 -166
- package/src/organization-model/__tests__/domains/offerings.test.ts +83 -88
- package/src/organization-model/__tests__/domains/policies.test.ts +323 -0
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +30 -30
- package/src/organization-model/__tests__/domains/resources.test.ts +396 -175
- package/src/organization-model/__tests__/domains/roles.test.ts +463 -402
- package/src/organization-model/__tests__/domains/statuses.test.ts +13 -10
- package/src/organization-model/__tests__/domains/systems.test.ts +209 -193
- package/src/organization-model/__tests__/flatten-additive-merge.test.ts +362 -0
- package/src/organization-model/__tests__/foundation.test.ts +47 -75
- package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -0
- package/src/organization-model/__tests__/graph.test.ts +1336 -149
- package/src/organization-model/__tests__/icons.test.ts +10 -1
- package/src/organization-model/__tests__/knowledge.test.ts +418 -61
- package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -0
- package/src/organization-model/__tests__/migration-helpers.test.ts +591 -0
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +103 -94
- package/src/organization-model/__tests__/recursive-system-schema.test.ts +549 -0
- package/src/organization-model/__tests__/resolve.test.ts +303 -42
- package/src/organization-model/__tests__/schema.test.ts +863 -153
- package/src/organization-model/__tests__/surface-projection.test.ts +284 -174
- package/src/organization-model/catalogs/lead-gen.ts +144 -0
- package/src/organization-model/content-kinds/config.ts +36 -0
- package/src/organization-model/content-kinds/index.ts +78 -0
- package/src/organization-model/content-kinds/pipeline.ts +68 -0
- package/src/organization-model/content-kinds/registry.ts +44 -0
- package/src/organization-model/content-kinds/status.ts +71 -0
- package/src/organization-model/content-kinds/template.ts +83 -0
- package/src/organization-model/content-kinds/types.ts +117 -0
- package/src/organization-model/contracts.ts +27 -17
- package/src/organization-model/defaults.ts +489 -107
- package/src/organization-model/domains/actions.ts +333 -0
- package/src/organization-model/domains/customers.ts +10 -7
- package/src/organization-model/domains/entities.ts +144 -0
- package/src/organization-model/domains/goals.ts +9 -6
- package/src/organization-model/domains/knowledge.ts +128 -54
- package/src/organization-model/domains/navigation.ts +139 -416
- package/src/organization-model/domains/offerings.ts +15 -10
- package/src/organization-model/domains/policies.ts +102 -0
- package/src/organization-model/domains/projects.ts +6 -40
- package/src/organization-model/domains/prospecting.ts +395 -514
- package/src/organization-model/domains/resources.ts +173 -81
- package/src/organization-model/domains/roles.ts +96 -93
- package/src/organization-model/domains/sales.test.ts +218 -218
- package/src/organization-model/domains/sales.ts +380 -589
- package/src/organization-model/domains/shared.ts +8 -8
- package/src/organization-model/domains/statuses.ts +298 -89
- package/src/organization-model/domains/systems.ts +240 -38
- package/src/organization-model/foundation.ts +35 -48
- package/src/organization-model/graph/build.ts +1035 -279
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/link.ts +10 -10
- package/src/organization-model/graph/schema.ts +77 -56
- package/src/organization-model/graph/types.ts +75 -56
- package/src/organization-model/helpers.ts +312 -59
- package/src/organization-model/icons.ts +78 -66
- package/src/organization-model/index.ts +129 -16
- package/src/organization-model/migration-helpers.ts +252 -0
- package/src/organization-model/ontology.ts +661 -0
- package/src/organization-model/organization-graph.mdx +110 -89
- package/src/organization-model/organization-model.mdx +226 -171
- package/src/organization-model/published.ts +295 -139
- package/src/organization-model/resolve.ts +139 -21
- package/src/organization-model/schema.ts +841 -301
- package/src/organization-model/surface-projection.ts +212 -218
- package/src/organization-model/types.ts +181 -90
- package/src/platform/api/types.ts +38 -38
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/index.ts +23 -23
- package/src/platform/registry/__tests__/command-view.test.ts +5 -7
- package/src/platform/registry/__tests__/resource-link.test.ts +35 -30
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +17 -32
- package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -0
- package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2051
- package/src/platform/registry/__tests__/validation.test.ts +1347 -1343
- package/src/platform/registry/command-view.ts +10 -10
- package/src/platform/registry/index.ts +103 -103
- package/src/platform/registry/resource-link.ts +32 -32
- package/src/platform/registry/resource-registry.ts +890 -878
- package/src/platform/registry/serialization.ts +295 -295
- package/src/platform/registry/serialized-types.ts +166 -166
- package/src/platform/registry/stats-types.ts +68 -68
- package/src/platform/registry/types.ts +425 -425
- package/src/platform/registry/validation.ts +745 -743
- package/src/platform/utils/__tests__/validation.test.ts +1084 -1084
- package/src/platform/utils/validation.ts +425 -425
- package/src/projects/api-schemas.test.ts +39 -39
- package/src/projects/api-schemas.ts +291 -291
- package/src/reference/_generated/contracts.md +2389 -2121
- package/src/reference/glossary.md +76 -76
- package/src/scaffold-registry/__tests__/index.test.ts +206 -206
- package/src/scaffold-registry/__tests__/schema.test.ts +166 -166
- package/src/scaffold-registry/index.ts +392 -392
- package/src/scaffold-registry/schema.ts +243 -243
- package/src/server.ts +289 -289
- package/src/supabase/database.types.ts +3153 -3093
- package/src/test-utils/README.md +37 -37
- package/src/test-utils/entities.ts +108 -108
- package/src/test-utils/fixtures/memberships.ts +82 -82
- package/src/test-utils/index.ts +12 -12
- package/src/test-utils/organization-model.ts +65 -65
- package/src/test-utils/published.ts +6 -6
- package/src/test-utils/rls/RLSTestContext.ts +588 -588
- package/src/test-utils/test-utils.test.ts +44 -49
- package/src/organization-model/__tests__/domains/operations.test.ts +0 -203
- package/src/organization-model/domains/features.ts +0 -31
- 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
|
|
209
|
-
const result = CustomersDomainSchema.safeParse({
|
|
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
|
|
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
|
|
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
|
-
|
|
224
|
-
|
|
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
|
|
231
|
+
expect(Object.keys(result.data)).toHaveLength(2)
|
|
231
232
|
}
|
|
232
233
|
})
|
|
233
234
|
|
|
234
|
-
it('rejects
|
|
235
|
-
const result = CustomersDomainSchema.safeParse({
|
|
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: {
|
|
258
|
+
customers: {}
|
|
256
259
|
})
|
|
257
|
-
expect(model.customers
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
|
275
|
-
expect(model.customers
|
|
276
|
-
expect(model.customers
|
|
277
|
-
expect(model.customers
|
|
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: {
|
|
284
|
+
customers: { 'seg-isolated': { id: 'seg-isolated', order: 10 } }
|
|
283
285
|
})
|
|
284
286
|
expect(model.identity).toBeDefined()
|
|
285
|
-
expect(model.
|
|
286
|
-
|
|
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
|
|
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
|
|
233
|
-
const result = GoalsDomainSchema.safeParse({
|
|
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
|
|
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
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
406
|
+
it('passes when goals record is empty (no ranges to validate)', () => {
|
|
391
407
|
expect(() =>
|
|
392
408
|
resolveOrganizationModel({
|
|
393
|
-
goals: {
|
|
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
|
|
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
|
|
423
|
+
expect(Object.keys(model.goals)).toHaveLength(0)
|
|
408
424
|
})
|
|
409
425
|
|
|
410
|
-
it('merges partial goals override and preserves empty
|
|
411
|
-
const model = resolveOrganizationModel({ goals: {
|
|
412
|
-
expect(model.goals
|
|
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: {
|
|
433
|
+
roles: { 'role-ceo': { id: 'role-ceo', order: 10, title: 'CEO' } },
|
|
418
434
|
goals: {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
|
430
|
-
expect(model.goals
|
|
431
|
-
expect(model.goals
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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.
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
|
|
476
|
-
expect(
|
|
477
|
-
expect(
|
|
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
|
})
|