@elevasis/core 0.10.0 → 0.11.1

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 (58) hide show
  1. package/dist/index.d.ts +69 -159
  2. package/dist/index.js +324 -613
  3. package/dist/organization-model/index.d.ts +69 -159
  4. package/dist/organization-model/index.js +324 -613
  5. package/dist/test-utils/index.d.ts +192 -45
  6. package/dist/test-utils/index.js +260 -600
  7. package/package.json +1 -1
  8. package/src/__tests__/template-core-compatibility.test.ts +73 -91
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +94 -182
  10. package/src/auth/multi-tenancy/index.ts +20 -17
  11. package/src/auth/multi-tenancy/memberships/api-schemas.ts +142 -126
  12. package/src/auth/multi-tenancy/memberships/index.ts +26 -22
  13. package/src/auth/multi-tenancy/permissions.test.ts +42 -0
  14. package/src/auth/multi-tenancy/permissions.ts +104 -0
  15. package/src/organization-model/README.md +102 -97
  16. package/src/organization-model/__tests__/defaults.test.ts +19 -6
  17. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +24 -93
  18. package/src/organization-model/__tests__/graph.test.ts +82 -894
  19. package/src/organization-model/__tests__/resolve.test.ts +59 -690
  20. package/src/organization-model/__tests__/schema.test.ts +83 -407
  21. package/src/organization-model/contracts.ts +4 -3
  22. package/src/organization-model/defaults.ts +277 -141
  23. package/src/organization-model/domains/features.ts +31 -22
  24. package/src/organization-model/domains/navigation.ts +27 -20
  25. package/src/organization-model/foundation.ts +42 -54
  26. package/src/organization-model/graph/build.ts +42 -217
  27. package/src/organization-model/graph/index.ts +4 -4
  28. package/src/organization-model/graph/link.ts +10 -0
  29. package/src/organization-model/graph/schema.ts +21 -16
  30. package/src/organization-model/graph/types.ts +10 -10
  31. package/src/organization-model/helpers.ts +74 -0
  32. package/src/organization-model/index.ts +7 -7
  33. package/src/organization-model/organization-graph.mdx +89 -272
  34. package/src/organization-model/organization-model.mdx +152 -320
  35. package/src/organization-model/published.ts +20 -19
  36. package/src/organization-model/resolve.ts +8 -33
  37. package/src/organization-model/schema.ts +63 -205
  38. package/src/organization-model/types.ts +12 -11
  39. package/src/platform/constants/versions.ts +3 -3
  40. package/src/platform/registry/__tests__/command-view.test.ts +6 -5
  41. package/src/platform/registry/__tests__/resource-link.test.ts +30 -0
  42. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +15 -15
  43. package/src/platform/registry/command-view.ts +10 -12
  44. package/src/platform/registry/index.ts +93 -93
  45. package/src/platform/registry/resource-link.ts +32 -0
  46. package/src/platform/registry/resource-registry.ts +917 -876
  47. package/src/platform/registry/serialization.ts +56 -73
  48. package/src/platform/registry/serialized-types.ts +17 -12
  49. package/src/platform/registry/types.ts +14 -43
  50. package/src/reference/_generated/contracts.md +94 -182
  51. package/src/reference/glossary.md +71 -105
  52. package/src/scaffold-registry/__tests__/index.test.ts +125 -1
  53. package/src/scaffold-registry/__tests__/schema.test.ts +48 -20
  54. package/src/scaffold-registry/index.ts +236 -188
  55. package/src/scaffold-registry/schema.ts +47 -22
  56. package/src/supabase/database.types.ts +2880 -2719
  57. package/src/test-utils/fixtures/memberships.ts +82 -80
  58. package/src/platform/registry/domains.ts +0 -165
@@ -1,36 +1,34 @@
1
1
  import { z } from 'zod'
