@elevasis/core 0.2.1 → 0.4.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 (45) hide show
  1. package/dist/index.d.ts +63 -103
  2. package/dist/index.js +431 -111
  3. package/dist/organization-model/index.d.ts +63 -103
  4. package/dist/organization-model/index.js +431 -111
  5. package/package.json +1 -1
  6. package/src/README.md +1 -1
  7. package/src/__tests__/template-foundations-compatibility.test.ts +28 -36
  8. package/src/auth/multi-tenancy/types.ts +4 -11
  9. package/src/auth/multi-tenancy/users/api-schemas.ts +1 -1
  10. package/src/business/base-entities.test.ts +481 -0
  11. package/src/business/base-entities.ts +241 -0
  12. package/src/business/delivery/types.ts +1 -1
  13. package/src/business/index.ts +3 -0
  14. package/src/execution/index.ts +3 -6
  15. package/src/index.ts +1 -1
  16. package/src/organization-model/README.md +25 -26
  17. package/src/organization-model/__tests__/graph.test.ts +103 -71
  18. package/src/organization-model/__tests__/resolve.test.ts +22 -31
  19. package/src/organization-model/contracts.ts +3 -0
  20. package/src/organization-model/defaults.ts +59 -7
  21. package/src/organization-model/domains/features.ts +19 -54
  22. package/src/organization-model/domains/navigation.ts +266 -17
  23. package/src/organization-model/domains/shared.ts +1 -10
  24. package/src/organization-model/foundation.ts +97 -0
  25. package/src/organization-model/graph/build.ts +34 -67
  26. package/src/organization-model/graph/schema.ts +2 -4
  27. package/src/organization-model/graph/types.ts +3 -15
  28. package/src/organization-model/index.ts +2 -0
  29. package/src/organization-model/organization-graph.mdx +37 -28
  30. package/src/organization-model/organization-model.mdx +34 -36
  31. package/src/organization-model/published.ts +12 -3
  32. package/src/organization-model/schema.ts +38 -34
  33. package/src/organization-model/types.ts +5 -10
  34. package/src/platform/constants/versions.ts +1 -1
  35. package/src/platform/sse/events.ts +1 -34
  36. package/src/projects/api-schemas.ts +2 -1
  37. package/src/reference/_generated/contracts.md +10 -31
  38. package/src/reference/glossary.md +14 -18
  39. package/src/supabase/database.types.ts +0 -107
  40. package/src/test-utils/rls/RLSTestContext.ts +1 -31
  41. package/src/execution/calibration/__tests__/schemas.test.ts +0 -320
  42. package/src/execution/calibration/index.ts +0 -3
  43. package/src/execution/calibration/schemas.ts +0 -121
  44. package/src/execution/calibration/sse-events.ts +0 -125
  45. package/src/execution/calibration/types.ts +0 -190
@@ -73,16 +73,6 @@ function ensureResourceNode(
73
73
  })
74
74
  }
75
75
 
76
- function titleCase(value: string): string {
77
- return value
78
- .replace(/[-_.]+/g, ' ')
79
- .replace(/\s+/g, ' ')
80
- .trim()
81
- .split(' ')
82
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
83
- .join(' ')
84
- }
85
-
86
76
  type CommandViewResource =
87
77
  | CommandViewData['workflows'][number]
88
78
  | CommandViewData['agents'][number]
@@ -126,38 +116,10 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
126
116
  }
127
117
  pushUniqueNode(nodes, nodeIds, organizationNode)
128
118
 
