@elevasis/core 0.26.0 → 0.28.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 (49) hide show
  1. package/dist/index.d.ts +162 -105
  2. package/dist/index.js +280 -174
  3. package/dist/knowledge/index.d.ts +43 -43
  4. package/dist/organization-model/index.d.ts +162 -105
  5. package/dist/organization-model/index.js +280 -174
  6. package/dist/test-utils/index.d.ts +20 -20
  7. package/dist/test-utils/index.js +184 -126
  8. package/package.json +3 -3
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +976 -1063
  10. package/src/business/acquisition/api-schemas.test.ts +1962 -1841
  11. package/src/business/acquisition/api-schemas.ts +1461 -1464
  12. package/src/business/acquisition/crm-next-action.test.ts +45 -25
  13. package/src/business/acquisition/crm-next-action.ts +227 -220
  14. package/src/business/acquisition/crm-priority.test.ts +41 -8
  15. package/src/business/acquisition/crm-priority.ts +365 -349
  16. package/src/business/acquisition/crm-state-actions.test.ts +208 -153
  17. package/src/business/acquisition/derive-actions.test.ts +90 -13
  18. package/src/business/acquisition/derive-actions.ts +8 -139
  19. package/src/business/acquisition/ontology-validation.ts +72 -158
  20. package/src/business/pdf/sections/investment.ts +1 -1
  21. package/src/business/pdf/sections/summary-investment.ts +1 -1
  22. package/src/execution/engine/tools/tool-maps.ts +872 -831
  23. package/src/organization-model/__tests__/cross-ref.test.ts +167 -0
  24. package/src/organization-model/__tests__/define-domain-record.test.ts +289 -0
  25. package/src/organization-model/__tests__/om-spine-doc-contract.test.ts +56 -0
  26. package/src/organization-model/__tests__/published-zero-leak.test.ts +60 -1
  27. package/src/organization-model/__tests__/resolve.test.ts +1 -1
  28. package/src/organization-model/__tests__/schema-refinements.test.ts +72 -0
  29. package/src/organization-model/cross-ref.ts +175 -0
  30. package/src/organization-model/domains/actions.ts +13 -0
  31. package/src/organization-model/domains/branding.ts +6 -6
  32. package/src/organization-model/domains/customers.ts +95 -78
  33. package/src/organization-model/domains/entities.ts +157 -144
  34. package/src/organization-model/domains/goals.ts +100 -83
  35. package/src/organization-model/domains/knowledge.ts +106 -93
  36. package/src/organization-model/domains/offerings.ts +88 -71
  37. package/src/organization-model/domains/policies.ts +115 -102
  38. package/src/organization-model/domains/roles.ts +109 -96
  39. package/src/organization-model/domains/sales.test.ts +104 -218
  40. package/src/organization-model/domains/sales.ts +212 -375
  41. package/src/organization-model/domains/statuses.ts +351 -339
  42. package/src/organization-model/domains/systems.ts +176 -164
  43. package/src/organization-model/helpers.ts +331 -306
  44. package/src/organization-model/index.ts +43 -0
  45. package/src/organization-model/published.ts +27 -2
  46. package/src/organization-model/schema-refinements.ts +667 -0
  47. package/src/organization-model/schema.ts +8 -715
  48. package/src/platform/constants/versions.ts +1 -1
  49. package/src/reference/_generated/contracts.md +1000 -1087