2
- import { OrganizationModelBrandingSchema } from './domains/branding'
3
- import { OrganizationModelSalesSchema } from './domains/sales'
4
- import { OrganizationModelProjectsSchema } from './domains/projects'
5
- import { FeatureSchema } from './domains/features'
6
- import { OrganizationModelProspectingSchema } from './domains/prospecting'
7
- import { OrganizationModelNavigationSchema } from './domains/navigation'
8
- import { ResourceMappingSchema } from './domains/shared'
9
- import { IdentityDomainSchema, DEFAULT_ORGANIZATION_MODEL_IDENTITY } from './domains/identity'
10
- import { CustomersDomainSchema, DEFAULT_ORGANIZATION_MODEL_CUSTOMERS } from './domains/customers'
11
- import { OfferingsDomainSchema, DEFAULT_ORGANIZATION_MODEL_OFFERINGS } from './domains/offerings'
2
+ import { OrganizationModelBrandingSchema } from './domains/branding'
3
+ import { OrganizationModelSalesSchema } from './domains/sales'
4
+ import { OrganizationModelProjectsSchema } from './domains/projects'
5
+ import { FeatureSchema } from './domains/features'
6
+ import { OrganizationModelProspectingSchema } from './domains/prospecting'
7
+ import { OrganizationModelNavigationSchema } from './domains/navigation'
8
+ import { IdentityDomainSchema, DEFAULT_ORGANIZATION_MODEL_IDENTITY } from './domains/identity'
9
+ import { CustomersDomainSchema, DEFAULT_ORGANIZATION_MODEL_CUSTOMERS } from './domains/customers'
10
+ import { OfferingsDomainSchema, DEFAULT_ORGANIZATION_MODEL_OFFERINGS } from './domains/offerings'
12
11
  import { RolesDomainSchema, DEFAULT_ORGANIZATION_MODEL_ROLES } from './domains/roles'
13
12
  import { GoalsDomainSchema, DEFAULT_ORGANIZATION_MODEL_GOALS } from './domains/goals'
14
13
  import { OperationsDomainSchema } from './domains/operations'
15
14
  import { StatusesDomainSchema } from './domains/statuses'
16
15
 
17
16
  const OrganizationModelSchemaBase = z.object({
18
- version: z.literal(1).default(1),
19
- features: z.array(FeatureSchema).default([]),
20
- branding: OrganizationModelBrandingSchema,
21
- navigation: OrganizationModelNavigationSchema,
22
- sales: OrganizationModelSalesSchema,
23
- prospecting: OrganizationModelProspectingSchema,
24
- projects: OrganizationModelProjectsSchema,
17
+ version: z.literal(1).default(1),
18
+ features: z.array(FeatureSchema).default([]),
19
+ branding: OrganizationModelBrandingSchema,
20
+ navigation: OrganizationModelNavigationSchema.default({ surfaces: [], groups: [] }),
21
+ sales: OrganizationModelSalesSchema,
22
+ prospecting: OrganizationModelProspectingSchema,
23
+ projects: OrganizationModelProjectsSchema,
25
24
  identity: IdentityDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_IDENTITY),
26
25
  customers: CustomersDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_CUSTOMERS),
27
26
  offerings: OfferingsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_OFFERINGS),
28
27
  roles: RolesDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_ROLES),
29
- goals: GoalsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_GOALS),
30
- statuses: StatusesDomainSchema.default({ entries: [] }),
31
- operations: OperationsDomainSchema.default({ entries: [] }),
32
- resourceMappings: z.array(ResourceMappingSchema).default([])
33
- })
28
+ goals: GoalsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_GOALS),
29
+ statuses: StatusesDomainSchema.default({ entries: [] }),
30
+ operations: OperationsDomainSchema.default({ entries: [] })
31
+ })
34
32
 
