@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
|
@@ -20,7 +20,16 @@ describe('OrganizationModelIconTokenSchema', () => {
|
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
it('rejects random strings outside the built-in and custom namespaces', () => {
|
|
23
|
-
for (const token of [
|
|
23
|
+
for (const token of [
|
|
24
|
+
'nav.crm',
|
|
25
|
+
'feature.sales',
|
|
26
|
+
'surface.dashboard',
|
|
27
|
+
'action.approve',
|
|
28
|
+
'status.success',
|
|
29
|
+
'random.icon',
|
|
30
|
+
'custom.',
|
|
31
|
+
'custom.Bad'
|
|
32
|
+
]) {
|
|
24
33
|
expect(OrganizationModelIconTokenSchema.safeParse(token).success).toBe(false)
|
|
25
34
|
}
|
|
26
35
|
})
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import {
|
|
3
3
|
OrgKnowledgeNodeSchema,
|
|
4
4
|
OrgKnowledgeKindSchema,
|
|
5
5
|
KnowledgeDomainSchema,
|
|
6
|
-
KnowledgeLinkSchema
|
|
6
|
+
KnowledgeLinkSchema,
|
|
7
|
+
KnowledgeTargetKindSchema,
|
|
8
|
+
KnowledgeTargetRefSchema
|
|
7
9
|
} from '../domains/knowledge'
|
|
8
10
|
import { OrganizationGraphEdgeKindSchema } from '../graph/schema'
|
|
9
11
|
import { buildOrganizationGraph } from '../graph/build'
|
|
10
12
|
import { DEFAULT_ORGANIZATION_MODEL, DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE } from '../defaults'
|
|
11
13
|
import { resolveOrganizationModel } from '../resolve'
|
|
14
|
+
import { compileOrganizationOntology } from '../ontology'
|
|
15
|
+
import { byOntology, governedBy, parsePath } from '../../knowledge/queries'
|
|
12
16
|
import type { OrganizationModel } from '../types'
|
|
13
17
|
|
|
14
18
|
// ---------------------------------------------------------------------------
|
|
15
|
-
// OrgKnowledgeNodeSchema
|
|
19
|
+
// OrgKnowledgeNodeSchema — shape and validation
|
|
16
20
|
// ---------------------------------------------------------------------------
|
|
17
21
|
|
|
18
22
|
describe('OrgKnowledgeNodeSchema', () => {
|
|
@@ -87,10 +91,10 @@ describe('OrgKnowledgeNodeSchema', () => {
|
|
|
87
91
|
it('accepts an optional semantic icon token', () => {
|
|
88
92
|
const node = OrgKnowledgeNodeSchema.parse({
|
|
89
93
|
...makeNode('playbook'),
|
|
90
|
-
icon: '
|
|
94
|
+
icon: 'playbook'
|
|
91
95
|
})
|
|
92
96
|
|
|
93
|
-
expect(node.icon).toBe('
|
|
97
|
+
expect(node.icon).toBe('playbook')
|
|
94
98
|
})
|
|
95
99
|
|
|
96
100
|
it('rejects an unknown icon token', () => {
|
|
@@ -101,6 +105,33 @@ describe('OrgKnowledgeNodeSchema', () => {
|
|
|
101
105
|
|
|
102
106
|
expect(result.success).toBe(false)
|
|
103
107
|
})
|
|
108
|
+
|
|
109
|
+
it('accepts an optional externalUrl', () => {
|
|
110
|
+
const node = OrgKnowledgeNodeSchema.parse({
|
|
111
|
+
...makeNode('reference'),
|
|
112
|
+
externalUrl: 'https://docs.elevasis.com/technical/architecture'
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
expect(node.externalUrl).toBe('https://docs.elevasis.com/technical/architecture')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('rejects an invalid externalUrl', () => {
|
|
119
|
+
const result = OrgKnowledgeNodeSchema.safeParse({
|
|
120
|
+
...makeNode('reference'),
|
|
121
|
+
externalUrl: 'not-a-url'
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
expect(result.success).toBe(false)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('accepts an optional generated sourceFilePath', () => {
|
|
128
|
+
const node = OrgKnowledgeNodeSchema.parse({
|
|
129
|
+
...makeNode('reference'),
|
|
130
|
+
sourceFilePath: 'packages/elevasis-core/src/knowledge/nodes/platform/reference.mdx'
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
expect(node.sourceFilePath).toBe('packages/elevasis-core/src/knowledge/nodes/platform/reference.mdx')
|
|
134
|
+
})
|
|
104
135
|
})
|
|
105
136
|
|
|
106
137
|
describe('resolveOrganizationModel with knowledge owner roles', () => {
|
|
@@ -117,8 +148,9 @@ describe('resolveOrganizationModel with knowledge owner roles', () => {
|
|
|
117
148
|
it('passes when ownerIds reference declared role ids', () => {
|
|
118
149
|
expect(() =>
|
|
119
150
|
resolveOrganizationModel({
|
|
120
|
-
roles: {
|
|
121
|
-
|
|
151
|
+
roles: { 'role.ops-lead': { id: 'role.ops-lead', order: 10, title: 'Ops Lead' } },
|
|
152
|
+
// Phase 4: knowledge is a flat Record<id, OrgKnowledgeNode>
|
|
153
|
+
knowledge: { 'knowledge.test-owner': node }
|
|
122
154
|
})
|
|
123
155
|
).not.toThrow()
|
|
124
156
|
})
|
|
@@ -126,14 +158,123 @@ describe('resolveOrganizationModel with knowledge owner roles', () => {
|
|
|
126
158
|
it('throws when ownerIds reference unknown role ids', () => {
|
|
127
159
|
expect(() =>
|
|
128
160
|
resolveOrganizationModel({
|
|
129
|
-
knowledge: {
|
|
161
|
+
knowledge: { 'knowledge.test-owner': node }
|
|
130
162
|
})
|
|
131
163
|
).toThrow(/unknown owner role/)
|
|
132
164
|
})
|
|
133
165
|
})
|
|
134
166
|
|
|
167
|
+
describe('resolveOrganizationModel with ontology knowledge targets', () => {
|
|
168
|
+
it('accepts knowledge links that directly target compiled ontology records', () => {
|
|
169
|
+
expect(() =>
|
|
170
|
+
resolveOrganizationModel({
|
|
171
|
+
systems: {
|
|
172
|
+
'sales.crm': {
|
|
173
|
+
id: 'sales.crm',
|
|
174
|
+
order: 10,
|
|
175
|
+
label: 'CRM',
|
|
176
|
+
enabled: true,
|
|
177
|
+
ontology: {
|
|
178
|
+
objectTypes: {
|
|
179
|
+
'sales.crm:object/deal': {
|
|
180
|
+
id: 'sales.crm:object/deal',
|
|
181
|
+
label: 'Deal'
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
knowledge: {
|
|
188
|
+
'knowledge.deal-policy': {
|
|
189
|
+
id: 'knowledge.deal-policy',
|
|
190
|
+
kind: 'reference',
|
|
191
|
+
title: 'Deal Policy',
|
|
192
|
+
summary: 'How deal records are governed.',
|
|
193
|
+
body: 'Deal policy.',
|
|
194
|
+
links: [{ target: { kind: 'ontology', id: 'sales.crm:object/deal' } }],
|
|
195
|
+
updatedAt: '2026-05-14'
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
).not.toThrow()
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
it('rejects knowledge links to unknown ontology records', () => {
|
|
203
|
+
expect(() =>
|
|
204
|
+
resolveOrganizationModel({
|
|
205
|
+
knowledge: {
|
|
206
|
+
'knowledge.missing-ontology': {
|
|
207
|
+
id: 'knowledge.missing-ontology',
|
|
208
|
+
kind: 'reference',
|
|
209
|
+
title: 'Missing Ontology',
|
|
210
|
+
summary: 'References a missing ontology record.',
|
|
211
|
+
body: 'Missing.',
|
|
212
|
+
links: [{ target: { kind: 'ontology', id: 'sales.crm:object/missing' } }],
|
|
213
|
+
updatedAt: '2026-05-14'
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
).toThrow(/unknown ontology target/)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('compiles authored ontology targets without requiring legacy provenance fields', () => {
|
|
221
|
+
const model = resolveOrganizationModel({
|
|
222
|
+
systems: {
|
|
223
|
+
'sales.crm': {
|
|
224
|
+
id: 'sales.crm',
|
|
225
|
+
order: 10,
|
|
226
|
+
label: 'CRM',
|
|
227
|
+
enabled: true,
|
|
228
|
+
ontology: {
|
|
229
|
+
objectTypes: {
|
|
230
|
+
'sales.crm:object/deal': {
|
|
231
|
+
id: 'sales.crm:object/deal',
|
|
232
|
+
label: 'Deal'
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
actionTypes: {
|
|
236
|
+
'sales.crm:action/update-deal': {
|
|
237
|
+
id: 'sales.crm:action/update-deal',
|
|
238
|
+
label: 'Update Deal',
|
|
239
|
+
actsOn: ['sales.crm:object/deal']
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
catalogTypes: {
|
|
243
|
+
'sales.crm:catalog/pipeline': {
|
|
244
|
+
id: 'sales.crm:catalog/pipeline',
|
|
245
|
+
label: 'Pipeline',
|
|
246
|
+
appliesTo: 'sales.crm:object/deal'
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
knowledge: {
|
|
253
|
+
'knowledge.pipeline-policy': {
|
|
254
|
+
id: 'knowledge.pipeline-policy',
|
|
255
|
+
kind: 'reference',
|
|
256
|
+
title: 'Pipeline Policy',
|
|
257
|
+
summary: 'How pipeline ontology is governed.',
|
|
258
|
+
body: 'Pipeline policy.',
|
|
259
|
+
links: [{ target: { kind: 'ontology', id: 'sales.crm:catalog/pipeline' } }],
|
|
260
|
+
updatedAt: '2026-05-14'
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
const compiled = compileOrganizationOntology(model)
|
|
265
|
+
|
|
266
|
+
expect(compiled.diagnostics).toEqual([])
|
|
267
|
+
expect(compiled.ontology.objectTypes['sales.crm:object/deal']).not.toHaveProperty('legacyEntityId')
|
|
268
|
+
expect(compiled.ontology.actionTypes['sales.crm:action/update-deal']).not.toHaveProperty('legacyActionId')
|
|
269
|
+
expect(compiled.ontology.catalogTypes['sales.crm:catalog/pipeline']).not.toHaveProperty('legacyContentId')
|
|
270
|
+
expect(model.knowledge['knowledge.pipeline-policy']?.links[0]?.nodeId).toBe(
|
|
271
|
+
'ontology:sales.crm:catalog/pipeline'
|
|
272
|
+
)
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
|
|
135
276
|
// ---------------------------------------------------------------------------
|
|
136
|
-
// OrgKnowledgeKindSchema
|
|
277
|
+
// OrgKnowledgeKindSchema — enum shape
|
|
137
278
|
// ---------------------------------------------------------------------------
|
|
138
279
|
|
|
139
280
|
describe('OrgKnowledgeKindSchema', () => {
|
|
@@ -150,7 +291,7 @@ describe('OrgKnowledgeKindSchema', () => {
|
|
|
150
291
|
})
|
|
151
292
|
|
|
152
293
|
// ---------------------------------------------------------------------------
|
|
153
|
-
// OrganizationGraphEdgeKindSchema
|
|
294
|
+
// OrganizationGraphEdgeKindSchema — governs edge is present
|
|
154
295
|
// ---------------------------------------------------------------------------
|
|
155
296
|
|
|
156
297
|
describe('OrganizationGraphEdgeKindSchema', () => {
|
|
@@ -160,60 +301,79 @@ describe('OrganizationGraphEdgeKindSchema', () => {
|
|
|
160
301
|
})
|
|
161
302
|
|
|
162
303
|
// ---------------------------------------------------------------------------
|
|
163
|
-
// DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE
|
|
304
|
+
// DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE — Phase 4: flat Record<id, OrgKnowledgeNode> (D3).
|
|
305
|
+
// The wrapper shape { version, lastModified, nodes[] } was removed; versioning
|
|
306
|
+
// is in domainMetadata.knowledge. The default is an empty map {}.
|
|
164
307
|
// ---------------------------------------------------------------------------
|
|
165
308
|
|
|
166
309
|
describe('DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE', () => {
|
|
167
|
-
it('
|
|
168
|
-
expect(DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE
|
|
310
|
+
it('is an empty object (flat map with no pre-seeded nodes)', () => {
|
|
311
|
+
expect(DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE).toEqual({})
|
|
169
312
|
})
|
|
170
313
|
|
|
171
314
|
it('passes KnowledgeDomainSchema.parse without throwing', () => {
|
|
172
315
|
expect(() => KnowledgeDomainSchema.parse(DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE)).not.toThrow()
|
|
173
316
|
})
|
|
317
|
+
|
|
318
|
+
it('does not have a .nodes array (wrapper shape was removed in Phase 4)', () => {
|
|
319
|
+
expect(DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE).not.toHaveProperty('nodes')
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('does not have a .version field (moved to domainMetadata.knowledge)', () => {
|
|
323
|
+
expect(DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE).not.toHaveProperty('version')
|
|
324
|
+
})
|
|
174
325
|
})
|
|
175
326
|
|
|
176
327
|
// ---------------------------------------------------------------------------
|
|
177
|
-
// buildOrganizationGraph
|
|
178
|
-
// Synthetic OM
|
|
179
|
-
// knowledge nodes (empty after Wave 2a, Decision 2-B).
|
|
328
|
+
// buildOrganizationGraph — knowledge nodes + governs edges
|
|
329
|
+
// Synthetic OM authored via flat Record<id, OrgKnowledgeNode> per Phase 4 schema.
|
|
180
330
|
// ---------------------------------------------------------------------------
|
|
181
331
|
|
|
332
|
+
// Phase 4: knowledge is a flat Record<id, OrgKnowledgeNode> — no .nodes array wrapper.
|
|
182
333
|
const SYNTHETIC_OM: OrganizationModel = {
|
|
183
334
|
...DEFAULT_ORGANIZATION_MODEL,
|
|
335
|
+
systems: Object.fromEntries(
|
|
336
|
+
Object.entries(DEFAULT_ORGANIZATION_MODEL.systems).map(([key, system]) => [
|
|
337
|
+
key,
|
|
338
|
+
key === 'sales.crm' || key === 'sales.lead-gen'
|
|
339
|
+
? { ...system, governedByKnowledge: ['knowledge.test-playbook-1'] }
|
|
340
|
+
: system
|
|
341
|
+
])
|
|
342
|
+
),
|
|
184
343
|
knowledge: {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
344
|
+
'knowledge.test-playbook-1': {
|
|
345
|
+
id: 'knowledge.test-playbook-1',
|
|
346
|
+
kind: 'playbook',
|
|
347
|
+
title: 'Test Playbook',
|
|
348
|
+
summary: 'Test playbook summary.',
|
|
349
|
+
body: '## Overview\n\nContent.',
|
|
350
|
+
links: [
|
|
351
|
+
{ target: { kind: 'system', id: 'sales.crm' }, nodeId: 'system:sales.crm' },
|
|
352
|
+
{ target: { kind: 'system', id: 'sales.lead-gen' }, nodeId: 'system:sales.lead-gen' }
|
|
353
|
+
],
|
|
354
|
+
ownerIds: [],
|
|
355
|
+
updatedAt: '2026-01-01'
|
|
356
|
+
},
|
|
357
|
+
'knowledge.test-strategy-1': {
|
|
358
|
+
id: 'knowledge.test-strategy-1',
|
|
359
|
+
kind: 'strategy',
|
|
360
|
+
title: 'Test Strategy',
|
|
361
|
+
summary: 'Test strategy summary.',
|
|
362
|
+
body: '## Strategy\n\nContent.',
|
|
363
|
+
links: [],
|
|
364
|
+
ownerIds: [],
|
|
365
|
+
updatedAt: '2026-01-02'
|
|
366
|
+
},
|
|
367
|
+
'knowledge.test-reference-1': {
|
|
368
|
+
id: 'knowledge.test-reference-1',
|
|
369
|
+
kind: 'reference',
|
|
370
|
+
title: 'Test Reference',
|
|
371
|
+
summary: 'Test reference summary.',
|
|
372
|
+
body: '## Reference\n\nContent.',
|
|
373
|
+
links: [],
|
|
374
|
+
ownerIds: [],
|
|
375
|
+
updatedAt: '2026-01-03'
|
|
376
|
+
}
|
|
217
377
|
}
|
|
218
378
|
}
|
|
219
379
|
|
|
@@ -222,11 +382,11 @@ describe('buildOrganizationGraph with knowledge', () => {
|
|
|
222
382
|
|
|
223
383
|
it('emits a knowledge node for each synthetic node', () => {
|
|
224
384
|
const knowledgeNodes = graph.nodes.filter((n) => n.kind === 'knowledge')
|
|
225
|
-
expect(knowledgeNodes.length).toBe(SYNTHETIC_OM.knowledge.
|
|
385
|
+
expect(knowledgeNodes.length).toBe(Object.keys(SYNTHETIC_OM.knowledge).length)
|
|
226
386
|
})
|
|
227
387
|
|
|
228
388
|
it('knowledge nodes carry sourceId matching the OM node id', () => {
|
|
229
|
-
for (const omNode of SYNTHETIC_OM.knowledge
|
|
389
|
+
for (const omNode of Object.values(SYNTHETIC_OM.knowledge)) {
|
|
230
390
|
const graphNode = graph.nodes.find((n) => n.sourceId === omNode.id)
|
|
231
391
|
expect(graphNode).toBeDefined()
|
|
232
392
|
expect(graphNode?.kind).toBe('knowledge')
|
|
@@ -236,12 +396,12 @@ describe('buildOrganizationGraph with knowledge', () => {
|
|
|
236
396
|
|
|
237
397
|
it('emits governs edges for nodes that have links', () => {
|
|
238
398
|
const governsEdges = graph.edges.filter((e) => e.kind === 'governs')
|
|
239
|
-
const totalLinks = SYNTHETIC_OM.knowledge.
|
|
399
|
+
const totalLinks = Object.values(SYNTHETIC_OM.knowledge).reduce((sum, n) => sum + n.links.length, 0)
|
|
240
400
|
expect(governsEdges.length).toBe(totalLinks)
|
|
241
401
|
})
|
|
242
402
|
|
|
243
403
|
it('links targetId resolves to an existing graph node id for at least one node', () => {
|
|
244
|
-
const nodeWithLinks = SYNTHETIC_OM.knowledge.
|
|
404
|
+
const nodeWithLinks = Object.values(SYNTHETIC_OM.knowledge).find((n) => n.links.length > 0)
|
|
245
405
|
expect(nodeWithLinks).toBeDefined()
|
|
246
406
|
|
|
247
407
|
const graphNodeIds = new Set(graph.nodes.map((n) => n.id))
|
|
@@ -255,16 +415,116 @@ describe('buildOrganizationGraph with knowledge', () => {
|
|
|
255
415
|
const defaultKnowledgeNodes = defaultGraph.nodes.filter((n) => n.kind === 'knowledge')
|
|
256
416
|
expect(defaultKnowledgeNodes).toHaveLength(0)
|
|
257
417
|
})
|
|
418
|
+
|
|
419
|
+
it('emits governs edges from knowledge directly to ontology graph nodes', () => {
|
|
420
|
+
const model = resolveOrganizationModel({
|
|
421
|
+
systems: {
|
|
422
|
+
'sales.crm': {
|
|
423
|
+
id: 'sales.crm',
|
|
424
|
+
order: 10,
|
|
425
|
+
label: 'CRM',
|
|
426
|
+
enabled: true,
|
|
427
|
+
ontology: {
|
|
428
|
+
objectTypes: {
|
|
429
|
+
'sales.crm:object/deal': {
|
|
430
|
+
id: 'sales.crm:object/deal',
|
|
431
|
+
label: 'Deal'
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
knowledge: {
|
|
438
|
+
'knowledge.deal-policy': {
|
|
439
|
+
id: 'knowledge.deal-policy',
|
|
440
|
+
kind: 'reference',
|
|
441
|
+
title: 'Deal Policy',
|
|
442
|
+
summary: 'How deal records are governed.',
|
|
443
|
+
body: 'Deal policy.',
|
|
444
|
+
links: [{ target: { kind: 'ontology', id: 'sales.crm:object/deal' } }],
|
|
445
|
+
ownerIds: [],
|
|
446
|
+
updatedAt: '2026-05-14'
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
})
|
|
450
|
+
const ontologyGraph = buildOrganizationGraph({ organizationModel: model })
|
|
451
|
+
|
|
452
|
+
expect(
|
|
453
|
+
ontologyGraph.edges.find(
|
|
454
|
+
(edge) =>
|
|
455
|
+
edge.kind === 'governs' &&
|
|
456
|
+
edge.sourceId === 'knowledge:knowledge.deal-policy' &&
|
|
457
|
+
edge.targetId === 'ontology:sales.crm:object/deal'
|
|
458
|
+
)
|
|
459
|
+
).toBeDefined()
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
it('knowledge query helpers traverse ontology graph node IDs', () => {
|
|
463
|
+
const model = resolveOrganizationModel({
|
|
464
|
+
systems: {
|
|
465
|
+
'sales.crm': {
|
|
466
|
+
id: 'sales.crm',
|
|
467
|
+
order: 10,
|
|
468
|
+
label: 'CRM',
|
|
469
|
+
enabled: true,
|
|
470
|
+
ontology: {
|
|
471
|
+
objectTypes: {
|
|
472
|
+
'sales.crm:object/deal': {
|
|
473
|
+
id: 'sales.crm:object/deal',
|
|
474
|
+
label: 'Deal'
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
knowledge: {
|
|
481
|
+
'knowledge.deal-policy': {
|
|
482
|
+
id: 'knowledge.deal-policy',
|
|
483
|
+
kind: 'reference',
|
|
484
|
+
title: 'Deal Policy',
|
|
485
|
+
summary: 'How deal records are governed.',
|
|
486
|
+
body: 'Deal policy.',
|
|
487
|
+
links: [{ target: { kind: 'ontology', id: 'sales.crm:object/deal' } }],
|
|
488
|
+
ownerIds: [],
|
|
489
|
+
updatedAt: '2026-05-14'
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
})
|
|
493
|
+
const ontologyGraph = buildOrganizationGraph({ organizationModel: model })
|
|
494
|
+
const nodes = Object.values(model.knowledge)
|
|
495
|
+
|
|
496
|
+
expect(byOntology(ontologyGraph, 'sales.crm:object/deal', nodes).map((node) => node.id)).toEqual([
|
|
497
|
+
'knowledge.deal-policy'
|
|
498
|
+
])
|
|
499
|
+
expect(governedBy(ontologyGraph, 'ontology:sales.crm:object/deal')).toEqual(['knowledge:knowledge.deal-policy'])
|
|
500
|
+
expect(parsePath('/by-ontology/sales.crm:object/deal')).toEqual({
|
|
501
|
+
mount: 'by-ontology',
|
|
502
|
+
args: ['sales.crm:object/deal']
|
|
503
|
+
})
|
|
504
|
+
})
|
|
258
505
|
})
|
|
259
506
|
|
|
260
507
|
// ---------------------------------------------------------------------------
|
|
261
|
-
// KnowledgeLinkSchema
|
|
508
|
+
// KnowledgeLinkSchema — nodeId format validation
|
|
262
509
|
// ---------------------------------------------------------------------------
|
|
263
510
|
|
|
264
511
|
describe('KnowledgeLinkSchema', () => {
|
|
265
|
-
it('accepts a
|
|
266
|
-
|
|
267
|
-
|
|
512
|
+
it('accepts and normalizes a canonical typed target', () => {
|
|
513
|
+
const link = KnowledgeLinkSchema.parse({ target: { kind: 'system', id: 'sales.crm' } })
|
|
514
|
+
|
|
515
|
+
expect(link).toEqual({
|
|
516
|
+
target: { kind: 'system', id: 'sales.crm' },
|
|
517
|
+
nodeId: 'system:sales.crm'
|
|
518
|
+
})
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
it('accepts and normalizes a legacy kind:dotted-path nodeId', () => {
|
|
522
|
+
const link = KnowledgeLinkSchema.parse({ nodeId: 'system:sales.lead-gen' })
|
|
523
|
+
|
|
524
|
+
expect(link).toEqual({
|
|
525
|
+
target: { kind: 'system', id: 'sales.lead-gen' },
|
|
526
|
+
nodeId: 'system:sales.lead-gen'
|
|
527
|
+
})
|
|
268
528
|
})
|
|
269
529
|
|
|
270
530
|
it('accepts resource: and knowledge: prefixed node IDs', () => {
|
|
@@ -272,8 +532,20 @@ describe('KnowledgeLinkSchema', () => {
|
|
|
272
532
|
expect(() => KnowledgeLinkSchema.parse({ nodeId: 'knowledge:knowledge.test-doc' })).not.toThrow()
|
|
273
533
|
})
|
|
274
534
|
|
|
535
|
+
it('accepts content-node: prefixed node IDs (D4 — KnowledgeTargetKindSchema includes content-node)', () => {
|
|
536
|
+
expect(() => KnowledgeLinkSchema.parse({ nodeId: 'content-node:sales.crm:default-pipeline' })).not.toThrow()
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
it('accepts and normalizes ontology graph node IDs', () => {
|
|
540
|
+
const link = KnowledgeLinkSchema.parse({ nodeId: 'ontology:sales.crm:object/deal' })
|
|
541
|
+
|
|
542
|
+
expect(link).toEqual({
|
|
543
|
+
target: { kind: 'ontology', id: 'sales.crm:object/deal' },
|
|
544
|
+
nodeId: 'ontology:sales.crm:object/deal'
|
|
545
|
+
})
|
|
546
|
+
})
|
|
547
|
+
|
|
275
548
|
it('rejects a bare dotted ID with no kind: prefix', () => {
|
|
276
|
-
// NodeIdStringSchema requires kind:dotted-path format
|
|
277
549
|
expect(KnowledgeLinkSchema.safeParse({ nodeId: 'sales.crm' }).success).toBe(false)
|
|
278
550
|
})
|
|
279
551
|
|
|
@@ -282,8 +554,7 @@ describe('KnowledgeLinkSchema', () => {
|
|
|
282
554
|
})
|
|
283
555
|
|
|
284
556
|
it('rejects a nodeId with uppercase characters', () => {
|
|
285
|
-
|
|
286
|
-
expect(KnowledgeLinkSchema.safeParse({ nodeId: 'Feature:Sales.CRM' }).success).toBe(false)
|
|
557
|
+
expect(KnowledgeLinkSchema.safeParse({ nodeId: 'system:Sales.CRM' }).success).toBe(false)
|
|
287
558
|
})
|
|
288
559
|
|
|
289
560
|
it('rejects a link missing the nodeId field', () => {
|
|
@@ -295,8 +566,95 @@ describe('KnowledgeLinkSchema', () => {
|
|
|
295
566
|
})
|
|
296
567
|
})
|
|
297
568
|
|
|
569
|
+
describe('KnowledgeTargetRefSchema', () => {
|
|
570
|
+
it('accepts modeled target kinds', () => {
|
|
571
|
+
expect(KnowledgeTargetRefSchema.safeParse({ kind: 'system', id: 'sales.crm' }).success).toBe(true)
|
|
572
|
+
expect(KnowledgeTargetRefSchema.safeParse({ kind: 'customer-segment', id: 'customer.enterprise' }).success).toBe(
|
|
573
|
+
true
|
|
574
|
+
)
|
|
575
|
+
})
|
|
576
|
+
|
|
577
|
+
it('accepts content-node as a target kind (D4)', () => {
|
|
578
|
+
expect(KnowledgeTargetKindSchema.safeParse('content-node').success).toBe(true)
|
|
579
|
+
expect(KnowledgeTargetRefSchema.safeParse({ kind: 'content-node', id: 'sales.crm:default-pipeline' }).success).toBe(
|
|
580
|
+
true
|
|
581
|
+
)
|
|
582
|
+
})
|
|
583
|
+
|
|
584
|
+
it('keeps content-node targets as compatibility pass-through links', () => {
|
|
585
|
+
const link = KnowledgeLinkSchema.parse({
|
|
586
|
+
target: { kind: 'content-node', id: 'sales.crm:default-pipeline' }
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
expect(link).toEqual({
|
|
590
|
+
target: { kind: 'content-node', id: 'sales.crm:default-pipeline' },
|
|
591
|
+
nodeId: 'content-node:sales.crm:default-pipeline'
|
|
592
|
+
})
|
|
593
|
+
})
|
|
594
|
+
|
|
595
|
+
it('accepts ontology as a target kind when the id is a valid ontology id', () => {
|
|
596
|
+
expect(KnowledgeTargetKindSchema.safeParse('ontology').success).toBe(true)
|
|
597
|
+
expect(KnowledgeTargetRefSchema.safeParse({ kind: 'ontology', id: 'sales.crm:object/deal' }).success).toBe(true)
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
it('rejects ontology targets with malformed ontology ids', () => {
|
|
601
|
+
expect(KnowledgeTargetRefSchema.safeParse({ kind: 'ontology', id: 'sales.crm.deal' }).success).toBe(false)
|
|
602
|
+
})
|
|
603
|
+
|
|
604
|
+
it('rejects unknown target kinds', () => {
|
|
605
|
+
expect(KnowledgeTargetKindSchema.safeParse('integration').success).toBe(false)
|
|
606
|
+
expect(KnowledgeTargetRefSchema.safeParse({ kind: 'integration', id: 'hubspot' }).success).toBe(false)
|
|
607
|
+
})
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
// ---------------------------------------------------------------------------
|
|
611
|
+
// KnowledgeDomainSchema — flat Record shape (Phase 4 D3)
|
|
612
|
+
// ---------------------------------------------------------------------------
|
|
613
|
+
|
|
614
|
+
describe('KnowledgeDomainSchema — flat Record<id, OrgKnowledgeNode>', () => {
|
|
615
|
+
it('accepts an empty map', () => {
|
|
616
|
+
expect(KnowledgeDomainSchema.safeParse({}).success).toBe(true)
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
it('defaults to empty map when undefined', () => {
|
|
620
|
+
const result = KnowledgeDomainSchema.safeParse(undefined)
|
|
621
|
+
expect(result.success).toBe(true)
|
|
622
|
+
if (result.success) {
|
|
623
|
+
expect(result.data).toEqual({})
|
|
624
|
+
}
|
|
625
|
+
})
|
|
626
|
+
|
|
627
|
+
it('accepts a Record keyed by knowledge node ids', () => {
|
|
628
|
+
const result = KnowledgeDomainSchema.safeParse({
|
|
629
|
+
'knowledge.my-playbook': {
|
|
630
|
+
id: 'knowledge.my-playbook',
|
|
631
|
+
kind: 'playbook',
|
|
632
|
+
title: 'My Playbook',
|
|
633
|
+
summary: 'A test playbook.',
|
|
634
|
+
body: '## Content',
|
|
635
|
+
links: [],
|
|
636
|
+
ownerIds: [],
|
|
637
|
+
updatedAt: '2026-05-01'
|
|
638
|
+
}
|
|
639
|
+
})
|
|
640
|
+
expect(result.success).toBe(true)
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
it('rejects the old wrapper shape { version, lastModified, nodes[] }', () => {
|
|
644
|
+
// The old wrapper-with-array shape does not parse as a Record<id, OrgKnowledgeNode>
|
|
645
|
+
const result = KnowledgeDomainSchema.safeParse({
|
|
646
|
+
version: 1,
|
|
647
|
+
lastModified: '2026-05-10',
|
|
648
|
+
nodes: []
|
|
649
|
+
})
|
|
650
|
+
// Each key ('version', 'lastModified', 'nodes') would be parsed as a knowledge node id,
|
|
651
|
+
// and the values are not valid OrgKnowledgeNodeSchema — so this should fail
|
|
652
|
+
expect(result.success).toBe(false)
|
|
653
|
+
})
|
|
654
|
+
})
|
|
655
|
+
|
|
298
656
|
// ---------------------------------------------------------------------------
|
|
299
|
-
// OrgKnowledgeNodeSchema
|
|
657
|
+
// OrgKnowledgeNodeSchema — field length constraints
|
|
300
658
|
// ---------------------------------------------------------------------------
|
|
301
659
|
|
|
302
660
|
describe('OrgKnowledgeNodeSchema length constraints', () => {
|
|
@@ -347,7 +705,6 @@ describe('OrgKnowledgeNodeSchema length constraints', () => {
|
|
|
347
705
|
})
|
|
348
706
|
|
|
349
707
|
it('rejects id with uppercase characters', () => {
|
|
350
|
-
// ModelIdSchema requires lowercase kebab/dot/underscore separated IDs
|
|
351
708
|
const result = OrgKnowledgeNodeSchema.safeParse({ ...BASE, id: 'Knowledge.Test' })
|
|
352
709
|
expect(result.success).toBe(false)
|
|
353
710
|
})
|