@elevasis/core 0.24.1 → 0.25.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 +75 -3
- package/dist/index.js +332 -4
- package/dist/knowledge/index.d.ts +30 -1
- package/dist/organization-model/index.d.ts +75 -3
- package/dist/organization-model/index.js +332 -4
- package/dist/test-utils/index.d.ts +1 -0
- package/dist/test-utils/index.js +4 -3
- package/package.json +1 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +94 -94
- package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +10 -10
- package/src/knowledge/__tests__/queries.test.ts +960 -546
- package/src/knowledge/format.ts +322 -100
- package/src/knowledge/index.ts +18 -5
- package/src/knowledge/queries.ts +1004 -239
- package/src/organization-model/__tests__/deprecate-helpers.test.ts +71 -0
- package/src/organization-model/__tests__/resolve.test.ts +9 -7
- package/src/organization-model/__tests__/scaffolders.test.ts +93 -0
- package/src/organization-model/defaults.ts +3 -3
- package/src/organization-model/helpers.ts +76 -9
- package/src/organization-model/icons.ts +1 -0
- package/src/organization-model/index.ts +3 -2
- package/src/organization-model/published.ts +15 -2
- package/src/organization-model/scaffolders/helpers.ts +84 -0
- package/src/organization-model/scaffolders/index.ts +19 -0
- package/src/organization-model/scaffolders/scaffoldKnowledgeNode.ts +48 -0
- package/src/organization-model/scaffolders/scaffoldOntologyRecord.ts +38 -0
- package/src/organization-model/scaffolders/scaffoldResource.ts +59 -0
- package/src/organization-model/scaffolders/scaffoldSystem.ts +110 -0
- package/src/organization-model/scaffolders/types.ts +81 -0
- package/src/platform/constants/versions.ts +1 -1
- package/src/reference/_generated/contracts.md +94 -94
- package/src/reference/glossary.md +71 -69
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
getSystemDeprecationDependents,
|
|
4
|
+
type OrganizationModel,
|
|
5
|
+
type OmTopologyDomain
|
|
6
|
+
} from '../index'
|
|
7
|
+
|
|
8
|
+
const model = {
|
|
9
|
+
systems: {
|
|
10
|
+
sales: {
|
|
11
|
+
id: 'sales',
|
|
12
|
+
order: 1,
|
|
13
|
+
label: 'Sales',
|
|
14
|
+
lifecycle: 'active',
|
|
15
|
+
systems: {
|
|
16
|
+
crm: {
|
|
17
|
+
id: 'sales.crm',
|
|
18
|
+
order: 2,
|
|
19
|
+
label: 'CRM',
|
|
20
|
+
lifecycle: 'active'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
resources: {
|
|
26
|
+
'crm-active-workflow': {
|
|
27
|
+
id: 'crm-active-workflow',
|
|
28
|
+
order: 1,
|
|
29
|
+
kind: 'workflow',
|
|
30
|
+
systemPath: 'sales.crm',
|
|
31
|
+
status: 'active',
|
|
32
|
+
codeRefs: []
|
|
33
|
+
},
|
|
34
|
+
'crm-archived-workflow': {
|
|
35
|
+
id: 'crm-archived-workflow',
|
|
36
|
+
order: 2,
|
|
37
|
+
kind: 'workflow',
|
|
38
|
+
systemPath: 'sales.crm',
|
|
39
|
+
status: 'archived',
|
|
40
|
+
codeRefs: []
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
knowledge: {},
|
|
44
|
+
topology: {
|
|
45
|
+
version: 1,
|
|
46
|
+
relationships: {
|
|
47
|
+
'crm-active-uses-sales': {
|
|
48
|
+
from: { kind: 'resource', id: 'crm-active-workflow' },
|
|
49
|
+
kind: 'uses',
|
|
50
|
+
to: { kind: 'system', id: 'sales' }
|
|
51
|
+
},
|
|
52
|
+
'archived-resource-edge': {
|
|
53
|
+
from: { kind: 'resource', id: 'crm-archived-workflow' },
|
|
54
|
+
kind: 'uses',
|
|
55
|
+
to: { kind: 'system', id: 'sales' }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} satisfies OmTopologyDomain
|
|
59
|
+
} as OrganizationModel
|
|
60
|
+
|
|
61
|
+
describe('getSystemDeprecationDependents', () => {
|
|
62
|
+
it('enumerates active resources and topology edges for a system tree', () => {
|
|
63
|
+
const dependents = getSystemDeprecationDependents(model, 'sales', { includeDescendants: true })
|
|
64
|
+
|
|
65
|
+
expect(dependents.resources.map((resource) => resource.id)).toEqual(['crm-active-workflow'])
|
|
66
|
+
expect(dependents.topologyEdges.map((edge) => edge.id).sort()).toEqual([
|
|
67
|
+
'archived-resource-edge',
|
|
68
|
+
'crm-active-uses-sales'
|
|
69
|
+
])
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -31,13 +31,15 @@ describe('organization-model resolve', () => {
|
|
|
31
31
|
const business = model.navigation.sidebar.primary.business
|
|
32
32
|
expect(business?.type).toBe('group')
|
|
33
33
|
if (business?.type === 'group') {
|
|
34
|
-
expect(business.children.clients).toMatchObject({
|
|
35
|
-
type: 'surface',
|
|
36
|
-
path: '/clients',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
expect(business.children.clients).toMatchObject({
|
|
35
|
+
type: 'surface',
|
|
36
|
+
path: '/clients',
|
|
37
|
+
icon: 'clients',
|
|
38
|
+
targets: { systems: ['clients'] }
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
expect(model.systems.clients?.icon).toBe('clients')
|
|
42
|
+
})
|
|
41
43
|
|
|
42
44
|
it('adds tenant systems additively to the merged record', () => {
|
|
43
45
|
const baseSize = Object.keys(resolveOrganizationModel().systems).length
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { DEFAULT_ORGANIZATION_MODEL, scaffoldOrganizationModel, type OrganizationModel } from '..'
|
|
3
|
+
|
|
4
|
+
const BASE_MODEL: OrganizationModel = {
|
|
5
|
+
...DEFAULT_ORGANIZATION_MODEL,
|
|
6
|
+
systems: {
|
|
7
|
+
sales: {
|
|
8
|
+
id: 'sales',
|
|
9
|
+
order: 10,
|
|
10
|
+
label: 'Sales',
|
|
11
|
+
lifecycle: 'active',
|
|
12
|
+
systems: {
|
|
13
|
+
crm: {
|
|
14
|
+
id: 'sales.crm',
|
|
15
|
+
order: 20,
|
|
16
|
+
label: 'CRM',
|
|
17
|
+
lifecycle: 'active'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
resources: {},
|
|
23
|
+
knowledge: {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('organization-model scaffolders', () => {
|
|
27
|
+
it('plans the minimal system scaffold without route files', () => {
|
|
28
|
+
const plan = scaffoldOrganizationModel(BASE_MODEL, {
|
|
29
|
+
intent: 'system',
|
|
30
|
+
id: 'sales.partners',
|
|
31
|
+
label: 'Partners',
|
|
32
|
+
description: 'Partner operations.',
|
|
33
|
+
noProject: true
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
expect(plan.intent).toBe('system')
|
|
37
|
+
expect(plan.id).toBe('sales.partners')
|
|
38
|
+
expect(plan.edits.map((edit) => edit.path)).toEqual(
|
|
39
|
+
expect.arrayContaining([
|
|
40
|
+
'packages/elevasis-core/src/organization-model/systems.ts',
|
|
41
|
+
'packages/elevasis-core/src/organization-model/navigation.ts',
|
|
42
|
+
'packages/elevasis-core/src/organization-model/roles.ts',
|
|
43
|
+
'packages/elevasis-core/src/organization-model/assembly.ts',
|
|
44
|
+
'packages/elevasis-core/src/organization-model/knowledge.ts'
|
|
45
|
+
])
|
|
46
|
+
)
|
|
47
|
+
expect(plan.writes.some((write) => write.path.endsWith('manifest.stub.ts'))).toBe(true)
|
|
48
|
+
expect(plan.writes.some((write) => write.path.includes('/routes/'))).toBe(false)
|
|
49
|
+
expect(plan.projectCommand).toBeUndefined()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('defaults system scaffolds to a project chain unless skipped', () => {
|
|
53
|
+
const plan = scaffoldOrganizationModel(BASE_MODEL, {
|
|
54
|
+
intent: 'system',
|
|
55
|
+
id: 'sales.enablement',
|
|
56
|
+
label: 'Enablement'
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
expect(plan.projectCommand).toContain('project:create --link-om sales.enablement')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('keeps ontology/resource/knowledge project chains opt-in', () => {
|
|
63
|
+
const ontology = scaffoldOrganizationModel(BASE_MODEL, {
|
|
64
|
+
intent: 'ontology',
|
|
65
|
+
id: 'deal-score',
|
|
66
|
+
systemPath: 'sales.crm',
|
|
67
|
+
kind: 'object'
|
|
68
|
+
})
|
|
69
|
+
const resource = scaffoldOrganizationModel(BASE_MODEL, {
|
|
70
|
+
intent: 'resource',
|
|
71
|
+
id: 'crm-score-workflow',
|
|
72
|
+
systemPath: 'sales.crm'
|
|
73
|
+
})
|
|
74
|
+
const knowledge = scaffoldOrganizationModel(BASE_MODEL, {
|
|
75
|
+
intent: 'knowledge',
|
|
76
|
+
id: 'knowledge.crm-scoring',
|
|
77
|
+
systemPath: 'sales.crm'
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
expect(ontology.projectCommand).toBeUndefined()
|
|
81
|
+
expect(resource.projectCommand).toBeUndefined()
|
|
82
|
+
expect(knowledge.projectCommand).toBeUndefined()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('rejects duplicate system paths during planning', () => {
|
|
86
|
+
expect(() =>
|
|
87
|
+
scaffoldOrganizationModel(BASE_MODEL, {
|
|
88
|
+
intent: 'system',
|
|
89
|
+
id: 'sales.crm'
|
|
90
|
+
})
|
|
91
|
+
).toThrow('system already exists: sales.crm')
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -46,7 +46,7 @@ const DEFAULT_ORGANIZATION_MODEL_NAVIGATION: OrganizationModelNavigation = {
|
|
|
46
46
|
business: {
|
|
47
47
|
type: 'group',
|
|
48
48
|
label: 'Business',
|
|
49
|
-
icon: '
|
|
49
|
+
icon: 'briefcase',
|
|
50
50
|
order: 20,
|
|
51
51
|
children: {
|
|
52
52
|
sales: {
|
|
@@ -63,7 +63,7 @@ const DEFAULT_ORGANIZATION_MODEL_NAVIGATION: OrganizationModelNavigation = {
|
|
|
63
63
|
label: 'Clients',
|
|
64
64
|
path: '/clients',
|
|
65
65
|
surfaceType: 'list',
|
|
66
|
-
icon: '
|
|
66
|
+
icon: 'clients',
|
|
67
67
|
order: 20,
|
|
68
68
|
targets: { systems: ['clients'] }
|
|
69
69
|
},
|
|
@@ -438,7 +438,7 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
|
|
|
438
438
|
enabled: true,
|
|
439
439
|
lifecycle: 'active',
|
|
440
440
|
color: 'orange',
|
|
441
|
-
icon: '
|
|
441
|
+
icon: 'clients',
|
|
442
442
|
path: '/clients'
|
|
443
443
|
},
|
|
444
444
|
operations: {
|
|
@@ -2,6 +2,7 @@ import type { OrganizationModel, OrganizationModelSystemEntry } from './types'
|
|
|
2
2
|
import type { ContentNode } from './content-kinds/types'
|
|
3
3
|
import type { JsonValue } from './domains/systems'
|
|
4
4
|
import type { ResourceEntry } from './domains/resources'
|
|
5
|
+
import type { OmTopologyRelationship } from './domains/topology'
|
|
5
6
|
|
|
6
7
|
// W1A has landed: ContentNode and SystemEntry (with content + subsystems) are now
|
|
7
8
|
// defined in their canonical locations.
|
|
@@ -314,14 +315,80 @@ export function resolveSystemConfig(model: OrganizationModel, path: string): Rec
|
|
|
314
315
|
* getResourcesForSystem(model, 'sales', { includeDescendants: true })
|
|
315
316
|
* // → resources where systemPath === 'sales' OR systemPath starts with 'sales.'
|
|
316
317
|
*/
|
|
317
|
-
export function getResourcesForSystem(
|
|
318
|
-
model: OrganizationModel,
|
|
319
|
-
systemPath: string,
|
|
320
|
-
options: { includeDescendants?: boolean } = {}
|
|
321
|
-
): ResourceEntry[] {
|
|
318
|
+
export function getResourcesForSystem(
|
|
319
|
+
model: OrganizationModel,
|
|
320
|
+
systemPath: string,
|
|
321
|
+
options: { includeDescendants?: boolean } = {}
|
|
322
|
+
): ResourceEntry[] {
|
|
322
323
|
const { includeDescendants = false } = options
|
|
323
324
|
const prefix = systemPath + '.'
|
|
324
|
-
return Object.values(model.resources ?? {}).filter(
|
|
325
|
-
(r) => r.systemPath === systemPath || (includeDescendants && r.systemPath.startsWith(prefix))
|
|
326
|
-
)
|
|
327
|
-
}
|
|
325
|
+
return Object.values(model.resources ?? {}).filter(
|
|
326
|
+
(r) => r.systemPath === systemPath || (includeDescendants && r.systemPath.startsWith(prefix))
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export interface SystemTopologyEdge {
|
|
331
|
+
id: string
|
|
332
|
+
relationship: OmTopologyRelationship
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export interface SystemDeprecationDependents {
|
|
336
|
+
resources: ResourceEntry[]
|
|
337
|
+
topologyEdges: SystemTopologyEdge[]
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function systemPathIsInScope(candidate: string, systemPath: string, includeDescendants: boolean): boolean {
|
|
341
|
+
return candidate === systemPath || (includeDescendants && candidate.startsWith(`${systemPath}.`))
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function topologyRelationshipTouchesSystem(
|
|
345
|
+
relationship: OmTopologyRelationship,
|
|
346
|
+
systemPath: string,
|
|
347
|
+
resourceIds: Set<string>,
|
|
348
|
+
includeDescendants: boolean
|
|
349
|
+
): boolean {
|
|
350
|
+
if (
|
|
351
|
+
relationship.systemPath !== undefined &&
|
|
352
|
+
systemPathIsInScope(relationship.systemPath, systemPath, includeDescendants)
|
|
353
|
+
) {
|
|
354
|
+
return true
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
for (const ref of [relationship.from, relationship.to]) {
|
|
358
|
+
if (ref.kind === 'system' && systemPathIsInScope(ref.id, systemPath, includeDescendants)) return true
|
|
359
|
+
if (ref.kind === 'resource' && resourceIds.has(ref.id)) return true
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return false
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function getTopologyEdgesForSystem(
|
|
366
|
+
model: OrganizationModel,
|
|
367
|
+
systemPath: string,
|
|
368
|
+
options: { includeDescendants?: boolean; resourceIds?: Iterable<string> } = {}
|
|
369
|
+
): SystemTopologyEdge[] {
|
|
370
|
+
const { includeDescendants = false } = options
|
|
371
|
+
const resourceIds = new Set(
|
|
372
|
+
options.resourceIds ?? getResourcesForSystem(model, systemPath, { includeDescendants }).map((r) => r.id)
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
return Object.entries(model.topology?.relationships ?? {})
|
|
376
|
+
.filter(([, relationship]) =>
|
|
377
|
+
topologyRelationshipTouchesSystem(relationship, systemPath, resourceIds, includeDescendants)
|
|
378
|
+
)
|
|
379
|
+
.map(([id, relationship]) => ({ id, relationship }))
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function getSystemDeprecationDependents(
|
|
383
|
+
model: OrganizationModel,
|
|
384
|
+
systemPath: string,
|
|
385
|
+
options: { includeDescendants?: boolean } = {}
|
|
386
|
+
): SystemDeprecationDependents {
|
|
387
|
+
const resources = getResourcesForSystem(model, systemPath, options).filter((resource) => resource.status === 'active')
|
|
388
|
+
const resourceIds = new Set(resources.map((resource) => resource.id))
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
resources,
|
|
392
|
+
topologyEdges: getTopologyEdgesForSystem(model, systemPath, { ...options, resourceIds })
|
|
393
|
+
}
|
|
394
|
+
}
|
|
@@ -13,8 +13,9 @@ export * from './resolve'
|
|
|
13
13
|
export * from './foundation'
|
|
14
14
|
export * from './surface-projection'
|
|
15
15
|
export * from './helpers'
|
|
16
|
-
export * from './migration-helpers'
|
|
17
|
-
export * from './
|
|
16
|
+
export * from './migration-helpers'
|
|
17
|
+
export * from './scaffolders'
|
|
18
|
+
export * from './graph'
|
|
18
19
|
export * from './catalogs/lead-gen'
|
|
19
20
|
export * from './domains/branding'
|
|
20
21
|
// Phase 4: OrganizationModelSalesSchema, OrganizationModelProspectingSchema,
|
|
@@ -201,8 +201,21 @@ export {
|
|
|
201
201
|
} from './domains/knowledge'
|
|
202
202
|
export { defineOrganizationModel, resolveOrganizationModel, resolveOrganizationModelWithResources } from './resolve'
|
|
203
203
|
export type { ResolvedSystemEntry, ResolvedOrganizationModel } from './resolve'
|
|
204
|
-
export { createFoundationOrganizationModel } from './foundation'
|
|
205
|
-
|
|
204
|
+
export { createFoundationOrganizationModel } from './foundation'
|
|
205
|
+
export { scaffoldOrganizationModel } from './scaffolders'
|
|
206
|
+
export type {
|
|
207
|
+
BaseOmScaffoldSpec,
|
|
208
|
+
KnowledgeNodeScaffoldSpec,
|
|
209
|
+
OmScaffoldEdit,
|
|
210
|
+
OmScaffoldIntent,
|
|
211
|
+
OmScaffoldPlan,
|
|
212
|
+
OmScaffoldSpec,
|
|
213
|
+
OmScaffoldWrite,
|
|
214
|
+
OntologyRecordScaffoldSpec,
|
|
215
|
+
ResourceScaffoldSpec,
|
|
216
|
+
SystemScaffoldSpec
|
|
217
|
+
} from './scaffolders'
|
|
218
|
+
|
|
206
219
|
export type {
|
|
207
220
|
OntologyActionType,
|
|
208
221
|
OntologyCatalogType,
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { formatOntologyId, type OntologyKind, type OrganizationModel } from '..'
|
|
2
|
+
import { getSystem, listAllSystems } from '../helpers'
|
|
3
|
+
import type { OmScaffoldPlan } from './types'
|
|
4
|
+
|
|
5
|
+
export function slugify(input: string): string {
|
|
6
|
+
return input
|
|
7
|
+
.trim()
|
|
8
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
11
|
+
.replace(/^-+|-+$/g, '')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function titleize(input: string): string {
|
|
15
|
+
return input
|
|
16
|
+
.split(/[-._\s]+/)
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
19
|
+
.join(' ')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function assertSystemPathAvailable(model: OrganizationModel, path: string): void {
|
|
23
|
+
if (getSystem(model, path) !== undefined) {
|
|
24
|
+
throw new Error(`system already exists: ${path}`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const parent = parentPathOf(path)
|
|
28
|
+
if (parent !== undefined && getSystem(model, parent) === undefined) {
|
|
29
|
+
throw new Error(`parent system does not exist: ${parent}`)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function assertSystemExists(model: OrganizationModel, path: string): void {
|
|
34
|
+
if (getSystem(model, path) === undefined) {
|
|
35
|
+
throw new Error(`system does not exist: ${path}`)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function parentPathOf(path: string): string | undefined {
|
|
40
|
+
const index = path.lastIndexOf('.')
|
|
41
|
+
return index === -1 ? undefined : path.slice(0, index)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function localIdOf(path: string): string {
|
|
45
|
+
return path.split('.').at(-1) ?? path
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function nextSystemOrder(model: OrganizationModel, parentPath?: string): number {
|
|
49
|
+
const siblings =
|
|
50
|
+
parentPath === undefined
|
|
51
|
+
? listAllSystems(model).filter(({ path }) => !path.includes('.'))
|
|
52
|
+
: listAllSystems(model).filter(({ path }) => parentPathOf(path) === parentPath)
|
|
53
|
+
const maxOrder = siblings.reduce((max, { system }) => Math.max(max, system.order), 0)
|
|
54
|
+
return Math.ceil((maxOrder + 10) / 10) * 10
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function ontologyMapName(kind: OntologyKind): string {
|
|
58
|
+
const map: Record<OntologyKind, string> = {
|
|
59
|
+
object: 'objectTypes',
|
|
60
|
+
link: 'linkTypes',
|
|
61
|
+
action: 'actionTypes',
|
|
62
|
+
catalog: 'catalogTypes',
|
|
63
|
+
event: 'eventTypes',
|
|
64
|
+
interface: 'interfaceTypes',
|
|
65
|
+
'value-type': 'valueTypes',
|
|
66
|
+
property: 'sharedProperties',
|
|
67
|
+
group: 'groups',
|
|
68
|
+
surface: 'surfaces'
|
|
69
|
+
}
|
|
70
|
+
return map[kind]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function makeOntologyId(systemPath: string, kind: OntologyKind, localId: string): string {
|
|
74
|
+
return formatOntologyId({ scope: systemPath, kind, localId: slugify(localId) })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function addProjectNextStep(plan: OmScaffoldPlan, enabled: boolean, command: string): OmScaffoldPlan {
|
|
78
|
+
if (!enabled) return plan
|
|
79
|
+
return {
|
|
80
|
+
...plan,
|
|
81
|
+
projectCommand: command,
|
|
82
|
+
nextSteps: [...plan.nextSteps, `Run project handoff: ${command}`]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { OrganizationModel } from '..'
|
|
2
|
+
import { scaffoldKnowledgeNode } from './scaffoldKnowledgeNode'
|
|
3
|
+
import { scaffoldOntologyRecord } from './scaffoldOntologyRecord'
|
|
4
|
+
import { scaffoldResource } from './scaffoldResource'
|
|
5
|
+
import { scaffoldSystem } from './scaffoldSystem'
|
|
6
|
+
import type { OmScaffoldPlan, OmScaffoldSpec } from './types'
|
|
7
|
+
|
|
8
|
+
export function scaffoldOrganizationModel(model: OrganizationModel, spec: OmScaffoldSpec): OmScaffoldPlan {
|
|
9
|
+
if (spec.intent === 'system') return scaffoldSystem(model, spec)
|
|
10
|
+
if (spec.intent === 'ontology') return scaffoldOntologyRecord(model, spec)
|
|
11
|
+
if (spec.intent === 'resource') return scaffoldResource(model, spec)
|
|
12
|
+
return scaffoldKnowledgeNode(model, spec)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export * from './types'
|
|
16
|
+
export { scaffoldKnowledgeNode } from './scaffoldKnowledgeNode'
|
|
17
|
+
export { scaffoldOntologyRecord } from './scaffoldOntologyRecord'
|
|
18
|
+
export { scaffoldResource } from './scaffoldResource'
|
|
19
|
+
export { scaffoldSystem } from './scaffoldSystem'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { OrganizationModel } from '..'
|
|
2
|
+
import { assertSystemExists, slugify, titleize } from './helpers'
|
|
3
|
+
import type { KnowledgeNodeScaffoldSpec, OmScaffoldPlan } from './types'
|
|
4
|
+
|
|
5
|
+
export function scaffoldKnowledgeNode(model: OrganizationModel, spec: KnowledgeNodeScaffoldSpec): OmScaffoldPlan {
|
|
6
|
+
if (spec.systemPath !== undefined) assertSystemExists(model, spec.systemPath)
|
|
7
|
+
if (model.knowledge?.[spec.id] !== undefined) {
|
|
8
|
+
throw new Error(`knowledge node already exists: ${spec.id}`)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const title = spec.label ?? titleize(spec.id.replace(/^knowledge\./, ''))
|
|
12
|
+
const slug = slugify(spec.id.replace(/^knowledge\./, ''))
|
|
13
|
+
const kind = spec.kind ?? 'reference'
|
|
14
|
+
const links = spec.systemPath === undefined ? '' : `links:\n - system:${spec.systemPath}\n`
|
|
15
|
+
const ownerIds = spec.ownerRoleId === undefined ? '' : `ownerIds:\n - ${spec.ownerRoleId}\n`
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
intent: 'knowledge',
|
|
19
|
+
id: spec.id,
|
|
20
|
+
summary: `Scaffold ${kind} knowledge node "${spec.id}".`,
|
|
21
|
+
writes: [
|
|
22
|
+
{
|
|
23
|
+
path: `packages/elevasis-core/src/knowledge/nodes/${slug}.mdx.stub`,
|
|
24
|
+
mode: 'create',
|
|
25
|
+
description: 'Knowledge node MDX skeleton.',
|
|
26
|
+
content: `---\nid: ${spec.id}\nkind: ${kind}\ntitle: ${JSON.stringify(title)}\nsummary: ${JSON.stringify(spec.description ?? `${title} knowledge node.`)}\n${links}${ownerIds}updatedAt: 2026-05-15\n---\n\n## Overview\n\nCapture the durable operating knowledge here.\n`
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
edits: [
|
|
30
|
+
{
|
|
31
|
+
path: 'packages/elevasis-core/src/organization-model/knowledge.ts',
|
|
32
|
+
description: 'Register this generated node after replacing the .stub suffix.',
|
|
33
|
+
snippet: `${JSON.stringify(spec.id)}`
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
warnings: [],
|
|
37
|
+
nextSteps: [
|
|
38
|
+
'Replace the .stub suffix after the node is ready to become canonical.',
|
|
39
|
+
spec.systemPath === undefined
|
|
40
|
+
? 'Add links to the OM nodes this knowledge governs.'
|
|
41
|
+
: `Run pnpm exec elevasis om:verify --scope ${spec.systemPath}.`
|
|
42
|
+
],
|
|
43
|
+
projectCommand:
|
|
44
|
+
spec.withProject && spec.systemPath !== undefined
|
|
45
|
+
? `pnpm exec elevasis project:create --link-om ${spec.systemPath} --title "Add ${title} knowledge" --kind om-change`
|
|
46
|
+
: undefined
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { OrganizationModel } from '..'
|
|
2
|
+
import { assertSystemExists, makeOntologyId, ontologyMapName, titleize } from './helpers'
|
|
3
|
+
import type { OmScaffoldPlan, OntologyRecordScaffoldSpec } from './types'
|
|
4
|
+
|
|
5
|
+
export function scaffoldOntologyRecord(model: OrganizationModel, spec: OntologyRecordScaffoldSpec): OmScaffoldPlan {
|
|
6
|
+
assertSystemExists(model, spec.systemPath)
|
|
7
|
+
const localId = spec.localId ?? spec.id
|
|
8
|
+
const ontologyId = makeOntologyId(spec.systemPath, spec.kind, localId)
|
|
9
|
+
const label = spec.label ?? titleize(localId)
|
|
10
|
+
const mapName = ontologyMapName(spec.kind)
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
intent: 'ontology',
|
|
14
|
+
id: ontologyId,
|
|
15
|
+
summary: `Scaffold ${spec.kind} ontology record "${ontologyId}".`,
|
|
16
|
+
writes: [],
|
|
17
|
+
edits: [
|
|
18
|
+
{
|
|
19
|
+
path: 'packages/elevasis-core/src/organization-model/systems.ts',
|
|
20
|
+
description: `Add this record under ${spec.systemPath}.ontology.${mapName}.`,
|
|
21
|
+
snippet: `${JSON.stringify(ontologyId)}: {
|
|
22
|
+
id: ${JSON.stringify(ontologyId)},
|
|
23
|
+
label: ${JSON.stringify(label)},
|
|
24
|
+
ownerSystemId: ${JSON.stringify(spec.systemPath)}${spec.description === undefined ? '' : `,
|
|
25
|
+
description: ${JSON.stringify(spec.description)}`}
|
|
26
|
+
}`
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
warnings: [],
|
|
30
|
+
nextSteps: [
|
|
31
|
+
'Wire any cross-references using canonical ontology IDs.',
|
|
32
|
+
`Run pnpm exec elevasis om:verify --scope ${spec.systemPath}.`
|
|
33
|
+
],
|
|
34
|
+
projectCommand: spec.withProject
|
|
35
|
+
? `pnpm exec elevasis project:create --link-om ${spec.systemPath} --title "Add ${label} ontology" --kind om-change`
|
|
36
|
+
: undefined
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { OrganizationModel } from '..'
|
|
2
|
+
import { assertSystemExists, slugify, titleize } from './helpers'
|
|
3
|
+
import type { OmScaffoldPlan, ResourceScaffoldSpec } from './types'
|
|
4
|
+
|
|
5
|
+
export function scaffoldResource(model: OrganizationModel, spec: ResourceScaffoldSpec): OmScaffoldPlan {
|
|
6
|
+
assertSystemExists(model, spec.systemPath)
|
|
7
|
+
if (model.resources?.[spec.id] !== undefined) {
|
|
8
|
+
throw new Error(`resource already exists: ${spec.id}`)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const label = spec.label ?? titleize(spec.id)
|
|
12
|
+
const kind = spec.kind ?? 'workflow'
|
|
13
|
+
const slug = slugify(spec.id)
|
|
14
|
+
const writes = spec.withStubWorkflow
|
|
15
|
+
? [
|
|
16
|
+
{
|
|
17
|
+
path: `packages/elevasis-core/src/workflows/${slug}.stub.ts`,
|
|
18
|
+
mode: 'create' as const,
|
|
19
|
+
description: 'Optional workflow implementation stub. Runtime wiring remains manual.',
|
|
20
|
+
content: `// Stub only. Replace with a real workflow implementation before registering the resource.\nexport const ${slug.replaceAll('-', '_')}Workflow = {\n id: ${JSON.stringify(spec.id)}\n}\n`
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
: []
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
intent: 'resource',
|
|
27
|
+
id: spec.id,
|
|
28
|
+
summary: `Scaffold ${kind} Resource "${spec.id}" for System "${spec.systemPath}".`,
|
|
29
|
+
writes,
|
|
30
|
+
edits: [
|
|
31
|
+
{
|
|
32
|
+
path: 'packages/elevasis-core/src/organization-model/resources.ts',
|
|
33
|
+
description: 'Add this descriptor to platformResources/platformResourceDescriptors.',
|
|
34
|
+
snippet: `${JSON.stringify(spec.id)}: {
|
|
35
|
+
id: ${JSON.stringify(spec.id)},
|
|
36
|
+
order: 0,
|
|
37
|
+
kind: ${JSON.stringify(kind)},
|
|
38
|
+
systemPath: ${JSON.stringify(spec.systemPath)},
|
|
39
|
+
title: ${JSON.stringify(label)},
|
|
40
|
+
${spec.description === undefined ? '' : `description: ${JSON.stringify(spec.description)},\n `}${spec.ownerRoleId === undefined ? '' : `ownerRoleId: ${JSON.stringify(spec.ownerRoleId)},\n `}status: 'active',
|
|
41
|
+
codeRefs: []
|
|
42
|
+
}`
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
path: 'packages/elevasis-core/src/organization-model/topology.ts',
|
|
46
|
+
description: 'Add topology relationships only when this resource triggers, uses, or requires approval from another OM node.',
|
|
47
|
+
snippet: `// topologyRelationship({ from: topologyRef('resource', ${JSON.stringify(spec.id)}), kind: 'uses', to: ... })`
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
warnings: ['Resource ontology bindings are intentionally omitted until action/read/write contracts are known.'],
|
|
51
|
+
nextSteps: [
|
|
52
|
+
'Bind resource.ontology.actions/reads/writes after the ontology records exist.',
|
|
53
|
+
`Run pnpm exec elevasis om:verify --scope ${spec.systemPath}.`
|
|
54
|
+
],
|
|
55
|
+
projectCommand: spec.withProject
|
|
56
|
+
? `pnpm exec elevasis project:create --link-om ${spec.systemPath} --title "Add ${label} resource" --kind om-change`
|
|
57
|
+
: undefined
|
|
58
|
+
}
|
|
59
|
+
}
|