35
33
  function addIssue(ctx: z.RefinementCtx, path: Array<string | number>, message: string): void {
36
34
  ctx.addIssue({
@@ -40,7 +38,7 @@ function addIssue(ctx: z.RefinementCtx, path: Array<string | number>, message: s
40
38
  })
41
39
  }
42
40
 
43
- function collectIds<T extends { id: string }>(
41
+ function collectIds<T extends { id: string }>(
44
42
  items: T[],
45
43
  ctx: z.RefinementCtx,
46
44
  collectionPath: Array<string | number>,
@@ -57,144 +55,46 @@ function collectIds<T extends { id: string }>(
57
55
  itemsById.set(item.id, item)
58
56
  })
59
57
 
60
- return itemsById
61
- }
62
-
63
- export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((model, ctx) => {
64
- const featuresById = collectIds(model.features, ctx, ['features'], 'Feature')
65
- const surfacesById = collectIds(model.navigation.surfaces, ctx, ['navigation', 'surfaces'], 'Surface')
66
- collectIds(model.navigation.groups, ctx, ['navigation', 'groups'], 'Navigation group')
67
- collectIds(model.resourceMappings, ctx, ['resourceMappings'], 'Resource mapping')
68
-
69
- const resourceMappingsByResourceId = new Map<string, (typeof model.resourceMappings)[number]>()
70
- model.resourceMappings.forEach((resourceMapping, index) => {
71
- if (resourceMappingsByResourceId.has(resourceMapping.resourceId)) {
72
- addIssue(
73
- ctx,
74
- ['resourceMappings', index, 'resourceId'],
75
- `Resource mapping resourceId "${resourceMapping.resourceId}" must be unique`
76
- )
77
- return
78
- }
79
-
80
- resourceMappingsByResourceId.set(resourceMapping.resourceId, resourceMapping)
81
- })
82
-
83
- if (model.navigation.defaultSurfaceId && !surfacesById.has(model.navigation.defaultSurfaceId)) {
84
- addIssue(
85
- ctx,
86
- ['navigation', 'defaultSurfaceId'],
87
- `Default surface "${model.navigation.defaultSurfaceId}" must reference a declared navigation surface`
88
- )
89
- }
90
-
91
- model.navigation.groups.forEach((group, groupIndex) => {
92
- group.surfaceIds.forEach((surfaceId, surfaceIndex) => {
93
- if (!surfacesById.has(surfaceId)) {
94
- addIssue(
95
- ctx,
96
- ['navigation', 'groups', groupIndex, 'surfaceIds', surfaceIndex],
97
- `Navigation group "${group.id}" references unknown surface "${surfaceId}"`
98
- )
99
- }
100
- })
101
- })
102
-
103
- // Feature -> Surface bidirectional validation
104
- model.features.forEach((feature, featureIndex) => {
105
- feature.surfaceIds.forEach((surfaceId, surfaceIndex) => {
106
- const surface = surfacesById.get(surfaceId)
107
- if (!surface) {
108
- addIssue(
109
- ctx,
110
- ['features', featureIndex, 'surfaceIds', surfaceIndex],
111
- `Feature "${feature.id}" references unknown surface "${surfaceId}"`
112
- )
113
- return
114
- }
115
-
116
- if (!surface.featureIds.includes(feature.id)) {
117
- addIssue(
118
- ctx,
119
- ['features', featureIndex, 'surfaceIds', surfaceIndex],
120
- `Feature "${feature.id}" references surface "${surfaceId}" but that surface does not include feature "${feature.id}"`
121
- )
122
- }
123
- })
124
-
125
- // Feature -> Resource bidirectional validation
126
- feature.resourceIds.forEach((resourceId, resourceIndex) => {
127
- const resourceMapping = resourceMappingsByResourceId.get(resourceId)
128
- if (!resourceMapping) {
129
- addIssue(
130
- ctx,
131
- ['features', featureIndex, 'resourceIds', resourceIndex],
132
- `Feature "${feature.id}" references unknown resource "${resourceId}"`
133
- )
134
- return
135
- }
136
-
137
- if (!resourceMapping.featureIds.includes(feature.id)) {
138
- addIssue(
139
- ctx,
140
- ['features', featureIndex, 'resourceIds', resourceIndex],
141
- `Feature "${feature.id}" references resource "${resourceId}" but that resource mapping does not include feature "${feature.id}"`
142
- )
143
- }
144
- })
145
- })
146
-
147
- // Surface -> Feature bidirectional validation and other surface refs
148
- model.navigation.surfaces.forEach((surface, surfaceIndex) => {
149
- if (surface.parentId && !surfacesById.has(surface.parentId)) {
150
- addIssue(
151
- ctx,
152
- ['navigation', 'surfaces', surfaceIndex, 'parentId'],
153
- `Surface "${surface.id}" references unknown parent surface "${surface.parentId}"`
154
- )
155
- }
156
-
157
- surface.featureIds.forEach((featureId, featureIndex) => {
158
- const feature = featuresById.get(featureId)
159
- if (!feature) {
160
- addIssue(
161
- ctx,
162
- ['navigation', 'surfaces', surfaceIndex, 'featureIds', featureIndex],
163
- `Surface "${surface.id}" references unknown feature "${featureId}"`
164
- )
165
- return
166
- }
167
-
168
- if (!feature.surfaceIds.includes(surface.id)) {
169
- addIssue(
170
- ctx,
171
- ['navigation', 'surfaces', surfaceIndex, 'featureIds', featureIndex],
172
- `Surface "${surface.id}" references feature "${featureId}" but that feature does not include surface "${surface.id}"`
173
- )
174
- }
175
- })
176
-
177
- // Surface -> Resource bidirectional validation
178
- surface.resourceIds.forEach((resourceId, resourceIndex) => {
179
- const resourceMapping = resourceMappingsByResourceId.get(resourceId)
180
- if (!resourceMapping) {
181
- addIssue(
182
- ctx,
183
- ['navigation', 'surfaces', surfaceIndex, 'resourceIds', resourceIndex],
184
- `Surface "${surface.id}" references unknown resource "${resourceId}"`
185
- )
186
- return
187
- }
188
-
189
- if (!resourceMapping.surfaceIds.includes(surface.id)) {
190
- addIssue(
191
- ctx,
192
- ['navigation', 'surfaces', surfaceIndex, 'resourceIds', resourceIndex],
193
- `Surface "${surface.id}" references resource "${resourceId}" but that surface does not include resource "${surface.id}"`
194
- )
195
- }
196
- })
197
- })
58
+ return itemsById
59
+ }
60
+
61
+ const LEGACY_FEATURE_ALIASES = new Map<string, string>([
62
+ ['crm', 'sales.crm'],
63
+ ['lead-gen', 'sales.lead-gen'],
64
+ ['submitted-requests', 'monitoring.submitted-requests']
65
+ ])
66
+
67
+ function hasFeature(featuresById: Map<string, unknown>, featureId: string): boolean {
68
+ return featuresById.has(featureId) || featuresById.has(LEGACY_FEATURE_ALIASES.get(featureId) ?? '')
69
+ }
70
+
71
+ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((model, ctx) => {
72
+ const featuresById = collectIds(model.features, ctx, ['features'], 'Feature')
73
+ model.features.forEach((feature, featureIndex) => {
74
+ const segments = feature.id.split('.')
75
+ if (segments.length > 1) {
76
+ const parentId = segments.slice(0, -1).join('.')
77
+ if (!featuresById.has(parentId)) {
78
+ addIssue(ctx, ['features', featureIndex, 'id'], `Feature "${feature.id}" references unknown parent "${parentId}"`)
79
+ }
80
+ }
81
+
82
+ const hasChildren = model.features.some(
83
+ (candidate) => candidate.id.startsWith(`${feature.id}.`) && candidate.id !== feature.id
84
+ )
85
+ if (hasChildren && feature.enabled) {
86
+ const hasEnabledDescendant = model.features.some(
87
+ (candidate) => candidate.id.startsWith(`${feature.id}.`) && candidate.enabled
88
+ )
89
+ if (!hasEnabledDescendant) {
90
+ addIssue(
91
+ ctx,
92
+ ['features', featureIndex, 'enabled'],
93
+ `Feature "${feature.id}" is enabled but has no enabled descendants`
94
+ )
95
+ }
96
+ }
97
+ })
198
98
 
199
99
  // Offerings -> CustomerSegment cross-ref: targetSegmentIds must resolve
200
100
  const segmentsById = new Map(model.customers.segments.map((seg) => [seg.id, seg]))
@@ -210,7 +110,7 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
210
110
  })
211
111
 
212
112
  // Offerings -> Feature cross-ref: deliveryFeatureId must resolve (when present)
213
- if (product.deliveryFeatureId !== undefined && !featuresById.has(product.deliveryFeatureId)) {
113
+ if (product.deliveryFeatureId !== undefined && !hasFeature(featuresById, product.deliveryFeatureId)) {
214
114
  addIssue(
215
115
  ctx,
216
116
  ['offerings', 'products', productIndex, 'deliveryFeatureId'],
@@ -242,46 +142,4 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
242
142
  }
243
143
  })
244
144
 
245
- // ResourceMapping -> Feature and Surface bidirectional validation
246
- model.resourceMappings.forEach((resourceMapping, resourceIndex) => {
247
- resourceMapping.featureIds.forEach((featureId, featureIndex) => {
248
- const feature = featuresById.get(featureId)
249
- if (!feature) {
250
- addIssue(
251
- ctx,
252
- ['resourceMappings', resourceIndex, 'featureIds', featureIndex],
253
- `Resource mapping "${resourceMapping.id}" references unknown feature "${featureId}"`
254
- )
255
- return
256
- }
257
-
258
- if (!feature.resourceIds.includes(resourceMapping.resourceId)) {
259
- addIssue(
260
- ctx,
261
- ['resourceMappings', resourceIndex, 'featureIds', featureIndex],
262
- `Resource mapping "${resourceMapping.id}" references feature "${featureId}" but that feature does not include resource "${resourceMapping.resourceId}"`
263
- )
264
- }
265
- })
266
-
267
- resourceMapping.surfaceIds.forEach((surfaceId, surfaceIndex) => {
268
- const surface = surfacesById.get(surfaceId)
269
- if (!surface) {
270
- addIssue(
271
- ctx,
272
- ['resourceMappings', resourceIndex, 'surfaceIds', surfaceIndex],
273
- `Resource mapping "${resourceMapping.id}" references unknown surface "${surfaceId}"`
274
- )
275
- return
276
- }
277
-
278
- if (!surface.resourceIds.includes(resourceMapping.resourceId)) {
279
- addIssue(
280
- ctx,
281
- ['resourceMappings', resourceIndex, 'surfaceIds', surfaceIndex],
282
- `Resource mapping "${resourceMapping.id}" references surface "${surfaceId}" but that surface does not include resource "${resourceMapping.resourceId}"`
283
- )
284
- }
285
- })
286
- })
287
- })
145
+ })
@@ -1,11 +1,11 @@
1
1
  import type { z } from 'zod'
