@elevasis/core 0.24.1 → 0.26.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 (82) hide show
  1. package/dist/index.d.ts +239 -86
  2. package/dist/index.js +474 -1346
  3. package/dist/knowledge/index.d.ts +57 -39
  4. package/dist/knowledge/index.js +1 -1
  5. package/dist/organization-model/index.d.ts +239 -86
  6. package/dist/organization-model/index.js +474 -1346
  7. package/dist/test-utils/index.d.ts +24 -31
  8. package/dist/test-utils/index.js +76 -1238
  9. package/package.json +1 -1
  10. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +108 -96
  11. package/src/business/acquisition/api-schemas.test.ts +70 -77
  12. package/src/business/acquisition/api-schemas.ts +21 -42
  13. package/src/business/acquisition/derive-actions.test.ts +11 -21
  14. package/src/business/acquisition/derive-actions.ts +61 -14
  15. package/src/business/acquisition/ontology-validation.ts +4 -4
  16. package/src/business/acquisition/types.ts +7 -8
  17. package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +10 -10
  18. package/src/knowledge/__tests__/queries.test.ts +960 -546
  19. package/src/knowledge/format.ts +322 -100
  20. package/src/knowledge/index.ts +18 -5
  21. package/src/knowledge/queries.ts +1004 -240
  22. package/src/organization-model/__tests__/content-kinds-registry.test.ts +35 -210
  23. package/src/organization-model/__tests__/defaults.test.ts +4 -4
  24. package/src/organization-model/__tests__/deprecate-helpers.test.ts +71 -0
  25. package/src/organization-model/__tests__/domains/actions.test.ts +12 -36
  26. package/src/organization-model/__tests__/domains/offerings.test.ts +13 -6
  27. package/src/organization-model/__tests__/domains/resources.test.ts +497 -350
  28. package/src/organization-model/__tests__/domains/systems.test.ts +6 -7
  29. package/src/organization-model/__tests__/flatten-additive-merge.test.ts +68 -80
  30. package/src/organization-model/__tests__/foundation.test.ts +81 -14
  31. package/src/organization-model/__tests__/graph.test.ts +662 -694
  32. package/src/organization-model/__tests__/knowledge.test.ts +31 -17
  33. package/src/organization-model/__tests__/lookup-helpers.test.ts +128 -438
  34. package/src/organization-model/__tests__/migration-helpers.test.ts +362 -591
  35. package/src/organization-model/__tests__/prospecting-ssot.test.ts +68 -103
  36. package/src/organization-model/__tests__/published-zero-leak.test.ts +17 -0
  37. package/src/organization-model/__tests__/recursive-system-schema.test.ts +159 -532
  38. package/src/organization-model/__tests__/resolve.test.ts +88 -49
  39. package/src/organization-model/__tests__/scaffolders.test.ts +93 -0
  40. package/src/organization-model/__tests__/schema.test.ts +65 -56
  41. package/src/organization-model/catalogs/lead-gen.ts +0 -103
  42. package/src/organization-model/defaults.ts +17 -702
  43. package/src/organization-model/domains/actions.ts +116 -333
  44. package/src/organization-model/domains/knowledge.ts +15 -7
  45. package/src/organization-model/domains/projects.ts +4 -4
  46. package/src/organization-model/domains/prospecting.ts +405 -395
  47. package/src/organization-model/domains/resources.ts +206 -135
  48. package/src/organization-model/domains/sales.ts +5 -5
  49. package/src/organization-model/domains/systems.ts +8 -23
  50. package/src/organization-model/graph/build.ts +223 -294
  51. package/src/organization-model/graph/schema.ts +2 -3
  52. package/src/organization-model/graph/types.ts +12 -14
  53. package/src/organization-model/helpers.ts +120 -141
  54. package/src/organization-model/icons.ts +1 -0
  55. package/src/organization-model/index.ts +107 -126
  56. package/src/organization-model/migration-helpers.ts +211 -249
  57. package/src/organization-model/ontology.ts +0 -60
  58. package/src/organization-model/organization-graph.mdx +4 -5
  59. package/src/organization-model/organization-model.mdx +1 -1
  60. package/src/organization-model/published.ts +251 -228
  61. package/src/organization-model/resolve.ts +4 -5
  62. package/src/organization-model/scaffolders/helpers.ts +84 -0
  63. package/src/organization-model/scaffolders/index.ts +19 -0
  64. package/src/organization-model/scaffolders/scaffoldKnowledgeNode.ts +48 -0
  65. package/src/organization-model/scaffolders/scaffoldOntologyRecord.ts +38 -0
  66. package/src/organization-model/scaffolders/scaffoldResource.ts +59 -0
  67. package/src/organization-model/scaffolders/scaffoldSystem.ts +110 -0
  68. package/src/organization-model/scaffolders/types.ts +81 -0
  69. package/src/organization-model/schema.ts +610 -704
  70. package/src/organization-model/types.ts +167 -161
  71. package/src/platform/constants/versions.ts +1 -1
  72. package/src/platform/registry/__tests__/validation.test.ts +23 -0
  73. package/src/platform/registry/validation.ts +13 -2
  74. package/src/reference/_generated/contracts.md +108 -96
  75. package/src/reference/glossary.md +71 -69
  76. package/src/organization-model/content-kinds/config.ts +0 -36
  77. package/src/organization-model/content-kinds/index.ts +0 -78
  78. package/src/organization-model/content-kinds/pipeline.ts +0 -68
  79. package/src/organization-model/content-kinds/registry.ts +0 -44
  80. package/src/organization-model/content-kinds/status.ts +0 -71
  81. package/src/organization-model/content-kinds/template.ts +0 -83
  82. package/src/organization-model/content-kinds/types.ts +0 -117
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevasis/core",
3
- "version": "0.24.1",
3
+ "version": "0.26.0",
4
4
  "license": "MIT",