129
- const featureEntries = Object.entries(organizationModel.features.enabled)
130
- .map(([featureKey, enabled]) => {
131
- const key = featureKey as keyof typeof organizationModel.features.enabled
132
- return {
133
- key: featureKey,
134
- enabled,
135
- label: organizationModel.features.labels[key] ?? titleCase(featureKey)
136
- }
137
- })
138
- .sort((a, b) => a.key.localeCompare(b.key))
139
-
140
- for (const feature of featureEntries) {
141
- const id = nodeId('feature', feature.key)
142
- pushUniqueNode(nodes, nodeIds, {
143
- id,
144
- kind: 'feature',
145
- label: feature.label,
146
- sourceId: feature.key,
147
- enabled: feature.enabled,
148
- featureKey: feature.key as OrganizationGraphNode['featureKey']
149
- })
150
- pushUniqueEdge(edges, edgeIds, {
151
- id: edgeId('contains', organizationNode.id, id),
152
- kind: 'contains',
153
- sourceId: organizationNode.id,
154
- targetId: id
155
- })
156
- }
157
-
158
- const domainMap = new Map<string, (typeof organizationModel.domains)[number]>()
159
- for (const domain of organizationModel.domains) {
160
- domainMap.set(domain.id, domain)
119
+ // Build feature map for lookups
120
+ const featureMap = new Map<string, (typeof organizationModel.features)[number]>()
121
+ for (const feature of organizationModel.features) {
122
+ featureMap.set(feature.id, feature)
161
123
  }
162
124
 
163
125
  const surfaceMap = new Map<string, (typeof organizationModel.navigation.surfaces)[number]>()
@@ -167,7 +129,9 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
167
129
 
168
130
  const entityIds = new Set<string>()
169
131
  const capabilityIds = new Set<string>()
170
- const resourceMappings = [...organizationModel.resourceMappings].sort((a, b) => a.resourceId.localeCompare(b.resourceId))
132
+ const resourceMappings = [...organizationModel.resourceMappings].sort((a, b) =>
133
+ a.resourceId.localeCompare(b.resourceId)
134
+ )
171
135
  for (const resourceMapping of resourceMappings) {
172
136
  for (const entityId of resourceMapping.entityIds) {
173
137
  entityIds.add(entityId)
@@ -177,14 +141,17 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
177
141
  }
178
142
  }
179
143
 
180
- for (const domain of [...organizationModel.domains].sort((a, b) => a.id.localeCompare(b.id))) {
181
- const id = nodeId('domain', domain.id)
144
+ // Unified feature loop: creates feature nodes with semantic data (replaces old feature + domain loops)
145
+ for (const feature of [...organizationModel.features].sort((a, b) => a.id.localeCompare(b.id))) {
146
+ const id = nodeId('feature', feature.id)
182
147
  pushUniqueNode(nodes, nodeIds, {
183
148
  id,
184
- kind: 'domain',
185
- label: domain.label,
186
- sourceId: domain.id,
187
- description: domain.description
149
+ kind: 'feature',
150
+ label: feature.label,
151
+ sourceId: feature.id,
152
+ description: feature.description,
153
+ enabled: feature.enabled,
154
+ featureId: feature.id
188
155
  })
189
156
  pushUniqueEdge(edges, edgeIds, {
190
157
  id: edgeId('contains', organizationNode.id, id),
@@ -193,13 +160,13 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
193
160
  targetId: id
194
161
  })
195
162
 
196
- for (const entityId of domain.entityIds) {
163
+ for (const entityId of feature.entityIds) {
197
164
  entityIds.add(entityId)
198
165
  }
199
- for (const capabilityId of domain.capabilityIds) {
166
+ for (const capabilityId of feature.capabilityIds) {
200
167
  capabilityIds.add(capabilityId)
201
168
  }
202
- for (const surfaceId of domain.surfaceIds) {
169
+ for (const surfaceId of feature.surfaceIds) {
203
170
  const surface = surfaceMap.get(surfaceId)
204
171
  if (surface) {
205
172
  pushUniqueEdge(edges, edgeIds, {
@@ -229,22 +196,22 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
229
196
  targetId: id
230
197
  })
231
198
 
232
- if (surface.featureKey) {
199
+ if (surface.featureId) {
233
200
  pushUniqueEdge(edges, edgeIds, {
234
- id: edgeId('exposes', nodeId('feature', surface.featureKey), id),
201
+ id: edgeId('exposes', nodeId('feature', surface.featureId), id),
235
202
  kind: 'exposes',
236
- sourceId: nodeId('feature', surface.featureKey),
203
+ sourceId: nodeId('feature', surface.featureId),
237
204
  targetId: id
238
205
  })
239
206
  }
240
207
 
241
- for (const domainId of surface.domainIds) {
242
- if (domainMap.has(domainId)) {
208
+ for (const featureId of surface.featureIds) {
209
+ if (featureMap.has(featureId)) {
243
210
  pushUniqueEdge(edges, edgeIds, {
244
- id: edgeId('references', id, nodeId('domain', domainId)),
211
+ id: edgeId('references', id, nodeId('feature', featureId)),
245
212
  kind: 'references',
246
213
  sourceId: id,
247
- targetId: nodeId('domain', domainId)
214
+ targetId: nodeId('feature', featureId)
248
215
  })
249
216
  }
250
217
  }
@@ -317,13 +284,13 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
317
284
  targetId: id
318
285
  })
319
286
 
320
- for (const domainId of resourceMapping.domainIds) {
321
- if (domainMap.has(domainId)) {
287
+ for (const featureId of resourceMapping.featureIds) {
288
+ if (featureMap.has(featureId)) {
322
289
  pushUniqueEdge(edges, edgeIds, {
323
- id: edgeId('maps_to', id, nodeId('domain', domainId)),
290
+ id: edgeId('maps_to', id, nodeId('feature', featureId)),
324
291
  kind: 'maps_to',
325
292
  sourceId: id,
326
- targetId: nodeId('domain', domainId)
293
+ targetId: nodeId('feature', featureId)
327
294
  })
328
295
  }
329
296
  }
@@ -374,13 +341,13 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
374
341
  resourceType: normalizeCommandViewResourceType(resource.type)
375
342
  })
376
343
 
377
- for (const domainId of resource.domains ?? []) {
378
- if (domainMap.has(domainId)) {
344
+ for (const featureId of resource.domains ?? []) {
345
+ if (featureMap.has(featureId)) {
379
346
  pushUniqueEdge(edges, edgeIds, {
380
- id: edgeId('references', resourceNode.id, nodeId('domain', domainId), 'domain'),
347
+ id: edgeId('references', resourceNode.id, nodeId('feature', featureId), 'feature'),
381
348
  kind: 'references',
382
349
  sourceId: resourceNode.id,
383
- targetId: nodeId('domain', domainId)
350
+ targetId: nodeId('feature', featureId)
384
351
  })
385
352
  }
386
353
  }
@@ -1,13 +1,11 @@
1
1
  import { z } from 'zod'
2
- import { DescriptionSchema, LabelSchema } from '../domains/shared'
3
- import { FeatureKeySchema } from '../domains/features'
2
+ import { DescriptionSchema, LabelSchema, ModelIdSchema } from '../domains/shared'
4
3
  import { SurfaceTypeSchema } from '../domains/navigation'
5
4
  import { OrganizationModelSchema } from '../schema'
6
5
 
7
6
  export const OrganizationGraphNodeKindSchema = z.enum([
8
7
  'organization',
9
8
  'feature',
10
- 'domain',
11
9
  'surface',
12
10
  'entity',
13
11
  'capability',
@@ -23,7 +21,7 @@ export const OrganizationGraphNodeSchema = z.object({
23
21
  sourceId: z.string().trim().min(1).max(255).optional(),
24
22
  description: DescriptionSchema.optional(),
25
23
  enabled: z.boolean().optional(),
26
- featureKey: FeatureKeySchema.optional(),
24
+ featureId: ModelIdSchema.optional(),
27
25
  surfaceType: SurfaceTypeSchema.optional(),
28
26
  resourceType: z.enum(['workflow', 'agent', 'trigger', 'integration', 'external', 'human_checkpoint']).optional()
29
27
  })
@@ -1,20 +1,8 @@
1
1
  import type { CommandViewData } from '../../platform/registry/command-view'
2
- import type {
3
- OrganizationModel,
4
- OrganizationModelFeatureKey,
5
- OrganizationModelResourceMapping,
6
- OrganizationModelSurface
7
- } from '../types'
2
+ import type { OrganizationModel, OrganizationModelResourceMapping, OrganizationModelSurface } from '../types'
8
3
  import type { RelationshipType } from '../../platform/registry/command-view'
9
4
 
10
- export type OrganizationGraphNodeKind =
11
- | 'organization'
12
- | 'feature'
13
- | 'domain'
14
- | 'surface'
15
- | 'entity'
16
- | 'capability'
17
- | 'resource'
5
+ export type OrganizationGraphNodeKind = 'organization' | 'feature' | 'surface' | 'entity' | 'capability' | 'resource'
18
6
 
19
7
  export type OrganizationGraphEdgeKind = 'contains' | 'references' | 'exposes' | 'maps_to'
20
8
 
@@ -25,7 +13,7 @@ export interface OrganizationGraphNode {
25
13
  sourceId?: string
26
14
  description?: string
27
15
  enabled?: boolean
28
- featureKey?: OrganizationModelFeatureKey
16
+ featureId?: string
29
17
  surfaceType?: OrganizationModelSurface['surfaceType']
30
18
  resourceType?: OrganizationModelResourceMapping['resourceType']
31
19
  }
@@ -1,7 +1,9 @@
1
1
  export * from './schema'
2
2
  export * from './types'
3
+ export * from './contracts'
3
4
  export * from './defaults'
4
5
  export * from './resolve'
6
+ export * from './foundation'
5
7
  export * from './graph'
6
8
  export * from './domains/branding'
7
9
  export * from './domains/crm'
@@ -5,7 +5,7 @@ description: Organization OS Graph layer documentation for the Cytoscape-based o
5
5
 
6
6
  ## Overview
7
7
 
8
- Within Organization OS, the organization graph is the dedicated **Graph** layer. It treats the organization model as the top-level ontology and bridges in Command View runtime topology so one graph can support semantic exploration and operations-oriented tracing. It is not a replacement renderer for Command View; it is a shared graph product that subsumes Command View as one operational lens.
8
+ Within Organization OS, the organization graph is the dedicated **Graph** layer. It treats the organization model as the top-level ontology and bridges in Command View runtime topology so one graph can support semantic exploration and operations-oriented tracing. Command View is now one lens over this shared graph, not a separate live graph renderer.
9
9
 
10
10
  Graph contracts live in `@repo/core` alongside the organization model. Rendering lives in `@repo/ui` with Cytoscape.js. The command-center route is a thin wrapper over the shared page.
11
11
 
@@ -32,7 +32,7 @@ Graph types are intentionally **not** part of the published `@elevasis/core` sur
32
32
  The graph helps users:
33
33
 
34
34
  - orient themselves in the organization model
35
- - discover how domains, capabilities, surfaces, entities, resources, and workflows connect
35
+ - discover how features, capabilities, surfaces, entities, resources, and workflows connect
36
36
  - trace upstream/downstream dependencies
37
37
  - understand ownership and implementation boundaries
38
38
  - assess blast radius before changing a workflow, agent, feature, or integration
@@ -46,21 +46,22 @@ The graph does not replace workflow editors, execution-run visualizers, or build
46
46
 
47
47
  The implementation uses one typed graph, not separate semantic and implementation taxonomies.
48
48
 
49
- - Node kinds: `organization`, `feature`, `domain`, `surface`, `entity`, `capability`, `resource`
49
+ - Node kinds: `organization`, `feature`, `surface`, `entity`, `capability`, `resource`
50
50
  - Edge kinds: `contains`, `references`, `exposes`, `maps_to`
51
51
  - `resource` nodes carry `resourceType` metadata: `workflow`, `agent`, `trigger`, `integration`, `external`, or `human_checkpoint`
52
52
  - `references` edges may also carry `relationshipType`: `triggers`, `uses`, or `approval`
53
53
 
54
- This means runtime topology is represented as bridged `resource` nodes plus relationship metadata on shared edge types, rather than as a second disconnected edge/node vocabulary.
54
+ This means runtime topology is represented as bridged `resource` nodes plus relationship metadata on shared edge types, rather than as a second disconnected edge/node vocabulary. `maps_to` is the main crossover edge: the builder emits it from organization-model resource mappings, and the UI presence helpers also treat it as topology-facing when classifying which non-resource nodes participate in runtime-adjacent views.
55
55
 
56
56
  ### Compatibility rules with the organization model
57
57
 
58
58
  - `OrganizationModelSchema` is the source of truth for semantic structure. The graph does not introduce a second ontology.
59
59
  - Graph node IDs and references reuse organization-model IDs wherever those IDs already exist.
60
- - `domain` nodes derive from `organizationModel.domains`.
61
60
  - `surface` nodes derive from `organizationModel.navigation.surfaces`; graph routing respects surface `path` and `defaultSurfaceId`.
62
61
  - Feature gating and labels respect `organizationModel.features.enabled` and `.labels`.
63
62
  - Implementation-resource bridging prefers `organizationModel.resourceMappings`.
63
+ - Semantic grouping now comes from the unified `organizationModel.features` array. The builder no longer emits separate `domain` nodes.
64
+ - Command View resource `domains` metadata is currently bridged onto `feature` references, not onto a distinct domain-node layer.
64
65
  - Graph defaults remain valid when produced by `resolveOrganizationModel()`.
65
66
  - If the graph needs a concept the model cannot express, extend the model first.
66
67
 
@@ -70,7 +71,6 @@ This means runtime topology is represented as bridged `resource` nodes plus rela
70
71
  type OrganizationGraphNodeKind =
71
72
  | 'organization'
72
73
  | 'feature'
73
- | 'domain'
74
74
  | 'surface'
75
75
  | 'entity'
76
76
  | 'capability'
@@ -86,7 +86,7 @@ interface OrganizationGraph {
86
86
  }
87
87
  ```
88
88
 
89
- `OrganizationGraphNode` also includes optional `sourceId`, `description`, `enabled`, `featureKey`, `surfaceType`, and `resourceType`. `OrganizationGraphEdge` includes optional `label` and `relationshipType`.
89
+ `OrganizationGraphNode` also includes optional `sourceId`, `description`, `enabled`, `featureId`, `surfaceType`, and `resourceType`. `OrganizationGraphEdge` includes optional `label` and `relationshipType`.
90
90
 
91
91
  ### Build pipeline
92
92
 
@@ -100,9 +100,9 @@ interface BuildOrganizationGraphInput {
100
100
  `buildOrganizationGraph` accepts optional topology input so the semantic graph still renders when operational bridging is sparse. The derivation flow is additive and upsert-oriented:
101
101
 
102
102
  1. Resolve the active organization model.
103
- 2. Derive semantic nodes and edges from domains, features, surfaces, entities, capabilities, and resource mappings.
103
+ 2. Derive semantic nodes and edges from features, surfaces, entities, capabilities, and resource mappings.
104
104
  3. Upsert bridged runtime resources from Command View data into shared `resource` nodes.
105
- 4. Merge topology relationships onto the same DTO using `references` or `maps_to` edges plus optional `relationshipType`.
105
+ 4. Bridge Command View runtime topology onto the same DTO as `references` edges between `resource` nodes, using `relationshipType` for runtime relationship labels.
106
106
  5. Hand the resulting graph to UI-level lensing, filtering, and Cytoscape projection.
107
107
 
108
108
  Keeping assembly outside the renderer keeps graph semantics testable without UI and prevents Cytoscape concepts from leaking into business logic.
@@ -110,7 +110,7 @@ Keeping assembly outside the renderer keeps graph semantics testable without UI
110
110
  ### Ownership split
111
111
 
112
112
  - `@repo/core` owns normalized node/edge types, schemas, and build/derivation helpers.
113
- - `@repo/ui` owns Cytoscape element conversion, layout presets, selection/hover/expansion/viewport state, detail panels, and presentation helpers.
113
+ - `@repo/ui` owns Cytoscape element conversion, layout presets, selection state, path tracing, viewport mechanics, detail panels, and presentation helpers.
114
114
  - `apps/command-center` owns route wiring and page-level integration.
115
115
 
116
116
  ## Interaction Model
@@ -124,6 +124,8 @@ The graph ships with two lens presets in UI code:
124
124
 
125
125
  Lenses do not change the core DTO. They configure how the shared graph is initially presented.
126
126
 
127
+ The command-view preset is intentionally narrow. It starts from topology-focused `resource` nodes so runtime traces stay readable, even though the underlying graph still contains semantic nodes and bridge edges.
128
+
127
129
  ### Modes
128
130
 
129
131
  - **Map mode** -- clustered for broad graph orientation
@@ -138,18 +140,13 @@ Layouts are deterministic presets, not unconstrained force simulation:
138
140
 
139
141
  ### Interactions
140
142
 
141
- Primary set, kept small and high-value:
143
+ Primary set in the current shared page:
142
144
 
143
145
  - click node for detail panel
144
146
  - click edge for relationship meaning
145
- - hover for adjacency preview
146
- - search and jump to node
147
- - expand immediate neighbors
148
- - isolate selected node + N-hop neighborhood
149
147
  - switch mode
150
- - pin selected nodes
151
- - highlight shortest or most relevant path between two nodes
152
- - filter by node kind, domain, capability, feature, status, environment
148
+ - highlight shortest directed paths between two visible nodes in trace mode
149
+ - filter by node kind, topology presence, and free-text search
153
150
 
154
151
  Deferred: freeform node placement, collaborative annotations, graph editing/authoring, arbitrary canvas drawing.
155
152
 
@@ -163,13 +160,15 @@ Filter inputs:
163
160
  - `nodeKinds`
164
161
  - `topologyPresence: 'all' | 'semantic-only' | 'topology-only'`
165
162
 
166
- Presence is derived per node:
163
+ Presence is derived per node in UI helpers:
167
164
 
168
165
  - `resource` nodes are `topology-only`
169
- - nodes with only semantic edges are `semantic-only`
170
- - nodes with both semantic and topology edges are treated as bridge nodes internally
166
+ - non-resource nodes with only semantic edges are `semantic-only`
167
+ - non-resource nodes with `maps_to` edges or runtime relationship edges alongside semantic edges are treated as `bridge` nodes internally
168
+
169
+ `bridge` is an internal classification used by the filtering helpers. The public filter control only exposes `all`, `semantic-only`, and `topology-only`, so bridge nodes appear when presence is `all`.
171
170
 
172
- Search matches node IDs, labels, descriptions, source IDs, feature keys, surface/resource types, edge labels, edge kinds, and `relationshipType`.
171
+ Search matches node IDs, labels, descriptions, source IDs, feature IDs, surface/resource types, edge labels, edge kinds, and `relationshipType`.
173
172
 
174
173
  The page applies `useDeferredValue()` to the active filters so graph search stays responsive while typing.
175
174
 
@@ -188,19 +187,28 @@ The graph is exposed through `ElevasisFeaturesProvider` rather than as app-local
188
187
  - The provider resolves that against `organizationModel.navigation.surfaces` and exposes the result as `organizationGraph` in context.
189
188
  - Shared UI/operations code stays manifest-driven while remaining aware of semantic organization topology.
190
189
 
190
+ This provider bridge resolves the canonical shared graph surface, not a Command View-specific surface. Command View still has its own navigation surface (`operations.command-view`), but the live page delegates into the shared graph renderer.
191
+
191
192
  ## Command View Integration
192
193
 
193
- `CommandViewPage` now delegates to `OrganizationGraphPage` through a command-view lens preset. The lens restores high-value operational context through:
194
+ `CommandViewPage` now delegates to `OrganizationGraphPage` through a command-view lens preset. The shared page adds command-view operational context through:
194
195
 
195
- - route-level execution-health cards
196
+ - command-view summary cards rendered in `OrganizationGraphPage`
196
197
  - selection-aware resource and human-checkpoint summaries inside the shared graph detail panel
197
198
  - follow-up actions for recent executions and pending tasks inside the detail panel
198
- - dedicated command-view sidebar content (`Command View` labeling and node filters live in the subshell sidebar, not on the graph canvas)
199
+ - dedicated command-view sidebar content alongside the shared graph controls
199
200
 
200
201
  Operational summary and drill-down derivation is covered by tests under `packages/ui/src/features/operations/organization-graph/__tests__/`.
201
202
 
202
203
  The detail experience is shared through `OrganizationGraphDetailPanel`, which can render both semantic context and command-view-specific follow-up sections.
203
204
 
205
+ Keep the current seam explicit:
206
+
207
+ - The graph canvas, selection model, path tracing, shared filters, and detail panel all live in `OrganizationGraphPage`.
208
+ - `OperationsSidebarMiddle` still renders `CommandViewSidebarContent` for `/operations/command-view`.
209
+ - That sidebar remains a companion operational panel with some legacy store/filter assumptions and is not the source of truth for the shared graph's filter state.
210
+ - Older React Flow Command View code still exists in the repo, but it is no longer the live renderer for the current Command View route.
211
+
204
212
  ## Cytoscape Responsibility Split
205
213
 
206
214
  Cytoscape owns:
@@ -217,16 +225,17 @@ React owns:
217
225
 
218
226
  - mode switching
219
227
  - side panels and inspectors
220
- - search and command-palette entry
228
+ - filter toolbar search and trace controls
221
229
  - route state and deep-linking
222
230
  - server data fetching
223
- - persistence of saved filters and views
224
231
 
225
232
  ## Package Boundary
226
233
 
227
234
  - `packages/ui/package.json` declares Cytoscape on the published UI surface.
228
235
  - `packages/ui/tsup.config.ts` and `packages/ui/rollup.dts.config.mjs` keep Cytoscape **externalized** for publish output.
229
- - Graph DTO types are internal to `@repo/core`. The published `@elevasis/core` wrapper still exposes only the narrow organization-model API. External graph adoption should not be assumed.
236
+ - Graph DTO types, schemas, and builders live in monorepo `@repo/core` organization-model modules.
237
+ - Cytoscape rendering, lens presets, filtering helpers, and detail/runtime presentation live in `@repo/ui`.
238
+ - The published `@elevasis/core` wrapper still exposes only the narrow organization-model API; this graph contract should be treated as monorepo-internal for now.
230
239
 
231
240
  ## Theming
232
241
 
@@ -21,7 +21,7 @@ The model does **not** replace the shared feature-provider system. It enriches a
21
21
  - `packages/core/src/organization-model/types.ts` -- exported TypeScript types
22
22
  - `packages/core/src/organization-model/defaults.ts` -- `DEFAULT_ORGANIZATION_MODEL`
23
23
  - `packages/core/src/organization-model/resolve.ts` -- `defineOrganizationModel`, `resolveOrganizationModel`
24
- - `packages/core/src/organization-model/domains/*.ts` -- feature keys, navigation surfaces, CRM/lead-gen/delivery semantics
24
+ - `packages/core/src/organization-model/domains/*.ts` -- feature schema, navigation surfaces, CRM/lead-gen/delivery semantics
25
25
  - `packages/core/src/published.ts` -- curated root barrel for the published package
26
26
  - `packages/core/src/organization-model/published.ts` -- curated organization-model barrel
27
27
  - `packages/core/src/__tests__/template-foundations-compatibility.test.ts` -- adapter-baseline guard
@@ -31,9 +31,8 @@ The model does **not** replace the shared feature-provider system. It enriches a
31
31
  Top-level fields on `OrganizationModel`:
32
32
 
33
33
  - `version`
34
- - `domains` -- semantic domains (`crm`, `lead-gen`, `delivery`, `operations`)
34
+ - `features` -- unified feature array (`OrganizationModelFeature[]`); each entry combines access gating, semantic grouping, and display metadata
35
35
  - `branding`
36
- - `features` -- `enabled` map and `labels` overrides
37
36
  - `navigation` -- surfaces, groups, `defaultSurfaceId`
38
37
  - `crm` -- pipeline stages and stage semantics
39
38
  - `leadGen` -- company/contact lifecycle stages
@@ -61,24 +60,21 @@ All `id` fields, `parentId`, `defaultSurfaceId`, and reference ID arrays use `Mo
61
60
 
62
61
  This applies to domain IDs, surface IDs, navigation group IDs, and resource mapping IDs.
63
62
 
64
- ### Default Feature Keys
63
+ ### Default Features
65
64
 
66
- ```ts
67
- type OrganizationModelFeatureKey =
68
- | 'acquisition'
69
- | 'delivery'
70
- | 'operations'
71
- | 'monitoring'
72
- | 'settings'
73
- | 'seo'
74
- | 'calibration'
75
- ```
65
+ Seven features ship by default in `DEFAULT_ORGANIZATION_MODEL.features`:
76
66
 
77
- These are the **grouped** published access/visibility keys. They differ from shell feature-module keys (`crm`, `lead-gen`, `delivery`). See [Feature Shell](../ui/feature-shell.mdx) for how the provider bridges the two.
67
+ - `crm` -- enabled; CRM pipeline and deal management
68
+ - `lead-gen` -- enabled; prospecting, qualification, and outreach
69
+ - `projects` -- enabled; projects, milestones, and client work execution (formerly `delivery`)
70
+ - `operations` -- enabled; organizational topology and orchestration visibility
71
+ - `monitoring` -- enabled; execution monitoring
72
+ - `settings` -- enabled; organization settings
73
+ - `seo` -- disabled by default; SEO surface
78
74
 
79
- ### Default Semantic Domains
75
+ Each feature entry (`OrganizationModelFeature`) combines what were previously three separate concepts: an access/gating key (the former `OrganizationModelFeatureKey`), a semantic domain (the former `SemanticDomainSchema` entry), and display metadata. The `features` field is now `z.array(FeatureSchema)` -- there is no separate `domains` array and no separate `enabled`/`labels` map.
80
76
 
81
- Four domains ship by default -- `crm`, `lead-gen`, `delivery`, `operations`. Each binds together entity IDs, surface IDs, resource IDs, and capability IDs. Domains are semantic, not purely navigational -- they describe what area of the business or resource model a thing belongs to, not which access key gates it.
77
+ `FeatureModule.featureId` on the UI side maps directly to one of these IDs. No alias layer is needed. See [Feature Shell](../ui/feature-shell.mdx) for how the provider resolves feature access from this array.
82
78
 
83
79
  ### ResourceMapping Shape
84
80
 
@@ -88,7 +84,7 @@ Four domains ship by default -- `crm`, `lead-gen`, `delivery`, `operations`. Eac
88
84
  - `resourceId` -- the actual resource identifier (string, max 255 chars)
89
85
  - `resourceType` -- one of `'workflow' | 'agent' | 'trigger' | 'integration' | 'external' | 'human_checkpoint'`
90
86
  - `label`, `description`, `color`, `icon` -- display metadata (from `DisplayMetadataSchema`)
91
- - `domainIds` -- domains this resource belongs to
87
+ - `featureIds` -- features this resource belongs to (replaces the former `domainIds`)
92
88
  - `entityIds` -- entities this resource operates on
93
89
  - `surfaceIds` -- surfaces this resource is exposed in
94
90
  - `capabilityIds` -- capabilities this resource fulfills
@@ -109,9 +105,9 @@ Surfaces such as `crm.pipeline`, `lead-gen.lists`, `projects.index`, `operations
109
105
  - `surfaceType` -- `'page' | 'dashboard' | 'graph' | 'detail' | 'list' | 'settings'`
110
106
  - `description` -- optional
111
107
  - `icon` -- optional icon name token (max 80 chars)
112
- - `featureKey` -- optional `OrganizationModelFeatureKey` that gates this surface
108
+ - `featureId` -- optional feature ID that gates this surface (replaces the former `featureKey` field); must match a feature `id` in the features array
113
109
  - `parentId` -- optional ModelId referencing a parent surface; validated to exist
114
- - `domainIds` -- domains this surface belongs to (bidirectionally validated)
110
+ - `featureIds` -- features this surface belongs to (bidirectionally validated; replaces the former `domainIds`)
115
111
  - `entityIds` -- entity IDs relevant to this surface
116
112
  - `resourceIds` -- resources exposed on this surface (bidirectionally validated against resource mappings)
117
113
  - `capabilityIds` -- capabilities this surface fulfills
@@ -126,11 +122,13 @@ Surfaces such as `crm.pipeline`, `lead-gen.lists`, `projects.index`, `operations
126
122
 
127
123
  The default groups (`primary-workspace`, `primary-operations`) both use `placement: 'primary'`.
128
124
 
129
- ### Domain-Specific Semantics
125
+ ### Feature-Specific Semantics
130
126
 
131
- - **CRM** -- pipeline stages and stage semantics
132
- - **Lead-gen** -- company and contact lifecycle stages
133
- - **Delivery** -- project, milestone, and task statuses
127
+ The top-level `crm`, `leadGen`, and `delivery` fields on `OrganizationModel` remain as named fields (not moved into per-feature config) and carry domain-specific semantic shapes:
128
+
129
+ - `crm` -- pipeline stages and stage semantics
130
+ - `leadGen` -- company and contact lifecycle stages
131
+ - `delivery` -- project, milestone, and task statuses (used by the `projects` feature)
134
132
 
135
133
  This is why the organization model is semantic, not just nav config -- it owns product meaning for the business objects the shell surfaces expose.
136
134
 
@@ -152,7 +150,7 @@ Merge semantics:
152
150
 
153
151
  **Uniqueness checks** -- IDs must be unique within their respective collections:
154
152
 
155
- - `domains[].id`
153
+ - `features[].id`
156
154
  - `navigation.surfaces[].id`
157
155
  - `navigation.groups[].id`
158
156
  - `resourceMappings[].id`
@@ -162,20 +160,20 @@ Merge semantics:
162
160
 
163
161
  - `navigation.defaultSurfaceId` must point at a declared surface
164
162
  - every `surfaceId` in a navigation group must resolve to a declared surface
165
- - every `surfaceId` in a domain must resolve to a declared surface
166
- - every `resourceId` in a domain must resolve to a declared resource mapping (by `resourceId`)
163
+ - every `surfaceId` in a feature must resolve to a declared surface
164
+ - every `resourceId` in a feature must resolve to a declared resource mapping (by `resourceId`)
167
165
  - surface `parentId` must reference an existing surface
168
- - every `domainId` on a surface must resolve to a declared domain
166
+ - every `featureId` on a surface must resolve to a declared feature
169
167
  - every `resourceId` on a surface must resolve to a declared resource mapping
170
- - every `domainId` on a resource mapping must resolve to a declared domain
168
+ - every `featureId` on a resource mapping must resolve to a declared feature
171
169
  - every `surfaceId` on a resource mapping must resolve to a declared surface
172
170
 
173
171
  **Bidirectional reference enforcement** -- cross-references must be mutual, not one-sided:
174
172
 
175
- - a domain listing `surfaceId` S requires surface S to list that domain's ID in its `domainIds`
176
- - a surface listing `domainId` D requires domain D to list that surface's ID in its `surfaceIds`
177
- - a domain listing `resourceId` R requires resource mapping R to list that domain's ID in its `domainIds`
178
- - a resource mapping listing `domainId` D requires domain D to list that resource's `resourceId` in its `resourceIds`
173
+ - a feature listing `surfaceId` S requires surface S to list that feature's ID in its `featureIds`
174
+ - a surface listing `featureId` F requires feature F to list that surface's ID in its `surfaceIds`
175
+ - a feature listing `resourceId` R requires resource mapping R to list that feature's ID in its `featureIds`
176
+ - a resource mapping listing `featureId` F requires feature F to list that resource's `resourceId` in its `resourceIds`
179
177
  - a surface listing `resourceId` R requires resource mapping R to list that surface's ID in its `surfaceIds`
180
178
  - a resource mapping listing `surfaceId` S requires surface S to list that resource's `resourceId` in its `resourceIds`
181
179
 
@@ -185,8 +183,8 @@ This bidirectional enforcement is what keeps the organization model semantically
185
183
 
186
184
  `ElevasisFeaturesProvider` uses the organization model in three ways:
187
185
 
188
- 1. **Feature resolution** -- provider first checks `useFeatureAccess()`, then refines through organization-model feature state when the model knows the key.
189
- 2. **Nav label and path resolution** -- when `organizationModel` is present, feature labels resolve from `organizationModel.features.labels`, and surface labels and paths resolve from `organizationModel.navigation.surfaces`. Semantic customization without changing manifest code.
186
+ 1. **Feature resolution** -- the provider looks up each manifest's `featureId` in `organizationModel.features` to determine `access.enabled`. If no matching feature entry is found, `access.enabled` defaults to `false`.
187
+ 2. **Nav label and path resolution** -- when `organizationModel` is present, feature labels resolve from the matching feature entry's `label` field, and surface labels and paths resolve from `organizationModel.navigation.surfaces`. Semantic customization without changing manifest code.
190
188
  3. **Organization-graph bridge** -- the `operationsManifest` declares `organizationGraph.surfaceId = 'operations.organization-graph'`. The provider resolves that against organization-model surfaces and exposes the result as `organizationGraph` in context. See [Organization Graph](./organization-graph.mdx).
191
189
 
192
190
  ## Published Package: `@elevasis/core`
@@ -238,7 +236,7 @@ Exports the foundations module provides to consumers:
238
236
  - `FoundationFeatureKey`, `FoundationSurfaceIcon`, `FoundationNavigationSurface`, `FoundationOrganizationModel`
239
237
  - `homeLabel`, `quickAccessSurfaceIds`, `getOrganizationSurface(surfaceId)`
240
238
 
241
- Downstream template shells pass `canonicalOrganizationModel` into `ElevasisFeaturesProvider`, preserving host-local dashboard/nav customizations alongside the shared runtime. Grouped core features map onto template vocabulary -- `crm` and `lead-gen` project from `acquisition`, `projects` from `delivery`.
239
+ Downstream template shells pass `canonicalOrganizationModel` into `ElevasisFeaturesProvider`, preserving host-local dashboard/nav customizations alongside the shared runtime. Feature IDs are now direct matches -- `crm`, `lead-gen`, `projects` in the org model correspond directly to the same IDs on `FeatureModule.featureId`. No alias layer is needed.
242
240
 
243
241
  Derivative projects (`external/nirvana-marketing`, `external/ZentaraHQ`) follow the same adapter + provider-wiring baseline with their own project-local customizations.
244
242
 
@@ -1,6 +1,9 @@
1
1
  export { OrganizationModelSchema } from './schema'
2
+ export { FeatureSchema } from './domains/features'
3
+ export { PROJECTS_FEATURE_ID, PROJECTS_INDEX_SURFACE_ID, DELIVERY_PROJECTS_VIEW_CAPABILITY_ID } from './contracts'
2
4
  export { DEFAULT_ORGANIZATION_MODEL } from './defaults'
3
5
  export { defineOrganizationModel, resolveOrganizationModel } from './resolve'
6
+ export { createFoundationOrganizationModel } from './foundation'
4
7
 
5
8
  export type {
6
9
  DeepPartial,
@@ -8,11 +11,17 @@ export type {
8
11
  OrganizationModelBranding,
9
12
  OrganizationModelCrm,
10
13
  OrganizationModelDelivery,
11
- OrganizationModelFeatureKey,
12
- OrganizationModelFeatures,
14
+ OrganizationModelFeature,
13
15
  OrganizationModelLeadGen,
14
16
  OrganizationModelNavigation,
15
17
  OrganizationModelResourceMapping,
16
- OrganizationModelSemanticDomain,
17
18
  OrganizationModelSurface
18
19
  } from './types'
20
+
21
+ export type {
22
+ FoundationBranding,
23
+ FoundationNavigationSurface,
24
+ FoundationOrganizationModel,
25
+ FoundationSurfaceIcon,
26
+ FoundationSurfaceType
27
+ } from './foundation'