2
2
  import { OrganizationModelBrandingSchema } from './domains/branding'
3
3
  import { OrganizationModelSalesSchema } from './domains/sales'
4
- import { OrganizationModelProjectsSchema } from './domains/projects'
5
- import { FeatureSchema } from './domains/features'
6
- import { OrganizationModelProspectingSchema } from './domains/prospecting'
7
- import { OrganizationModelNavigationSchema, SurfaceDefinitionSchema } from './domains/navigation'
8
- import { ResourceMappingSchema, TechStackEntrySchema } from './domains/shared'
4
+ import { OrganizationModelProjectsSchema } from './domains/projects'
5
+ import { FeatureSchema, NodeIdPathSchema, NodeIdStringSchema } from './domains/features'
6
+ import { OrganizationModelProspectingSchema } from './domains/prospecting'
7
+ import { OrganizationModelNavigationSchema, SurfaceDefinitionSchema } from './domains/navigation'
8
+ import { TechStackEntrySchema } from './domains/shared'
9
9
  import { OperationsDomainSchema, OperationEntrySchema, OperationSemanticClassSchema } from './domains/operations'
10
10
  import { StatusesDomainSchema, StatusEntrySchema, StatusSemanticClassSchema } from './domains/statuses'
11
11
  import { CustomersDomainSchema, CustomerSegmentSchema, FirmographicsSchema } from './domains/customers'