5
5
  "description": "Minimal shared constants across Elevasis monorepo",
6
6
  "sideEffects": false,
@@ -306,7 +306,7 @@ export type OrganizationModelSystemLifecycle = z.infer<typeof SystemLifecycleSch
306
306
  ### `OrganizationModelSystemStatus`
307
307
 
308
308
  ```typescript
309
- /** @deprecated Use OrganizationModelSystemLifecycle. Accepted for one publish cycle. */
309
+ /** @deprecated Use OrganizationModelSystemLifecycle. Accepted for one publish cycle. */
310
310
  export type OrganizationModelSystemStatus = z.infer<typeof SystemStatusSchema>
311
311
  ```
312
312
 
@@ -376,6 +376,18 @@ export type OrganizationModelResourceGovernanceStatus = z.infer<typeof ResourceG
376
376
  export type OrganizationModelResourceOntologyBinding = z.infer<typeof ResourceOntologyBindingSchema>
377
377
  ```
378
378
 
379
+ ### `OrganizationModelContractRef`
380
+
381
+ ```typescript
382
+ export type OrganizationModelContractRef = z.infer<typeof ContractRefSchema>
383
+ ```
384
+
385
+ ### `OrganizationModelResourceOntologyBindingContract`
386
+
387
+ ```typescript
388
+ export type OrganizationModelResourceOntologyBindingContract = ResourceOntologyBindingContract
389
+ ```
390
+
379
391
  ### `OrganizationModelAgentKind`
380
392
 
381
393
  ```typescript
@@ -625,7 +637,7 @@ export type OrganizationModelBuiltinIconToken = z.infer<typeof OrganizationModel
625
637
  ### `DeepPartial`
626
638
 
627
639
  ```typescript
628
- export type DeepPartial<T> =
640
+ export type DeepPartial<T> =
629
641
  T extends Array<infer U> ? Array<DeepPartial<U>> : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T
630
642
  ```
631
643
 
@@ -634,8 +646,8 @@ export type DeepPartial<T> =
634
646
  ### `ElevasisOrganizationModel`
635
647
 
636
648
  ```typescript
637
- export type ElevasisOrganizationModel = Omit<OrganizationModel, 'knowledge'> & {
638
- knowledge?: OrganizationModel['knowledge']
649
+ export type ElevasisOrganizationModel = Omit<OrganizationModel, 'knowledge'> & {
650
+ knowledge?: OrganizationModel['knowledge']
639
651
  }
640
652
  ```
641
653
 