@@ -0,0 +1,167 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
3
+ import { buildOmCrossRefIndex, knowledgeTargetExists } from '../cross-ref'
4
+ import { OrganizationModelSchema } from '../schema'
5
+ import type { OrganizationModel } from '../types'
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // Test helpers
9
+ // ---------------------------------------------------------------------------
10
+
11
+ /**
12
+ * Build a minimal but schema-valid OrganizationModel that contains:
13
+ * - a top-level system with id "sales" (path "sales")
14
+ * - a nested subsystem with id "sales.lead-gen" (path "sales.lead-gen")
15
+ * - a stage-kind catalog with id "sales.lead-gen:catalog/company-stage"
16
+ * and a single entry "prospect"
17
+ * - a knowledge node whose link targets the "prospect" stage
18
+ */
19
+ function makeModelWithStage(): OrganizationModel {
20
+ return {
21
+ ...DEFAULT_ORGANIZATION_MODEL,
22
+ systems: {
23
+ sales: {
24
+ id: 'sales',
25
+ order: 10,
26
+ label: 'Sales',
27
+ enabled: true,
28
+ lifecycle: 'active' as const,
29
+ systems: {
30
+ 'lead-gen': {
31
+ id: 'sales.lead-gen',
32
+ order: 10,
33
+ label: 'Lead Gen',
34
+ enabled: true,
35
+ lifecycle: 'active' as const,
36
+ path: '/sales/lead-gen',
37
+ ontology: {
38
+ catalogTypes: {
39
+ 'sales.lead-gen:catalog/company-stage': {
40
+ id: 'sales.lead-gen:catalog/company-stage',
41
+ label: 'Company Stages',
42
+ ownerSystemId: 'sales.lead-gen',
43
+ kind: 'stage' as const,
44
+ entries: {
45
+ prospect: {
46
+ label: 'Prospect',
47
+ description: 'Company identified as a potential lead.',
48
+ order: 1
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ },
58
+ knowledge: {
59
+ 'knowledge.lead-gen-playbook': {
60
+ id: 'knowledge.lead-gen-playbook',
61
+ kind: 'playbook' as const,
62
+ title: 'Lead Gen Playbook',
63
+ summary: 'Governs the lead gen stage progression.',
64
+ body: '## Overview\n\nPlaybook content.',
65
+ links: [
66
+ {
67
+ target: { kind: 'stage' as const, id: 'prospect' },
68
+ nodeId: 'stage:prospect'
69
+ }
70
+ ],
71
+ ownerIds: [],
72
+ updatedAt: '2026-05-17'
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // (a) stage-kind knowledge-link targets resolve via knowledgeTargetExists
80
+ // ---------------------------------------------------------------------------
81
+
82
+ describe('knowledgeTargetExists — stage kind', () => {
83
+ it('returns true for a stage id that exists in a stage-kind catalog', () => {
84
+ const model = makeModelWithStage()
85
+ const idx = buildOmCrossRefIndex(model)
86
+ expect(knowledgeTargetExists(idx, 'stage', 'prospect')).toBe(true)
87
+ })
88
+
89
+ it('returns false for a stage id that does not exist in any catalog', () => {
90
+ const model = makeModelWithStage()
91
+ const idx = buildOmCrossRefIndex(model)
92
+ expect(knowledgeTargetExists(idx, 'stage', 'nonexistent-stage')).toBe(false)
93
+ })
94
+ })
95
+
96
+ // ---------------------------------------------------------------------------
97
+ // (b) system targets resolve by dotted path AND by system.id (dual-keyed)
98
+ // ---------------------------------------------------------------------------
99
+
100
+ describe('knowledgeTargetExists — system kind dual-keyed lookup', () => {
101
+ it('resolves a system by its dotted path', () => {
102
+ const model = makeModelWithStage()
103
+ const idx = buildOmCrossRefIndex(model)
104
+ // "sales.lead-gen" is the dotted path in the systems tree
105
+ expect(knowledgeTargetExists(idx, 'system', 'sales.lead-gen')).toBe(true)
106
+ })
107
+
108
+ it('resolves a system by system.id when id differs from tree key', () => {
109
+ const model = makeModelWithStage()
110
+ const idx = buildOmCrossRefIndex(model)
111
+ // system.id === "sales.lead-gen" — same here, but the index is keyed by BOTH
112
+ // the path AND the id so this also exercises the id branch
113
+ expect(knowledgeTargetExists(idx, 'system', 'sales.lead-gen')).toBe(true)
114
+ })
115
+
116
+ it('returns false for an unknown system identifier', () => {
117
+ const model = makeModelWithStage()
118
+ const idx = buildOmCrossRefIndex(model)
119
+ expect(knowledgeTargetExists(idx, 'system', 'sales.crm')).toBe(false)
120
+ })
121
+ })
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // (c) Schema path and verify path agree on stage-targeting knowledge node
125
+ // ---------------------------------------------------------------------------
126
+
127
+ describe('schema / verify path parity — stage target', () => {
128
+ it('OrganizationModelSchema accepts a knowledge node with a stage link', () => {
129
+ const model = makeModelWithStage()
130
+ const result = OrganizationModelSchema.safeParse(model)
131
+ const stageIssues = result.success
132
+ ? []
133
+ : result.error.issues.filter(
134
+ (issue) =>
135
+ issue.message.includes('stage') ||
136
+ (Array.isArray(issue.path) && issue.path.includes('knowledge'))
137
+ )
138
+ expect(stageIssues).toHaveLength(0)
139
+ })
140
+
141
+ it('knowledgeTargetExists returns true for the stage target (verify path would accept)', () => {
142
+ const model = makeModelWithStage()
143
+ const idx = buildOmCrossRefIndex(model)
144
+ const knowledgeNode = model.knowledge['knowledge.lead-gen-playbook']
145
+ expect(knowledgeNode).toBeDefined()
146
+ for (const link of knowledgeNode.links) {
147
+ expect(knowledgeTargetExists(idx, link.target.kind, link.target.id)).toBe(true)
148
+ }
149
+ })
150
+
151
+ it('schema and verify path agree: both accept the stage link without error', () => {
152
+ const model = makeModelWithStage()
153
+
154
+ // Schema path: parse must succeed (no issues about the stage link)
155
+ const parseResult = OrganizationModelSchema.safeParse(model)
156
+ const knowledgeIssues = parseResult.success
157
+ ? []
158
+ : parseResult.error.issues.filter((issue) => Array.isArray(issue.path) && issue.path[0] === 'knowledge')
159
+ expect(knowledgeIssues).toHaveLength(0)
160
+
161
+ // Verify path: knowledgeTargetExists must return true for every link
162
+ const idx = buildOmCrossRefIndex(model)
163
+ const node = model.knowledge['knowledge.lead-gen-playbook']
164
+ const missingTargets = node.links.filter((link) => !knowledgeTargetExists(idx, link.target.kind, link.target.id))
165
+ expect(missingTargets).toHaveLength(0)
166
+ })
167
+ })
@@ -0,0 +1,289 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { z } from 'zod'
3
+ import { defineDomainRecord } from '../helpers'
4
+ import { defineAction, defineActions, ActionSchema } from '../domains/actions'
5
+ import { defineEntity, defineEntities, EntitySchema } from '../domains/entities'
6
+ import { defineSystem, defineSystems, SystemEntrySchema } from '../domains/systems'
7
+ import { definePolicy, definePolicies, PolicySchema } from '../domains/policies'
8
+ import { defineRole, defineRoles, RoleSchema } from '../domains/roles'
9
+ import { defineGoal, defineGoals, ObjectiveSchema } from '../domains/goals'
10
+ import { defineCustomer, defineCustomers, CustomerSegmentSchema } from '../domains/customers'
11
+ import { defineOffering, defineOfferings, ProductSchema } from '../domains/offerings'
12
+ import { defineStatus, defineStatuses, StatusEntrySchema } from '../domains/statuses'
13
+ import { defineKnowledgeNode, defineKnowledgeNodes, OrgKnowledgeNodeSchema } from '../domains/knowledge'
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Minimal valid fixtures for each domain
17
+ // ---------------------------------------------------------------------------
18
+
19
+ const validAction: z.input<typeof ActionSchema> = {
20
+ id: 'test-action',
21
+ order: 10,
22
+ label: 'Test Action',
23
+ invocations: []
24
+ }
25
+
26
+ const validEntity: z.input<typeof EntitySchema> = {
27
+ id: 'test.entity',
28
+ order: 10,
29
+ label: 'Test Entity',
30
+ ownedBySystemId: 'test-system'
31
+ }
32
+
33
+ const validSystem: z.input<typeof SystemEntrySchema> = {
34
+ id: 'test-system',
35
+ order: 10,
36
+ label: 'Test System'
37
+ }
38
+
39
+ const validPolicy: z.input<typeof PolicySchema> = {
40
+ id: 'test-policy',
41
+ order: 10,
42
+ label: 'Test Policy',
43
+ trigger: { kind: 'manual' },
44
+ actions: [{ kind: 'block' }]
45
+ }
46
+
47
+ const validRole: z.input<typeof RoleSchema> = {
48
+ id: 'test-role',
49
+ order: 10,
50
+ title: 'Test Role'
51
+ }
52
+
53
+ const validGoal: z.input<typeof ObjectiveSchema> = {
54
+ id: 'test-goal',
55
+ order: 10,
56
+ description: 'Grow ARR by 2x',
57
+ periodStart: '2026-01-01',
58
+ periodEnd: '2026-12-31'
59
+ }
60
+
61
+ const validCustomer: z.input<typeof CustomerSegmentSchema> = {
62
+ id: 'test-segment',
63
+ order: 10,
64
+ name: 'SMB Agencies'
65
+ }
66
+
67
+ const validOffering: z.input<typeof ProductSchema> = {
68
+ id: 'test-product',
69
+ order: 10,
70
+ name: 'Starter Plan'
71
+ }
72
+
73
+ const validStatus: z.input<typeof StatusEntrySchema> = {
74
+ id: 'test.status',
75
+ order: 10,
76
+ label: 'Test Status',
77
+ semanticClass: 'delivery.task'
78
+ }
79
+
80
+ const validKnowledgeNode: z.input<typeof OrgKnowledgeNodeSchema> = {
81
+ id: 'test-knowledge',
82
+ kind: 'reference',
83
+ title: 'Test Knowledge',
84
+ summary: 'A test knowledge node.',
85
+ body: 'Full body content here.',
86
+ updatedAt: '2026-01-01'
87
+ }
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // Generic defineDomainRecord
91
+ // ---------------------------------------------------------------------------
92
+
93
+ describe('defineDomainRecord', () => {
94
+ it('produces an id-keyed map for valid entries', () => {
95
+ const result = defineDomainRecord(ActionSchema, [validAction])
96
+ expect(result).toHaveProperty('test-action')
97
+ expect(result['test-action'].id).toBe('test-action')
98
+ expect(result['test-action'].label).toBe('Test Action')
99
+ })
100
+
101
+ it('produces a map keyed by each entry id when given multiple entries', () => {
102
+ const second: z.input<typeof ActionSchema> = { ...validAction, id: 'second-action', label: 'Second' }
103
+ const result = defineDomainRecord(ActionSchema, [validAction, second])
104
+ expect(Object.keys(result)).toEqual(['test-action', 'second-action'])
105
+ })
106
+
107
+ it('returns an empty map for an empty entries array', () => {
108
+ const result = defineDomainRecord(ActionSchema, [])
109
+ expect(result).toEqual({})
110
+ })
111
+
112
+ it('throws ZodError when an entry fails schema validation', () => {
113
+ const bad = { id: 'bad', order: 'not-a-number', label: 'Bad' } as unknown as z.input<typeof ActionSchema>
114
+ expect(() => defineDomainRecord(ActionSchema, [bad])).toThrow(z.ZodError)
115
+ })
116
+
117
+ it('applies schema defaults (invocations defaults to [])', () => {
118
+ const minimal = { id: 'minimal-action', order: 10, label: 'Minimal' } as z.input<typeof ActionSchema>
119
+ const result = defineDomainRecord(ActionSchema, [minimal])
120
+ expect(result['minimal-action'].invocations).toEqual([])
121
+ })
122
+ })
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Parameterized per-domain tests
126
+ // ---------------------------------------------------------------------------
127
+
128
+ describe.each([
129
+ {
130
+ domain: 'actions',
131
+ defineSingle: defineAction,
132
+ defineMultiple: defineActions,
133
+ valid: validAction,
134
+ schema: ActionSchema,
135
+ idField: 'id' as const,
136
+ invalidOverride: { order: 'not-a-number' }
137
+ },
138
+ {
139
+ domain: 'entities',
140
+ defineSingle: defineEntity,
141
+ defineMultiple: defineEntities,
142
+ valid: validEntity,
143
+ schema: EntitySchema,
144
+ idField: 'id' as const,
145
+ invalidOverride: { order: 'not-a-number' }
146
+ },
147
+ {
148
+ domain: 'systems',
149
+ defineSingle: defineSystem,
150
+ defineMultiple: defineSystems,
151
+ valid: validSystem,
152
+ schema: SystemEntrySchema,
153
+ idField: 'id' as const,
154
+ invalidOverride: { order: 'not-a-number' }
155
+ },
156
+ {
157
+ domain: 'policies',
158
+ defineSingle: definePolicy,
159
+ defineMultiple: definePolicies,
160
+ valid: validPolicy,
161
+ schema: PolicySchema,
162
+ idField: 'id' as const,
163
+ invalidOverride: { order: 'not-a-number' }
164
+ },
165
+ {
166
+ domain: 'roles',
167
+ defineSingle: defineRole,
168
+ defineMultiple: defineRoles,
169
+ valid: validRole,
170
+ schema: RoleSchema,
171
+ idField: 'id' as const,
172
+ invalidOverride: { order: 'not-a-number' }
173
+ },
174
+ {
175
+ domain: 'goals',
176
+ defineSingle: defineGoal,
177
+ defineMultiple: defineGoals,
178
+ valid: validGoal,
179
+ schema: ObjectiveSchema,
180
+ idField: 'id' as const,
181
+ invalidOverride: { order: 'not-a-number' }
182
+ },
183
+ {
184
+ domain: 'customers',
185
+ defineSingle: defineCustomer,
186
+ defineMultiple: defineCustomers,
187
+ valid: validCustomer,
188
+ schema: CustomerSegmentSchema,
189
+ idField: 'id' as const,
190
+ invalidOverride: { order: 'not-a-number' }
191
+ },
192
+ {
193
+ domain: 'offerings',
194
+ defineSingle: defineOffering,
195
+ defineMultiple: defineOfferings,
196
+ valid: validOffering,
197
+ schema: ProductSchema,
198
+ idField: 'id' as const,
199
+ invalidOverride: { order: 'not-a-number' }
200
+ },
201
+ {
202
+ domain: 'statuses',
203
+ defineSingle: defineStatus,
204
+ defineMultiple: defineStatuses,
205
+ valid: validStatus,
206
+ schema: StatusEntrySchema,
207
+ idField: 'id' as const,
208
+ invalidOverride: { order: 'not-a-number' }
209
+ },
210
+ {
211
+ domain: 'knowledge',
212
+ defineSingle: defineKnowledgeNode,
213
+ defineMultiple: defineKnowledgeNodes,
214
+ valid: validKnowledgeNode,
215
+ schema: OrgKnowledgeNodeSchema,
216
+ idField: 'id' as const,
217
+ invalidOverride: { kind: 'invalid-kind' }
218
+ }
219
+ ])(
220
+ 'define$domain — $domain',
221
+ ({ domain: _domain, defineSingle, defineMultiple, valid, schema, idField, invalidOverride }) => {
222
+ it('defineX — validates and returns a parsed entry', () => {
223
+ const result = defineSingle(valid as never)
224
+ expect(result).toHaveProperty(idField)
225
+ expect((result as Record<string, unknown>)[idField]).toBe((valid as Record<string, unknown>)[idField])
226
+ })
227
+
228
+ it('defineX — throws ZodError on invalid input', () => {
229
+ const bad = { ...valid, ...invalidOverride } as never
230
+ expect(() => defineSingle(bad)).toThrow(z.ZodError)
231
+ })
232
+
233
+ it('defineXs — produces an id-keyed map', () => {
234
+ const result = defineMultiple([valid] as never[])
235
+ const id = (valid as Record<string, unknown>)[idField] as string
236
+ expect(result).toHaveProperty(id)
237
+ expect((result[id] as Record<string, unknown>)[idField]).toBe(id)
238
+ })
239
+
240
+ it('defineXs — throws ZodError on invalid input', () => {
241
+ const bad = { ...valid, ...invalidOverride } as never
242
+ expect(() => defineMultiple([bad] as never[])).toThrow(z.ZodError)
243
+ })
244
+
245
+ it('defineXs — returns empty map for empty array', () => {
246
+ const result = defineMultiple([] as never[])
247
+ expect(result).toEqual({})
248
+ })
249
+
250
+ it('defineXs — validates through schema (no bypass)', () => {
251
+ // Confirm that defineMultiple uses schema.parse, not a pass-through.
252
+ // Missing required fields should throw even if the object looks similar.
253
+ const missingRequired = { [idField]: (valid as Record<string, unknown>)[idField] } as never
254
+ expect(() => defineMultiple([missingRequired] as never[])).toThrow(z.ZodError)
255
+ })
256
+ }
257
+ )
258
+
259
+ // ---------------------------------------------------------------------------
260
+ // Topology — existing helpers (defineTopology, defineTopologyRelationship)
261
+ // are already tested elsewhere; verified here that they exist and are callable.
262
+ // ---------------------------------------------------------------------------
263
+
264
+ describe('topology — existing helpers', () => {
265
+ it('defineTopologyRelationship is importable from domains/topology', async () => {
266
+ const { defineTopologyRelationship, topologyRef } = await import('../domains/topology')
267
+ const rel = defineTopologyRelationship({
268
+ from: topologyRef.system('sales'),
269
+ kind: 'uses',
270
+ to: topologyRef.resource('lead-gen.company.qualify')
271
+ })
272
+ expect(rel.kind).toBe('uses')
273
+ expect(rel.from.kind).toBe('system')
274
+ expect(rel.to.kind).toBe('resource')
275
+ })
276
+
277
+ it('defineTopology is importable and produces a domain object', async () => {
278
+ const { defineTopology, topologyRef } = await import('../domains/topology')
279
+ const domain = defineTopology({
280
+ 'rel-1': {
281
+ from: topologyRef.system('sales'),
282
+ kind: 'triggers',
283
+ to: topologyRef.resource('lead-gen.company.qualify')
284
+ }
285
+ })
286
+ expect(domain.version).toBe(1)
287
+ expect(domain.relationships).toHaveProperty('rel-1')
288
+ })
289
+ })
@@ -0,0 +1,56 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { describe, expect, it } from 'vitest'
4
+ import { ACTION_REGISTRY } from '../domains/prospecting'
5
+ import { getLeadGenStageCatalog } from '../migration-helpers'
6
+
7
+ /**
8
+ * OM Spine doc <-> code contract guard.
9
+ *
10
+ * `scripts/monorepo/meta-verify.mjs` only string-greps the primer for symbol
11
+ * names, so it cannot notice when a documented "constant" has become a
12
+ * type-only export or an empty stub. This drift actually shipped (the Wave-2
13
+ * generic/canonical OM split) and went uncaught for that reason. These
14
+ * assertions tie the primer text to the real code shape so the same class of
15
+ * drift fails the unit suite. Pure file reads + pure imports — CI-safe.
16
+ */
17
+
18
+ const repoRoot = fileURLToPath(new URL('../../../../../', import.meta.url))
19
+ const catalogTypeSrc = fileURLToPath(new URL('../catalogs/lead-gen.ts', import.meta.url))
20
+
21
+ const PRIMER_PATHS = [
22
+ 'apps/docs/content/docs/technical/development/design/om-spine.mdx',
23
+ '.claude/skills/org-os/operations/spine.md'
24
+ ]
25
+
26
+ const read = (p: string) => readFileSync(p, 'utf8')
27
+
28
+ describe('OM Spine doc/code contract', () => {
29
+ it('keeps the generic core action registry an empty stub (bindings live in @repo/elevasis-core)', () => {
30
+ expect(ACTION_REGISTRY).toEqual([])
31
+ })
32
+
33
+ it('exposes the lead-gen catalog only as a reader function, not a hand-authored constant', () => {
34
+ expect(typeof getLeadGenStageCatalog).toBe('function')
35
+
36
+ // catalogs/lead-gen.ts must remain type-only — no LEAD_GEN_STAGE_CATALOG value export.
37
+ const src = read(catalogTypeSrc)
38
+ expect(src).not.toMatch(/export\s+const\s+LEAD_GEN_STAGE_CATALOG\b/)
39
+ expect(src).toMatch(/export\s+interface\s+LeadGenStageCatalogEntry\b/)
40
+ })
41
+
42
+ it.each(PRIMER_PATHS)('primer %s describes the post-split reality, not the retired constant', (relPath) => {
43
+ const text = read(repoRoot + relPath)
44
+
45
+ // Negative: the exact stale claims that shipped before this fix must not return.
46
+ expect(text).not.toMatch(
47
+ /`LEAD_GEN_STAGE_CATALOG`\s+in\s+`packages\/core\/src\/organization-model\/catalogs\/lead-gen\.ts`/
48
+ )
49
+ expect(text).not.toMatch(/LEAD_GEN_STAGE_CATALOG \(vocabulary, catalogs\/lead-gen\.ts\)/)
50
+
51
+ // Positive: each primer must point at the real reader + canonical owners.
52
+ expect(text).toContain('getLeadGenStageCatalog')
53
+ expect(text).toContain('LEAD_GEN_ACTION_ENTRIES')
54
+ expect(text).toContain('@repo/elevasis-core')
55
+ })
56
+ })
@@ -1,10 +1,27 @@
1
1
  import { describe, expect, it } from 'vitest'
2
+ import * as publishedCore from '../../index'
2
3
  import * as publishedOrganizationModel from '../published'
3
4
 
4
5
  describe('published organization-model barrel zero-leak guard', () => {
5
6
  it('does not export Elevasis instance data through @repo/core/organization-model', () => {
6
7
  expect(Object.keys(publishedOrganizationModel)).not.toEqual(
7
- expect.arrayContaining(['LEAD_GEN_STAGE_CATALOG', 'CRM_ACTION_ENTRIES', 'LEAD_GEN_ACTION_ENTRIES'])
8
+ expect.arrayContaining([
9
+ 'LEAD_GEN_STAGE_CATALOG',
10
+ 'CRM_ACTION_ENTRIES',
11
+ 'LEAD_GEN_ACTION_ENTRIES',
12
+ 'CRM_PIPELINE_DEFINITION',
13
+ 'CRM_DISCOVERY_REPLIED_STATE',
14
+ 'CRM_DISCOVERY_LINK_SENT_STATE',
15
+ 'CRM_DISCOVERY_NUDGING_STATE',
16
+ 'CRM_DISCOVERY_BOOKING_CANCELLED_STATE',
17
+ 'CRM_REPLY_SENT_STATE',
18
+ 'CRM_FOLLOWUP_1_SENT_STATE',
19
+ 'CRM_FOLLOWUP_2_SENT_STATE',
20
+ 'CRM_FOLLOWUP_3_SENT_STATE',
21
+ 'CRM_PRIORITY_BUCKETS',
22
+ 'DEFAULT_CRM_PRIORITY_RULE_CONFIG',
23
+ 'DEFAULT_CRM_NEXT_ACTION_RULE_CONFIG'
24
+ ])
8
25
  )
9
26
 
10
27
  const serializedExports = JSON.stringify(publishedOrganizationModel)
@@ -13,5 +30,47 @@ describe('published organization-model barrel zero-leak guard', () => {
13
30
  expect(serializedExports).not.toContain('lgn-05-email-verification-workflow')
14
31
  expect(serializedExports).not.toContain('localServices')
15
32
  expect(serializedExports).not.toContain('dtcApolloClickup')
33
+ expect(serializedExports).not.toContain('Elevasis')
34
+ expect(serializedExports).not.toContain('elevasis.io')
35
+ expect(serializedExports).not.toContain('discovery_replied')
36
+ expect(serializedExports).not.toContain('discovery_link_sent')
37
+ expect(serializedExports).not.toContain('crm-send-booking-link-workflow')
38
+ })
39
+
40
+ it('does not expose Elevasis CRM stage data through the @repo/core root barrel', () => {
41
+ expect(Object.keys(publishedCore)).not.toEqual(
42
+ expect.arrayContaining([
43
+ 'CRM_PIPELINE_DEFINITION',
44
+ 'CRM_DISCOVERY_REPLIED_STATE',
45
+ 'CRM_DISCOVERY_LINK_SENT_STATE',
46
+ 'CRM_DISCOVERY_NUDGING_STATE',
47
+ 'CRM_DISCOVERY_BOOKING_CANCELLED_STATE',
48
+ 'CRM_REPLY_SENT_STATE',
49
+ 'CRM_FOLLOWUP_1_SENT_STATE',
50
+ 'CRM_FOLLOWUP_2_SENT_STATE',
51
+ 'CRM_FOLLOWUP_3_SENT_STATE',
52
+ 'CRM_PRIORITY_BUCKETS',
53
+ 'DEFAULT_CRM_PRIORITY_RULE_CONFIG',
54
+ 'DEFAULT_CRM_NEXT_ACTION_RULE_CONFIG',
55
+ 'DEFAULT_CRM_ACTIONS'
56
+ ])
57
+ )
58
+
59
+ let legacyCrmStatesResult: unknown
60
+ try {
61
+ legacyCrmStatesResult = (
62
+ publishedCore as typeof publishedCore & {
63
+ getCrmStatesForStage?: (stageKey: string) => unknown
64
+ }
65
+ ).getCrmStatesForStage?.('interested')
66
+ } catch {
67
+ legacyCrmStatesResult = undefined
68
+ }
69
+
70
+ expect(JSON.stringify(legacyCrmStatesResult ?? null)).not.toContain('discovery_replied')
71
+ const serializedRootExports = JSON.stringify(publishedCore)
72
+ expect(serializedRootExports).not.toContain('discovery_replied')
73
+ expect(serializedRootExports).not.toContain('discovery_link_sent')
74
+ expect(serializedRootExports).not.toContain('crm-send-booking-link-workflow')
16
75
  })
17
76
  })
@@ -169,7 +169,7 @@ describe('organization-model resolve', () => {
169
169
  })
170
170
 
171
171
  expect(model.branding.organizationName).toBe('OverriddenOrg')
172
- expect(model.branding.productName).toBe('Elevasis')
172
+ expect(model.branding.productName).toBe('Organization OS')
173
173
  expect(model.systems).toEqual({})
174
174
  })
175
175
 
@@ -0,0 +1,72 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { z } from 'zod'
3
+ import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
4
+ import { refineOrganizationModel } from '../schema-refinements'
5
+ import type { OrganizationModel } from '../types'
6
+
7
+ function collectRefinementIssues(model: OrganizationModel): z.ZodIssue[] {
8
+ const issues: z.ZodIssue[] = []
9
+ const ctx = {
10
+ addIssue(issue: z.ZodIssue): void {
11
+ issues.push(issue)
12
+ }
13
+ } as z.RefinementCtx
14
+
15
+ refineOrganizationModel(model, ctx)
16
+ return issues
17
+ }
18
+
19
+ describe('refineOrganizationModel', () => {
20
+ it('emits resource systemPath issues through the extracted refinement boundary', () => {
21
+ const model: OrganizationModel = {
22
+ ...DEFAULT_ORGANIZATION_MODEL,
23
+ resources: {
24
+ ...DEFAULT_ORGANIZATION_MODEL.resources,
25
+ 'missing-system-workflow': {
26
+ id: 'missing-system-workflow',
27
+ order: 10,
28
+ kind: 'workflow',
29
+ systemPath: 'missing.system',
30
+ status: 'active'
31
+ }
32
+ }
33
+ }
34
+
35
+ const issues = collectRefinementIssues(model)
36
+
37
+ expect(issues).toEqual(
38
+ expect.arrayContaining([
39
+ expect.objectContaining({
40
+ code: z.ZodIssueCode.custom,
41
+ path: ['resources', 'missing-system-workflow', 'systemPath'],
42
+ message: 'Resource "missing-system-workflow" references unknown system path "missing.system"'
43
+ })
44
+ ])
45
+ )
46
+ })
47
+
48
+ it('uses the shared ontology reference key map for loose ontology payload references', () => {
49
+ const model: OrganizationModel = {
50
+ ...DEFAULT_ORGANIZATION_MODEL,
51
+ ontology: {
52
+ ...DEFAULT_ORGANIZATION_MODEL.ontology,
53
+ objectTypes: {
54
+ ...DEFAULT_ORGANIZATION_MODEL.ontology.objectTypes,
55
+ 'global:object/test-object': {
56
+ id: 'global:object/test-object',
57
+ label: 'Test Object',
58
+ properties: {
59
+ badValueRef: { valueType: 'global:value-type/missing' }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ const issues = collectRefinementIssues(model)
67
+
68
+ expect(issues.some((issue) => issue.message.includes('valueType references unknown value-type ontology ID'))).toBe(
69
+ true
70
+ )
71
+ })
72
+ })