@@ -18,12 +18,13 @@ export type OrganizationModel = z.infer<typeof OrganizationModelSchema>
18
18
  export type OrganizationModelBranding = z.infer<typeof OrganizationModelBrandingSchema>
19
19
  export type OrganizationModelSales = z.infer<typeof OrganizationModelSalesSchema>
20
20
  export type OrganizationModelProspecting = z.infer<typeof OrganizationModelProspectingSchema>
21
- export type OrganizationModelProjects = z.infer<typeof OrganizationModelProjectsSchema>
22
- export type OrganizationModelFeature = z.infer<typeof FeatureSchema>
23
- export type OrganizationModelNavigation = z.infer<typeof OrganizationModelNavigationSchema>
24
- export type OrganizationModelSurface = z.infer<typeof SurfaceDefinitionSchema>
25
- export type OrganizationModelResourceMapping = z.infer<typeof ResourceMappingSchema>
26
- export type OrganizationModelTechStackEntry = z.infer<typeof TechStackEntrySchema>
21
+ export type OrganizationModelProjects = z.infer<typeof OrganizationModelProjectsSchema>
22
+ export type OrganizationModelFeature = z.infer<typeof FeatureSchema>
23
+ export type NodeIdPath = z.infer<typeof NodeIdPathSchema>
24
+ export type NodeIdString = z.infer<typeof NodeIdStringSchema>
25
+ export type OrganizationModelNavigation = z.infer<typeof OrganizationModelNavigationSchema>
26
+ export type OrganizationModelSurface = z.infer<typeof SurfaceDefinitionSchema>
27
+ export type OrganizationModelTechStackEntry = z.infer<typeof TechStackEntrySchema>
27
28
  export type OrganizationModelStatuses = z.infer<typeof StatusesDomainSchema>