@@ -660,107 +672,107 @@ export type SystemSidebarWidthResolver = number | ((context: { currentPath: stri
660
672
  ### `SystemModule`
661
673
 
662
674
  ```typescript
663
- export interface SystemModule {
664
- /** Unique stable identifier for this UI system module. */
665
- key: string
666
- /** Organization Model system id this module presents. Omit for navigation-only app surfaces. */
667
- systemId?: string
668
- /** Route prefixes owned by navigation-only app surfaces. */
669
- routePrefixes?: string[]
670
- /** Capability identifiers contributed by this system module. */
671
- capabilityIds?: string[]
672
- /** Icon used when this system node appears in shell navigation. */
673
- icon?: SystemIconComponent
674
- /** Sidebar component rendered when this system's subtree route is active. */
675
- sidebar?: SystemSidebarComponent
676
- /** Optional shell sidebar width override. Defaults to 250px. */
677
- sidebarWidth?: SystemSidebarWidthResolver
678
- /** Operations-only bridge connecting this system to the organization graph node. */
679
- organizationGraph?: OrganizationGraphSystemBridge
675
+ export interface SystemModule {
676
+ /** Unique stable identifier for this UI system module. */
677
+ key: string
678
+ /** Organization Model system id this module presents. Omit for navigation-only app surfaces. */
679
+ systemId?: string
680
+ /** Route prefixes owned by navigation-only app surfaces. */
681
+ routePrefixes?: string[]
682
+ /** Capability identifiers contributed by this system module. */
683
+ capabilityIds?: string[]
684
+ /** Icon used when this system node appears in shell navigation. */
685
+ icon?: SystemIconComponent
686
+ /** Sidebar component rendered when this system's subtree route is active. */
687
+ sidebar?: SystemSidebarComponent
688
+ /** Optional shell sidebar width override. Defaults to 250px. */
689
+ sidebarWidth?: SystemSidebarWidthResolver
690
+ /** Operations-only bridge connecting this system to the organization graph node. */
691
+ organizationGraph?: OrganizationGraphSystemBridge
680
692
  }
681
693
  ```
682
694
 
683
695
  ### `ResolvedSystemAccess`
684
696
 
685
697
  ```typescript
686
- export interface ResolvedSystemAccess {
687
- featureKey: string
688
- systemId?: string
689
- enabled: boolean
698
+ export interface ResolvedSystemAccess {
699
+ systemKey: string
700
+ systemId?: string
701
+ enabled: boolean
690
702
  }
691
703
  ```
692
704
 
693
705
  ### `ResolvedSystemSemantics`
694
706
 
695
707
  ```typescript
696
- export interface ResolvedSystemSemantics {
697
- capabilityIds: string[]
708
+ export interface ResolvedSystemSemantics {
709
+ capabilityIds: string[]
698
710
  }
699
711
  ```
700
712
 
701
713
  ### `ResolvedSystemModule`
702
714
 
703
715
  ```typescript
704
- export interface ResolvedSystemModule extends SystemModule {
705
- access: ResolvedSystemAccess
706
- semantics: ResolvedSystemSemantics
716
+ export interface ResolvedSystemModule extends SystemModule {
717
+ access: ResolvedSystemAccess
718
+ semantics: ResolvedSystemSemantics
707
719
  }
708
720
  ```
709
721
 
710
722
  ### `ResolvedShellSystem`
711
723
 
712
724
  ```typescript
713
- export type ResolvedShellSystem = OrganizationModelSystemEntry & {
714
- label: string
715
- iconComponent?: SystemIconComponent
725
+ export type ResolvedShellSystem = OrganizationModelSystemEntry & {
726
+ label: string
727
+ iconComponent?: SystemIconComponent
716
728
  }
717
729
  ```
718
730
 
719
731
  ### `ResolvedShellModel`
720
732
 
721
733
  ```typescript
722
- export interface ResolvedShellModel {
723
- systems: readonly ResolvedShellSystem[]
724
- findByPath: (path: string) => ResolvedShellSystem | undefined
725
- findById: (id: string) => ResolvedShellSystem | undefined
726
- childrenOf: (id: string) => ResolvedShellSystem[]
727
- ancestorsOf: (id: string) => ResolvedShellSystem[]
728
- parentOf: (id: string) => ResolvedShellSystem | undefined
729
- topLevel: () => ResolvedShellSystem[]
730
- requiresAdminFor: (id: string) => boolean
731
- devOnlyFor: (id: string) => boolean
734
+ export interface ResolvedShellModel {
735
+ systems: readonly ResolvedShellSystem[]
736
+ findByPath: (path: string) => ResolvedShellSystem | undefined
737
+ findById: (id: string) => ResolvedShellSystem | undefined
738
+ childrenOf: (id: string) => ResolvedShellSystem[]
739
+ ancestorsOf: (id: string) => ResolvedShellSystem[]
740
+ parentOf: (id: string) => ResolvedShellSystem | undefined
741
+ topLevel: () => ResolvedShellSystem[]
742
+ requiresAdminFor: (id: string) => boolean
743
+ devOnlyFor: (id: string) => boolean
732
744
  }
733
745
  ```
734
746
 
735
747
  ### `ShellSidebarLinkItem`
736
748
 
737
749
  ```typescript
738
- export interface ShellSidebarLinkItem {
739
- label: string
740
- link: string
741
- exact?: boolean
742
- activeMatchPaths?: string[]
750
+ export interface ShellSidebarLinkItem {
751
+ label: string
752
+ link: string
753
+ exact?: boolean
754
+ activeMatchPaths?: string[]
743
755
  }
744
756
  ```
745
757
 
746
758
  ### `ShellSidebarLinkGroup`
747
759
 
748
760
  ```typescript
749
- export interface ShellSidebarLinkGroup {
750
- icon: SystemIconComponent
751
- label: string
752
- links?: ShellSidebarLinkItem[]
753
- link?: string
761
+ export interface ShellSidebarLinkGroup {
762
+ icon: SystemIconComponent
763
+ label: string
764
+ links?: ShellSidebarLinkItem[]
765
+ link?: string
754
766
  }
755
767
  ```
756
768
 
757
769
  ### `ShellSidebarProjectionOptions`
758
770
 
759
771
  ```typescript
760
- export interface ShellSidebarProjectionOptions {
761
- isPlatformAdmin?: boolean
762
- isDev?: boolean
763
- section?: 'primary' | 'bottom'
772
+ export interface ShellSidebarProjectionOptions {
773
+ isPlatformAdmin?: boolean
774
+ isDev?: boolean
775
+ section?: 'primary' | 'bottom'
764
776
  }
765
777
  ```
766
778
 
@@ -773,75 +785,75 @@ export type ShellRouteMatchStatus = 'matched' | 'hidden' | 'unmatched'
773
785
  ### `ResolvedShellRouteMatch`
774
786
 
775
787
  ```typescript
776
- export interface ResolvedShellRouteMatch {
777
- status: ShellRouteMatchStatus
778
- path: string
779
- system?: ResolvedSystemModule
780
- node?: ResolvedShellSystem
788
+ export interface ResolvedShellRouteMatch {
789
+ status: ShellRouteMatchStatus
790
+ path: string
791
+ system?: ResolvedSystemModule
792
+ node?: ResolvedShellSystem
781
793
  }
782
794
  ```
783
795
 
784
796
  ### `ShellRuntime`
785
797
 
786
798
  ```typescript
787
- export interface ShellRuntime {
788
- resolveRoute: (path: string) => ResolvedShellRouteMatch
799
+ export interface ShellRuntime {
800
+ resolveRoute: (path: string) => ResolvedShellRouteMatch
789
801
  }
790
802
  ```
791
803
 
792
804
  ### `OrganizationGraphSystemBridge`
793
805
 
794
806
  ```typescript
795
- export interface OrganizationGraphSystemBridge {
796
- systemId?: string
807
+ export interface OrganizationGraphSystemBridge {
808
+ systemId?: string
797
809
  }
798
810
  ```
799
811
 
800
812
  ### `OrganizationGraphContextValue`
801
813
 
802
814
  ```typescript
803
- export interface OrganizationGraphContextValue {
804
- available: boolean
805
- systemId?: string
806
- systemPath?: string
815
+ export interface OrganizationGraphContextValue {
816
+ available: boolean
817
+ systemId?: string
818
+ systemPath?: string
807
819
  }
808
820
  ```
809
821
 
810
822
  ### `ElevasisSystemsProviderProps`
811
823
 
812
824
  ```typescript
813
- export interface ElevasisSystemsProviderProps {
814
- systems?: SystemModule[]
815
- organizationModel?: ElevasisOrganizationModel
816
- timeRange?: TimeRange
817
- operationsApiUrl?: string
818
- operationsSSEManager?: SSEConnectionManagerLike
819
- deliveryApiUrl?: string
820
- deliverySSEManager?: SSEConnectionManagerLike
821
- disabledSubsectionPaths?: string[]
822
- children: ReactNode
825
+ export interface ElevasisSystemsProviderProps {
826
+ systems?: SystemModule[]
827
+ organizationModel?: ElevasisOrganizationModel
828
+ timeRange?: TimeRange
829
+ operationsApiUrl?: string
830
+ operationsSSEManager?: SSEConnectionManagerLike
831
+ deliveryApiUrl?: string
832
+ deliverySSEManager?: SSEConnectionManagerLike
833
+ disabledSubsectionPaths?: string[]
834
+ children: ReactNode
823
835
  }
824
836
  ```
825
837
 
826
838
  ### `ElevasisSystemsContextValue`
827
839
 
828
840
  ```typescript
829
- export interface ElevasisSystemsContextValue {
830
- shellModel: ResolvedShellModel
831
- shellRuntime: ShellRuntime
832
- getSidebarLinks: (options?: ShellSidebarProjectionOptions) => ShellSidebarLinkGroup[]
833
- enabledResolvedSystems: ResolvedSystemModule[]
834
- resolvedSystems: ResolvedSystemModule[]
835
- organizationGraph: OrganizationGraphContextValue
836
- organizationModel?: OrganizationModel
837
- timeRange?: TimeRange
838
- operationsApiUrl?: string
839
- operationsSSEManager?: SSEConnectionManagerLike
840
- deliveryApiUrl?: string
841
- deliverySSEManager?: SSEConnectionManagerLike
842
- disabledSubsectionPaths: string[]
843
- isSystemEnabled: (key: string) => boolean
844
- getResolvedSystem: (key: string) => ResolvedSystemModule | undefined
841
+ export interface ElevasisSystemsContextValue {
842
+ shellModel: ResolvedShellModel
843
+ shellRuntime: ShellRuntime
844
+ getSidebarLinks: (options?: ShellSidebarProjectionOptions) => ShellSidebarLinkGroup[]
845
+ enabledResolvedSystems: ResolvedSystemModule[]
846
+ resolvedSystems: ResolvedSystemModule[]
847
+ organizationGraph: OrganizationGraphContextValue
848
+ organizationModel?: OrganizationModel
849
+ timeRange?: TimeRange
850
+ operationsApiUrl?: string
851
+ operationsSSEManager?: SSEConnectionManagerLike
852
+ deliveryApiUrl?: string
853
+ deliverySSEManager?: SSEConnectionManagerLike
854
+ disabledSubsectionPaths: string[]
855
+ isSystemEnabled: (key: string) => boolean
856
+ getResolvedSystem: (key: string) => ResolvedSystemModule | undefined
845
857
  }
846
858
  ```
847
859
 
@@ -2,8 +2,7 @@ import { describe, expect, it } from 'vitest'
2
2
  import {
3
3
  CRM_PIPELINE_DEFINITION,
4
4
  DEFAULT_CRM_PRIORITY_RULE_CONFIG,
5
- LEAD_GEN_PIPELINE_DEFINITIONS,
6
- LEAD_GEN_STAGE_CATALOG
5
+ LEAD_GEN_PIPELINE_DEFINITIONS
7
6
  } from '../../organization-model/domains/sales'
8
7
  import { CrmPriorityOverrideSchema, evaluateCrmDealPriority, resolveCrmPriorityRuleConfig } from './crm-priority'
9
8
  import {
@@ -61,8 +60,7 @@ import {
61
60
  CRM_STAGE_KEYS_FROM_ONTOLOGY,
62
61
  LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID,
63
62
  LEAD_GEN_STAGE_KEYS_FROM_ONTOLOGY,
64
- getLeadGenStageCatalogFromOntology,
65
- isLeadGenActionKey
63
+ getLeadGenStageCatalogFromOntology
66
64
  } from './ontology-validation'
67
65
 
68
66
  // ---------------------------------------------------------------------------
@@ -70,8 +68,10 @@ import {
70
68
  // ---------------------------------------------------------------------------
71
69
 
72
70
  const VALID_UUID = '00000000-0000-4000-8000-000000000001'
73
- const ISO_TS = '2026-04-27T12:34:56.000Z'
74
- const PRIORITY = {
71
+ const ISO_TS = '2026-04-27T12:34:56.000Z'
72
+ const VALID_COMPANY_STAGE_KEY = 'qualified'
73
+ const VALID_CONTACT_STAGE_KEY = 'verified'
74
+ const PRIORITY = {
75
75
  bucketKey: 'waiting' as const,
76
76
  rank: 30,
77
77
  label: 'Waiting',
@@ -130,17 +130,14 @@ describe('business ontology validation bridge', () => {
130
130
  expect(CrmStageKeySchema.options).toEqual(CRM_STAGE_KEYS_FROM_ONTOLOGY)
131
131
  })
132
132
 
133
- it('keeps lead-gen schema validation aligned to the ontology catalog and legacy stage constant', () => {
134
- const legacyStageKeys = Object.keys(LEAD_GEN_STAGE_CATALOG)
135
-
136
- expect(LEAD_GEN_STAGE_KEYS_FROM_ONTOLOGY).toEqual(legacyStageKeys)
137
- expect(Object.keys(getLeadGenStageCatalogFromOntology())).toEqual(legacyStageKeys)
133
+ it('keeps the generic core lead-gen catalog bridge vacuous until a model-owned catalog is supplied', () => {
134
+ expect(LEAD_GEN_STAGE_KEYS_FROM_ONTOLOGY).toEqual([])
135
+ expect(getLeadGenStageCatalogFromOntology()).toEqual({})
138
136
  })
139
137
 
140
- it('indexes legacy action ids through compiled ontology action types', () => {
141
- expect(BUSINESS_ONTOLOGY_VALIDATION_INDEX.actionTypesByLegacyId['send_reply']?.ownerSystemId).toBe('sales.crm')
142
- expect(isLeadGenActionKey('lead-gen.company.source')).toBe(true)
143
- expect(isLeadGenActionKey('lead-gen.missing.action')).toBe(false)
138
+ it('does not publish Elevasis lead-gen action ids through the generic core ontology bridge', () => {
139
+ expect(BUSINESS_ONTOLOGY_VALIDATION_INDEX.actionTypesByLegacyId['send_reply']).toBeUndefined()
140
+ expect(BUSINESS_ONTOLOGY_VALIDATION_INDEX.actionTypesByLegacyId['lead-gen.company.source']).toBeUndefined()
144
141
  })
145
142
  })
146
143
 
@@ -273,11 +270,11 @@ describe('CrmTransitionItemRequestSchema', () => {
273
270
  }
274
271
  })
275
272
 
276
- it('rejects non-CRM pipeline keys and unknown CRM keys', () => {
277
- expect(CrmTransitionItemRequestSchema.safeParse({ ...valid, pipelineKey: 'lead-gen' }).success).toBe(false)
278
- expect(CrmTransitionItemRequestSchema.safeParse({ ...valid, stageKey: 'unknown_stage' }).success).toBe(false)
279
- expect(CrmTransitionItemRequestSchema.safeParse({ ...valid, stateKey: 'unknown_state' }).success).toBe(false)
280
- })
273
+ it('accepts generic non-empty pipeline keys but rejects unknown CRM stage/state keys', () => {
274
+ expect(CrmTransitionItemRequestSchema.safeParse({ ...valid, pipelineKey: 'lead-gen' }).success).toBe(true)
275
+ expect(CrmTransitionItemRequestSchema.safeParse({ ...valid, stageKey: 'unknown_stage' }).success).toBe(false)
276
+ expect(CrmTransitionItemRequestSchema.safeParse({ ...valid, stateKey: 'unknown_state' }).success).toBe(false)
277
+ })
281
278
  })
282
279
 
283
280
  // ---------------------------------------------------------------------------
@@ -550,13 +547,13 @@ describe('UpdateContactRequestSchema', () => {
550
547
  )
551
548
  })
552
549
 
553
- it('accepts processingState keyed by the stage catalog', () => {
554
- const result = UpdateContactRequestSchema.safeParse({
555
- processingState: {
556
- [LEAD_GEN_STAGE_CATALOG.verified.key]: {
557
- status: 'no_result'
558
- }
559
- }
550
+ it('accepts processingState keyed by the stage catalog', () => {
551
+ const result = UpdateContactRequestSchema.safeParse({
552
+ processingState: {
553
+ [VALID_CONTACT_STAGE_KEY]: {
554
+ status: 'no_result'
555
+ }
556
+ }
560
557
  })
561
558
 
562
559
  expect(result.success).toBe(true)
@@ -1027,20 +1024,16 @@ describe('ListStatusSchema', () => {
1027
1024
  // PipelineStageSchema
1028
1025
  // ---------------------------------------------------------------------------
1029
1026
 
1030
- describe('PipelineStageSchema', () => {
1031
- it('accepts a key present in LEAD_GEN_STAGE_CATALOG', () => {
1032
- for (const key of Object.keys(LEAD_GEN_STAGE_CATALOG)) {
1033
- expect(PipelineStageSchema.safeParse({ key }).success).toBe(true)
1034
- }
1035
- })
1036
-
1037
- it('rejects a key not in LEAD_GEN_STAGE_CATALOG', () => {
1038
- const result = PipelineStageSchema.safeParse({ key: 'not_a_real_stage' })
1039
- expect(result.success).toBe(false)
1040
- if (!result.success) {
1041
- expect(result.error.issues[0]?.message).toMatch(/LEAD_GEN_STAGE_CATALOG/)
1042
- }
1043
- })
1027
+ describe('PipelineStageSchema', () => {
1028
+ it('accepts non-empty model-owned stage keys', () => {
1029
+ expect(PipelineStageSchema.safeParse({ key: VALID_COMPANY_STAGE_KEY }).success).toBe(true)
1030
+ expect(PipelineStageSchema.safeParse({ key: 'tenant-custom-stage' }).success).toBe(true)
1031
+ })
1032
+
1033
+ it('rejects an empty stage key', () => {
1034
+ const result = PipelineStageSchema.safeParse({ key: '' })
1035
+ expect(result.success).toBe(false)
1036
+ })
1044
1037
 
1045
1038
  it('accepts optional label, enabled, and order fields', () => {
1046
1039
  expect(PipelineStageSchema.safeParse({ key: 'scraped', label: 'Scraped', enabled: true, order: 1 }).success).toBe(
@@ -1061,23 +1054,23 @@ describe('BuildPlanSnapshotSchema', () => {
1061
1054
  expect(BuildPlanSnapshotSchema.safeParse(validSnapshot).success).toBe(true)
1062
1055
  })
1063
1056
 
1064
- it('rejects a step stageKey outside LEAD_GEN_STAGE_CATALOG', () => {
1065
- const result = BuildPlanSnapshotSchema.safeParse({
1066
- ...validSnapshot,
1067
- steps: [{ ...validSnapshot!.steps[0], stageKey: 'made-up-stage' }]
1068
- })
1069
-
1070
- expect(result.success).toBe(false)
1071
- })
1072
-
1073
- it('rejects a step actionKey outside ACTION_REGISTRY', () => {
1074
- const result = BuildPlanSnapshotSchema.safeParse({
1075
- ...validSnapshot,
1076
- steps: [{ ...validSnapshot!.steps[0], actionKey: 'lead-gen.missing.action' }]
1077
- })
1078
-
1079
- expect(result.success).toBe(false)
1080
- })
1057
+ it('accepts custom non-empty step stage keys at the transport boundary', () => {
1058
+ const result = BuildPlanSnapshotSchema.safeParse({
1059
+ ...validSnapshot,
1060
+ steps: [{ ...validSnapshot!.steps[0], stageKey: 'made-up-stage' }]
1061
+ })
1062
+
1063
+ expect(result.success).toBe(true)
1064
+ })
1065
+
1066
+ it('accepts custom non-empty action keys at the transport boundary', () => {
1067
+ const result = BuildPlanSnapshotSchema.safeParse({
1068
+ ...validSnapshot,
1069
+ steps: [{ ...validSnapshot!.steps[0], actionKey: 'lead-gen.missing.action' }]
1070
+ })
1071
+
1072
+ expect(result.success).toBe(true)
1073
+ })
1081
1074
 
1082
1075
  it('rejects duplicate step ids', () => {
1083
1076
  const first = validSnapshot!.steps[0]!
@@ -1475,13 +1468,13 @@ describe('UpdateCompanyRequestSchema', () => {
1475
1468
  expect(UpdateCompanyRequestSchema.safeParse({ status: 'invalid' }).success).toBe(true)
1476
1469
  })
1477
1470
 
1478
- it('accepts processingState keyed by the stage catalog', () => {
1479
- const result = UpdateCompanyRequestSchema.safeParse({
1480
- processingState: {
1481
- [LEAD_GEN_STAGE_CATALOG.qualified.key]: {
1482
- status: 'success',
1483
- data: { score: 92 }
1484
- }
1471
+ it('accepts processingState keyed by the stage catalog', () => {
1472
+ const result = UpdateCompanyRequestSchema.safeParse({
1473
+ processingState: {
1474
+ [VALID_COMPANY_STAGE_KEY]: {
1475
+ status: 'success',
1476
+ data: { score: 92 }
1477
+ }
1485
1478
  }
1486
1479
  })
1487
1480
 
@@ -1492,14 +1485,14 @@ describe('UpdateCompanyRequestSchema', () => {
1492
1485
  expect(UpdateCompanyRequestSchema.safeParse({ pipelineStatus: 'emailed' }).success).toBe(true)
1493
1486
  })
1494
1487
 
1495
- it('rejects processingState keys outside the stage catalog', () => {
1496
- expect(
1497
- UpdateCompanyRequestSchema.safeParse({
1498
- processingState: {
1499
- madeUpStage: { status: 'success' }
1500
- }
1501
- }).success
1502
- ).toBe(false)
1488
+ it('rejects empty processingState keys', () => {
1489
+ expect(
1490
+ UpdateCompanyRequestSchema.safeParse({
1491
+ processingState: {
1492
+ '': { status: 'success' }
1493
+ }
1494
+ }).success
1495
+ ).toBe(false)
1503
1496
  })
1504
1497
 
1505
1498
  it('accepts numEmployees of 0', () => {
@@ -1744,10 +1737,10 @@ describe('ListRecordsQuerySchema', () => {
1744
1737
  expect(ListRecordsQuerySchema.safeParse({ entity: 'company', stage: 'uploaded' }).success).toBe(true)
1745
1738
  })
1746
1739
 
1747
- it('still rejects unrelated stage/entity combinations', () => {
1748
- expect(ListRecordsQuerySchema.safeParse({ entity: 'contact', stage: 'qualified' }).success).toBe(false)
1749
- })
1750
- })
1740
+ it('accepts stage/entity combinations at the transport boundary', () => {
1741
+ expect(ListRecordsQuerySchema.safeParse({ entity: 'contact', stage: 'qualified' }).success).toBe(true)
1742
+ })
1743
+ })
1751
1744
 
1752
1745
  // ---------------------------------------------------------------------------
1753
1746
  // AcqListResponseSchema (forward-compat)
@@ -1,29 +1,19 @@
1
1
  import { z } from 'zod'
2
2
  import { UuidSchema, NonEmptyStringSchema } from '../../platform/utils/validation'
3
- import { CRM_PIPELINE_DEFINITION } from '../../organization-model/domains/sales'
4
3
  import { CredentialRequirementSchema, RecordColumnConfigSchema } from '../../organization-model/domains/prospecting'
5
4
  import { isProspectingBuildTemplateId } from './build-templates'
6
5
  import {
7
6
  CRM_STAGE_KEYS_FROM_ONTOLOGY,
8
- CRM_STATE_KEYS_FROM_ONTOLOGY,
9
- isLeadGenActionKey,
10
- isLeadGenRecordStageValidForEntity,
11
- isLeadGenStageKey
7
+ CRM_STATE_KEYS_FROM_ONTOLOGY
12
8
  } from './ontology-validation'
13
9
  export { CrmPriorityBucketKeySchema, CrmPriorityBucketOverrideSchema, CrmPriorityOverrideSchema } from './crm-priority'
14
10
  export type { CrmPriorityBucketOverride, CrmPriorityOverride, ResolvedCrmPriorityRuleConfig } from './crm-priority'
15
11
 
16
12
  export const ProcessingStageStatusSchema = z.enum(['success', 'no_result', 'skipped', 'error'])
17
13
 
18
- export const LeadGenStageKeySchema = z
19
- .string()
20
- .refine((value) => isLeadGenStageKey(value), {
21
- message: 'processing state key must match LEAD_GEN_STAGE_CATALOG'
22
- })
14
+ export const LeadGenStageKeySchema = z.string().trim().min(1)
23
15
 
24
- export const LeadGenActionKeySchema = z.string().refine((value) => isLeadGenActionKey(value), {
25
- message: 'actionKey must match ACTION_REGISTRY'
26
- })
16
+ export const LeadGenActionKeySchema = z.string().trim().min(1)
27
17
 
28
18
  const crmStageKeys = CRM_STAGE_KEYS_FROM_ONTOLOGY
29
19
  const crmStateKeys = CRM_STATE_KEYS_FROM_ONTOLOGY
@@ -135,9 +125,9 @@ export const TransitionItemRequestSchema = z
135
125
  })
136
126
  .strict()
137
127
 
138
- export const CrmTransitionItemRequestSchema = z
139
- .object({
140
- pipelineKey: z.literal(CRM_PIPELINE_DEFINITION.pipelineKey),
128
+ export const CrmTransitionItemRequestSchema = z
129
+ .object({
130
+ pipelineKey: z.string().min(1),
141
131
  stageKey: CrmStageKeySchema,
142
132
  stateKey: CrmStateKeySchema.nullable().optional(),
143
133
  reason: z.string().optional(),
@@ -508,14 +498,14 @@ export const IcpRubricSchema = z.object({
508
498
  })
509
499
 
510
500
  /**
511
- * One stage entry in a list's `pipeline_config.stages[]`. The `key` is
512
- * validated against `LEAD_GEN_STAGE_CATALOG` so list pipeline definitions
513
- * stay aligned with the org-os semantic layer.
514
- */
501
+ * One stage entry in a list's `pipeline_config.stages[]`.
502
+ *
503
+ * Stage catalogs are model-owned. The published core schema validates the
504
+ * transport shape and leaves closed-catalog checks to callers with a resolved
505
+ * Organization Model.
506
+ */
515
507
  export const PipelineStageSchema = z.object({
516
- key: z.string().refine((value) => isLeadGenStageKey(value), {
517
- message: 'pipeline stage key must match LEAD_GEN_STAGE_CATALOG'
518
- }),
508
+ key: LeadGenStageKeySchema,
519
509
  label: z.string().optional(),
520
510
  enabled: z.boolean().optional(),
521
511
  order: z.number().int().optional()
@@ -1183,25 +1173,14 @@ export const ListMembersQuerySchema = z
1183
1173
 
1184
1174
  export const ListRecordEntitySchema = z.enum(['company', 'contact'])
1185
1175
 
1186
- export const ListRecordsQuerySchema = z
1187
- .object({
1188
- entity: ListRecordEntitySchema,
1189
- stage: LeadGenStageKeySchema.optional(),
1190
- limit: z.coerce.number().int().min(1).max(500).default(50),
1191
- offset: z.coerce.number().int().min(0).default(0)
1192
- })
1193
- .strict()
1194
- .superRefine((query, ctx) => {
1195
- if (!query.stage) return
1196
-
1197
- if (!isLeadGenRecordStageValidForEntity(query.stage, query.entity)) {
1198
- ctx.addIssue({
1199
- code: z.ZodIssueCode.custom,
1200
- message: `stage "${query.stage}" is not valid for ${query.entity} records`,
1201
- path: ['stage']
1202
- })
1203
- }
1204
- })
1176
+ export const ListRecordsQuerySchema = z
1177
+ .object({
1178
+ entity: ListRecordEntitySchema,
1179
+ stage: LeadGenStageKeySchema.optional(),
1180
+ limit: z.coerce.number().int().min(1).max(500).default(50),
1181
+ offset: z.coerce.number().int().min(0).default(0)
1182
+ })
1183
+ .strict()
1205
1184
 
1206
1185
  export const MemberIdParamsSchema = z.object({
1207
1186
  memberId: UuidSchema