28
29
  export type OrganizationModelStatusEntry = z.infer<typeof StatusEntrySchema>
29
30
  export type OrganizationModelStatusSemanticClass = z.infer<typeof StatusSemanticClassSchema>
@@ -1,3 +1,3 @@
1
- export const VERSION = {
2
- CURRENT: '1.6.9'
3
- }
1
+ export const VERSION = {
2
+ CURRENT: '1.6.10'
3
+ }
@@ -37,10 +37,11 @@ const mockAgent: CommandViewAgent = {
37
37
  status: 'dev',
38
38
  modelProvider: 'anthropic',
39
39
  modelId: 'claude-sonnet-4-20250514',
40
- toolCount: 5,
41
- hasKnowledgeMap: true,
42
- hasMemory: false,
43
- domains: ['support', 'sales']
40
+ toolCount: 5,
41
+ hasKnowledgeMap: true,
42
+ hasMemory: false,
43
+ links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }],
44
+ category: 'production'
44
45
  }
45
46
 
46
47
  const mockWorkflow: CommandViewWorkflow = {
@@ -52,7 +53,7 @@ const mockWorkflow: CommandViewWorkflow = {
52
53
  status: 'prod',
53
54
  stepCount: 3,
54
55
  entryPoint: 'start',
55
- domains: ['support']
56
+ links: [{ nodeId: 'feature:monitoring.submitted-requests', kind: 'operates-on' }]
56
57
  }
57
58
 
58
59
  const mockTrigger: TriggerDefinition = {
@@ -0,0 +1,30 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { ResourceCategorySchema, ResourceLinkSchema, kindPrefix, validateNodeId } from '../resource-link'
3
+ import type { OrganizationGraph } from '../../../organization-model/graph/types'
4
+
5
+ describe('resource-link', () => {
6
+ it('parses resource links and categories', () => {
7
+ expect(ResourceLinkSchema.parse({ nodeId: 'feature:sales.crm', kind: 'operates-on' })).toEqual({
8
+ nodeId: 'feature:sales.crm',
9
+ kind: 'operates-on'
10
+ })
11
+ expect(ResourceCategorySchema.parse('diagnostic')).toBe('diagnostic')
12
+ })
13
+
14
+ it('rejects node ids without a kind prefix', () => {
15
+ expect(() => ResourceLinkSchema.parse({ nodeId: 'sales.crm', kind: 'operates-on' })).toThrow()
16
+ })
17
+
18
+ it('builds and validates graph node ids', () => {
19
+ const graph: OrganizationGraph = {
20
+ version: 1,
21
+ organizationModelVersion: 1,
22
+ nodes: [{ id: 'feature:sales.crm', kind: 'feature', label: 'CRM', sourceId: 'sales.crm' }],
23
+ edges: []
24
+ }
25
+
26
+ expect(kindPrefix('feature', 'sales.crm')).toBe('feature:sales.crm')
27
+ expect(() => validateNodeId('feature:sales.crm', graph)).not.toThrow()
28
+ expect(() => validateNodeId('feature:missing', graph)).toThrow(/Unknown organization graph node/)
29
+ })
30
+ })
@@ -35,16 +35,16 @@ describe('ResourceRegistry Integration', () => {
35
35
  const createMockWorkflow = (
36
36
  resourceId: string,
37
37
  status: 'dev' | 'prod' = 'dev',
38
- domains?: string[]
38
+ links?: Array<{ nodeId: string; kind: 'operates-on' }>
39
39
  ): WorkflowDefinition => ({
40
40
  config: {
41
41
  resourceId,
42
42
  name: `Workflow ${resourceId}`,
43
43
  description: `Test workflow ${resourceId}`,
44
- version: '1.0.0',
45
- type: 'workflow',
46
- status,
47
- domains
44
+ version: '1.0.0',
45
+ type: 'workflow',
46
+ status,
47
+ links
48
48
  },
49
49
  contract: {
50
50
  inputSchema: z.object({ data: z.string() }),
@@ -67,17 +67,17 @@ describe('ResourceRegistry Integration', () => {
67
67
  const createMockAgent = (
68
68
  resourceId: string,
69
69
  status: 'dev' | 'prod' = 'dev',
70
- domains?: string[]
70
+ links?: Array<{ nodeId: string; kind: 'operates-on' }>
71
71
  ): AgentDefinition => ({
72
72
  config: {
73
73
  resourceId,
74
74
  name: `Agent ${resourceId}`,
75
75
  description: `Test agent ${resourceId}`,
76
76
  version: '1.0.0',
77
- type: 'agent',
78
- status,
79
- systemPrompt: 'You are a test agent',
80
- domains
77
+ type: 'agent',
78
+ status,
79
+ systemPrompt: 'You are a test agent',
80
+ links
81
81
  },
82
82
  contract: {
83
83
  inputSchema: z.object({ query: z.string() }),
@@ -116,7 +116,7 @@ describe('ResourceRegistry Integration', () => {
116
116
  description: 'Webhook from Shopify on new orders',
117
117
  status: 'prod',
118
118
  webhookPath: '/webhooks/shopify/orders',
119
- domains: ['sales']
119
+ links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
120
120
  },
121
121
  {
122
122
  resourceId: 'trigger-schedule-support',
@@ -127,7 +127,7 @@ describe('ResourceRegistry Integration', () => {
127
127
  description: 'Check for new support tickets every hour',
128
128
  status: 'dev',
129
129
  schedule: '0 * * * *',
130
- domains: ['support']
130
+ links: [{ nodeId: 'feature:monitoring.submitted-requests', kind: 'operates-on' }]
131
131
  },
132
132
  {
133
133
  resourceId: 'trigger-manual-test',
@@ -150,7 +150,7 @@ describe('ResourceRegistry Integration', () => {
150
150
  credentialName: 'shopify-prod',
151
151
  name: 'Shopify Production',
152
152
  description: 'E-commerce platform',
153
- domains: ['sales']
153
+ links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
154
154
  },
155
155
  {
156
156
  resourceId: 'integration-zendesk',
@@ -161,7 +161,7 @@ describe('ResourceRegistry Integration', () => {
161
161
  credentialName: 'zendesk-dev',
162
162
  name: 'Zendesk Support',
163
163
  description: 'Support ticket system',
164
- domains: ['support']
164
+ links: [{ nodeId: 'feature:monitoring.submitted-requests', kind: 'operates-on' }]
165
165
  }
166
166
  ]
167
167
 
@@ -202,7 +202,7 @@ describe('ResourceRegistry Integration', () => {
202
202
  status: 'prod',
203
203
  requestedBy: { agents: ['order-agent'] },
204
204
  routesTo: { workflows: ['order-workflow'] },
205
- domains: ['sales']
205
+ links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
206
206
  },
207
207
  {
208
208
  resourceId: 'approval-escalation',
@@ -10,12 +10,11 @@
10
10
 
11
11
  import type {
12
12
  ResourceDefinition,
13
- TriggerDefinition,
14
- IntegrationDefinition,
15
- ExternalResourceDefinition,
16
- HumanCheckpointDefinition,
17
- DomainDefinition
18
- } from './types'
13
+ TriggerDefinition,
14
+ IntegrationDefinition,
15
+ ExternalResourceDefinition,
16
+ HumanCheckpointDefinition
17
+ } from './types'
19
18
 
20
19
  // ============================================================================
21
20
  // Node Types - Resources that appear in the graph
@@ -102,12 +101,11 @@ export interface CommandViewData {
102
101
  workflows: CommandViewWorkflow[]
103
102
  agents: CommandViewAgent[]
104
103
  triggers: TriggerDefinition[]
105
- integrations: IntegrationDefinition[]
106
- externalResources: ExternalResourceDefinition[]
107
- humanCheckpoints: HumanCheckpointDefinition[]
108
- edges: CommandViewEdge[]
109
- domainDefinitions?: DomainDefinition[]
110
- }
104
+ integrations: IntegrationDefinition[]
105
+ externalResources: ExternalResourceDefinition[]
106
+ humanCheckpoints: HumanCheckpointDefinition[]
107
+ edges: CommandViewEdge[]
108
+ }
111
109
 
112
110
  // ============================================================================
113
111
  // Type Guards