@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
@@ -2,16 +2,15 @@ import { z } from 'zod'
2
2
  import { OrganizationModelBrandingSchema } from './domains/branding'
3
3
  import { OrganizationModelCrmSchema } from './domains/crm'
4
4
  import { OrganizationModelDeliverySchema } from './domains/delivery'
5
- import { OrganizationModelFeaturesSchema } from './domains/features'
5
+ import { FeatureSchema } from './domains/features'
6
6
  import { OrganizationModelLeadGenSchema } from './domains/lead-gen'
7
7
  import { OrganizationModelNavigationSchema } from './domains/navigation'
8
- import { ResourceMappingSchema, SemanticDomainSchema } from './domains/shared'
8
+ import { ResourceMappingSchema } from './domains/shared'
9
9
 
10
10
  const OrganizationModelSchemaBase = z.object({
11
11
  version: z.literal(1).default(1),
12
- domains: z.array(SemanticDomainSchema).default([]),
12
+ features: z.array(FeatureSchema).default([]),
13
13
  branding: OrganizationModelBrandingSchema,
14
- features: OrganizationModelFeaturesSchema,
15
14
  navigation: OrganizationModelNavigationSchema,
16
15
  crm: OrganizationModelCrmSchema,
17
16
  leadGen: OrganizationModelLeadGenSchema,
@@ -48,7 +47,7 @@ function collectIds<T extends { id: string }>(
48
47
  }
49
48
 
50
49
  export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((model, ctx) => {
51
- const domainsById = collectIds(model.domains, ctx, ['domains'], 'Domain')
50
+ const featuresById = collectIds(model.features, ctx, ['features'], 'Feature')
52
51
  const surfacesById = collectIds(model.navigation.surfaces, ctx, ['navigation', 'surfaces'], 'Surface')
53
52
  collectIds(model.navigation.groups, ctx, ['navigation', 'groups'], 'Navigation group')
54
53
  collectIds(model.resourceMappings, ctx, ['resourceMappings'], 'Resource mapping')
@@ -87,48 +86,51 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
87
86
  })
88
87
  })
89
88
 
90
- model.domains.forEach((domain, domainIndex) => {
91
- domain.surfaceIds.forEach((surfaceId, surfaceIndex) => {
89
+ // Feature -> Surface bidirectional validation
90
+ model.features.forEach((feature, featureIndex) => {
91
+ feature.surfaceIds.forEach((surfaceId, surfaceIndex) => {
92
92
  const surface = surfacesById.get(surfaceId)
93
93
  if (!surface) {
94
94
  addIssue(
95
95
  ctx,
96
- ['domains', domainIndex, 'surfaceIds', surfaceIndex],
97
- `Domain "${domain.id}" references unknown surface "${surfaceId}"`
96
+ ['features', featureIndex, 'surfaceIds', surfaceIndex],
97
+ `Feature "${feature.id}" references unknown surface "${surfaceId}"`
98
98
  )
99
99
  return
100
100
  }
101
101
 
102
- if (!surface.domainIds.includes(domain.id)) {
102
+ if (!surface.featureIds.includes(feature.id)) {
103
103
  addIssue(
104
104
  ctx,
105
- ['domains', domainIndex, 'surfaceIds', surfaceIndex],
106
- `Domain "${domain.id}" references surface "${surfaceId}" but that surface does not include domain "${domain.id}"`
105
+ ['features', featureIndex, 'surfaceIds', surfaceIndex],
106
+ `Feature "${feature.id}" references surface "${surfaceId}" but that surface does not include feature "${feature.id}"`
107
107
  )
108
108
  }
109
109
  })
110
110
 
111
- domain.resourceIds.forEach((resourceId, resourceIndex) => {
111
+ // Feature -> Resource bidirectional validation
112
+ feature.resourceIds.forEach((resourceId, resourceIndex) => {
112
113
  const resourceMapping = resourceMappingsByResourceId.get(resourceId)
113
114
  if (!resourceMapping) {
114
115
  addIssue(
115
116
  ctx,
116
- ['domains', domainIndex, 'resourceIds', resourceIndex],
117
- `Domain "${domain.id}" references unknown resource "${resourceId}"`
117
+ ['features', featureIndex, 'resourceIds', resourceIndex],
118
+ `Feature "${feature.id}" references unknown resource "${resourceId}"`
118
119
  )
119
120
  return
120
121
  }
121
122
 
122
- if (!resourceMapping.domainIds.includes(domain.id)) {
123
+ if (!resourceMapping.featureIds.includes(feature.id)) {
123
124
  addIssue(
124
125
  ctx,
125
- ['domains', domainIndex, 'resourceIds', resourceIndex],
126
- `Domain "${domain.id}" references resource "${resourceId}" but that resource mapping does not include domain "${domain.id}"`
126
+ ['features', featureIndex, 'resourceIds', resourceIndex],
127
+ `Feature "${feature.id}" references resource "${resourceId}" but that resource mapping does not include feature "${feature.id}"`
127
128
  )
128
129
  }
129
130
  })
130
131
  })
131
132
 
133
+ // Surface -> Feature bidirectional validation and other surface refs
132
134
  model.navigation.surfaces.forEach((surface, surfaceIndex) => {
133
135
  if (surface.parentId && !surfacesById.has(surface.parentId)) {
134
136
  addIssue(
@@ -138,26 +140,27 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
138
140
  )
139
141
  }
140
142
 
141
- surface.domainIds.forEach((domainId, domainIndex) => {
142
- const domain = domainsById.get(domainId)
143
- if (!domain) {
143
+ surface.featureIds.forEach((featureId, featureIndex) => {
144
+ const feature = featuresById.get(featureId)
145
+ if (!feature) {
144
146
  addIssue(
145
147
  ctx,
146
- ['navigation', 'surfaces', surfaceIndex, 'domainIds', domainIndex],
147
- `Surface "${surface.id}" references unknown domain "${domainId}"`
148
+ ['navigation', 'surfaces', surfaceIndex, 'featureIds', featureIndex],
149
+ `Surface "${surface.id}" references unknown feature "${featureId}"`
148
150
  )
149
151
  return
150
152
  }
151
153
 
152
- if (!domain.surfaceIds.includes(surface.id)) {
154
+ if (!feature.surfaceIds.includes(surface.id)) {
153
155
  addIssue(
154
156
  ctx,
155
- ['navigation', 'surfaces', surfaceIndex, 'domainIds', domainIndex],
156
- `Surface "${surface.id}" references domain "${domainId}" but that domain does not include surface "${surface.id}"`
157
+ ['navigation', 'surfaces', surfaceIndex, 'featureIds', featureIndex],
158
+ `Surface "${surface.id}" references feature "${featureId}" but that feature does not include surface "${surface.id}"`
157
159
  )
158
160
  }
159
161
  })
160
162
 
163
+ // Surface -> Resource bidirectional validation
161
164
  surface.resourceIds.forEach((resourceId, resourceIndex) => {
162
165
  const resourceMapping = resourceMappingsByResourceId.get(resourceId)
163
166
  if (!resourceMapping) {
@@ -179,23 +182,24 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
179
182
  })
180
183
  })
181
184
 
185
+ // ResourceMapping -> Feature and Surface bidirectional validation
182
186
  model.resourceMappings.forEach((resourceMapping, resourceIndex) => {
183
- resourceMapping.domainIds.forEach((domainId, domainIndex) => {
184
- const domain = domainsById.get(domainId)
185
- if (!domain) {
187
+ resourceMapping.featureIds.forEach((featureId, featureIndex) => {
188
+ const feature = featuresById.get(featureId)
189
+ if (!feature) {
186
190
  addIssue(
187
191
  ctx,
188
- ['resourceMappings', resourceIndex, 'domainIds', domainIndex],
189
- `Resource mapping "${resourceMapping.id}" references unknown domain "${domainId}"`
192
+ ['resourceMappings', resourceIndex, 'featureIds', featureIndex],
193
+ `Resource mapping "${resourceMapping.id}" references unknown feature "${featureId}"`
190
194
  )
191
195
  return
192
196
  }
193
197
 
194
- if (!domain.resourceIds.includes(resourceMapping.resourceId)) {
198
+ if (!feature.resourceIds.includes(resourceMapping.resourceId)) {
195
199
  addIssue(
196
200
  ctx,
197
- ['resourceMappings', resourceIndex, 'domainIds', domainIndex],
198
- `Resource mapping "${resourceMapping.id}" references domain "${domainId}" but that domain does not include resource "${resourceMapping.resourceId}"`
201
+ ['resourceMappings', resourceIndex, 'featureIds', featureIndex],
202
+ `Resource mapping "${resourceMapping.id}" references feature "${featureId}" but that feature does not include resource "${resourceMapping.resourceId}"`
199
203
  )
200
204
  }
201
205
  })
@@ -2,10 +2,10 @@ import type { z } from 'zod'
2
2
  import { OrganizationModelBrandingSchema } from './domains/branding'
3
3
  import { OrganizationModelCrmSchema } from './domains/crm'
4
4
  import { OrganizationModelDeliverySchema } from './domains/delivery'
5
- import { OrganizationModelFeaturesSchema, FeatureKeySchema } from './domains/features'
5
+ import { FeatureSchema } from './domains/features'
6
6
  import { OrganizationModelLeadGenSchema } from './domains/lead-gen'
7
7
  import { OrganizationModelNavigationSchema, SurfaceDefinitionSchema } from './domains/navigation'
8
- import { ResourceMappingSchema, SemanticDomainSchema } from './domains/shared'
8
+ import { ResourceMappingSchema } from './domains/shared'
9
9
  import { OrganizationModelSchema } from './schema'
10
10
 
11
11
  export type OrganizationModel = z.infer<typeof OrganizationModelSchema>
@@ -13,15 +13,10 @@ export type OrganizationModelBranding = z.infer<typeof OrganizationModelBranding
13
13
  export type OrganizationModelCrm = z.infer<typeof OrganizationModelCrmSchema>
14
14
  export type OrganizationModelLeadGen = z.infer<typeof OrganizationModelLeadGenSchema>
15
15
  export type OrganizationModelDelivery = z.infer<typeof OrganizationModelDeliverySchema>
16
- export type OrganizationModelFeatures = z.infer<typeof OrganizationModelFeaturesSchema>
17
- export type OrganizationModelFeatureKey = z.infer<typeof FeatureKeySchema>
16
+ export type OrganizationModelFeature = z.infer<typeof FeatureSchema>
18
17
  export type OrganizationModelNavigation = z.infer<typeof OrganizationModelNavigationSchema>
19
18
  export type OrganizationModelSurface = z.infer<typeof SurfaceDefinitionSchema>
20
- export type OrganizationModelSemanticDomain = z.infer<typeof SemanticDomainSchema>
21
19
  export type OrganizationModelResourceMapping = z.infer<typeof ResourceMappingSchema>
22
20
 
23
- export type DeepPartial<T> = T extends Array<infer U>
24
- ? Array<DeepPartial<U>>
25
- : T extends object
26
- ? { [K in keyof T]?: DeepPartial<T[K]> }
27
- : T
21
+ export type DeepPartial<T> =
22
+ T extends Array<infer U> ? Array<DeepPartial<U>> : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T
@@ -1,3 +1,3 @@
1
1
  export const VERSION = {
2
- CURRENT: '1.5.7'
2
+ CURRENT: '1.6.0'
3
3
  }
@@ -14,54 +14,21 @@ import type {
14
14
  ExecutionCompleteEvent,
15
15
  ExecutionConnectedEvent
16
16
  } from '../../execution/core/sse-executions'
17
- import type {
18
- CalibrationSSEEvent,
19
- CalibrationExecutionStartedEvent,
20
- CalibrationExecutionCompletedEvent,
21
- CalibrationExecutionFailedEvent,
22
- CalibrationSessionStartedEvent,
23
- CalibrationTurnStartedEvent,
24
- CalibrationTurnCompletedEvent,
25
- CalibrationSessionCompletedEvent,
26
- CalibrationGradingStartedEvent,
27
- CalibrationGradingCompletedEvent,
28
- CalibrationGradingFailedEvent,
29
- CalibrationCompletedEvent,
30
- CalibrationFailedEvent,
31
- CalibrationConnectedEvent
32
- } from '../../execution/calibration/sse-events'
33
17
 
34
18
  // Re-export domain-specific event types
35
19
  export type { NotificationSSEEvent, NotificationCountUpdatedEvent }
36
20
  export type { ActivitySSEEvent, ActivityCreatedEvent, ActivityConnectedEvent }
37
21
  export type { CommandQueueSSEEvent, CommandQueueTaskUpdatedEvent, CommandQueueConnectedEvent }
38
22
  export type { ExecutionSSEEvent, ExecutionStartedEvent, ExecutionLogEvent, ExecutionCompleteEvent, ExecutionConnectedEvent }
39
- export type {
40
- CalibrationSSEEvent,
41
- CalibrationExecutionStartedEvent,
42
- CalibrationExecutionCompletedEvent,
43
- CalibrationExecutionFailedEvent,
44
- CalibrationSessionStartedEvent,
45
- CalibrationTurnStartedEvent,
46
- CalibrationTurnCompletedEvent,
47
- CalibrationSessionCompletedEvent,
48
- CalibrationGradingStartedEvent,
49
- CalibrationGradingCompletedEvent,
50
- CalibrationGradingFailedEvent,
51
- CalibrationCompletedEvent,
52
- CalibrationFailedEvent,
53
- CalibrationConnectedEvent
54
- }
55
23
 
56
24
  /**
57
25
  * Union of all application-level SSE events across all domains
58
26
  *
59
27
  * Use this for generic SSE handling that needs to support all event types.
60
- * For domain-specific handling, use NotificationSSEEvent, ActivitySSEEvent, ExecutionSSEEvent, CalibrationSSEEvent, etc.
28
+ * For domain-specific handling, use NotificationSSEEvent, ActivitySSEEvent, ExecutionSSEEvent, etc.
61
29
  */
62
30
  export type AppSSEEvent =
63
31
  | NotificationSSEEvent
64
32
  | ActivitySSEEvent
65
33
  | CommandQueueSSEEvent
66
34
  | ExecutionSSEEvent
67
- | CalibrationSSEEvent
@@ -102,7 +102,8 @@ export const UpdateProjectRequestSchema = z
102
102
  export const GetProjectsQuerySchema = z
103
103
  .object({
104
104
  kind: ProjectKindSchema.optional(),
105
- status: ProjectStatusSchema.optional()
105
+ status: ProjectStatusSchema.optional(),
106
+ search: z.string().trim().min(1).max(255).optional()
106
107
  })
107
108
  .strict()
108
109
 
@@ -1,3 +1,4 @@
1
+ <!-- Auto-generated on 2026-04-18T10:41:03.508Z by scripts/monorepo/generate-scaffold-contracts.js -->
1
2
  ---
2
3
  title: Reference Contracts
3
4
  description: Auto-generated TypeScript contracts for SDK consumers. Do not edit manually.
@@ -37,16 +38,10 @@ export type OrganizationModelLeadGen = z.infer<typeof OrganizationModelLeadGenSc
37
38
  export type OrganizationModelDelivery = z.infer<typeof OrganizationModelDeliverySchema>
38
39
  ```
39
40
 
40
- ### `OrganizationModelFeatures`
41
+ ### `OrganizationModelFeature`
41
42
 
42
43
  ```typescript
43
- export type OrganizationModelFeatures = z.infer<typeof OrganizationModelFeaturesSchema>
44
- ```
45
-
46
- ### `OrganizationModelFeatureKey`
47
-
48
- ```typescript
49
- export type OrganizationModelFeatureKey = z.infer<typeof FeatureKeySchema>
44
+ export type OrganizationModelFeature = z.infer<typeof FeatureSchema>
50
45
  ```
51
46
 
52
47
  ### `OrganizationModelNavigation`
@@ -61,12 +56,6 @@ export type OrganizationModelNavigation = z.infer<typeof OrganizationModelNaviga
61
56
  export type OrganizationModelSurface = z.infer<typeof SurfaceDefinitionSchema>
62
57
  ```
63
58
 
64
- ### `OrganizationModelSemanticDomain`
65
-
66
- ```typescript
67
- export type OrganizationModelSemanticDomain = z.infer<typeof SemanticDomainSchema>
68
- ```
69
-
70
59
  ### `OrganizationModelResourceMapping`
71
60
 
72
61
  ```typescript
@@ -76,11 +65,7 @@ export type OrganizationModelResourceMapping = z.infer<typeof ResourceMappingSch
76
65
  ### `DeepPartial`
77
66
 
78
67
  ```typescript
79
- export type DeepPartial<T> = T extends Array<infer U>
80
- ? Array<DeepPartial<U>>
81
- : T extends object
82
- ? { [K in keyof T]?: DeepPartial<T[K]> }
83
- : T
68
+ export type DeepPartial<T> = T extends Array<infer U> ? Array<DeepPartial<U>> : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T
84
69
  ```
85
70
 
86
71
  ## Feature System
@@ -121,15 +106,10 @@ export type FeatureSidebarComponent = ComponentType
121
106
 
122
107
  ```typescript
123
108
  export interface FeatureModule {
124
- /** Unique stable identifier for this feature (e.g. `'crm'`, `'delivery'`). */
109
+ /** Unique stable identifier for this feature (e.g. `'crm'`, `'projects'`). */
125
110
  key: string
126
- /** Feature key used for access-flag gating in the organization model. */
127
- accessFeatureKey: OrganizationModelFeatureKey
128
- /**
129
- * Semantic domain identifiers contributed by this feature.
130
- * Merged with surface-derived domain IDs during resolution.
131
- */
132
- domainIds?: OrganizationModelSemanticDomain['id'][]
111
+ /** Feature ID used for access-flag gating — must match the `id` of a feature in the organization model. */
112
+ featureId: string
133
113
  /**
134
114
  * Capability identifiers contributed by this feature.
135
115
  * Merged into `ResolvedFeatureSemantics.capabilityIds` at resolution time.
@@ -156,7 +136,7 @@ export interface FeatureModule {
156
136
 
157
137
  ```typescript
158
138
  export interface ResolvedFeatureAccess {
159
- featureKey: string
139
+ featureId: string
160
140
  enabled: boolean
161
141
  }
162
142
  ```
@@ -165,7 +145,6 @@ export interface ResolvedFeatureAccess {
165
145
 
166
146
  ```typescript
167
147
  export interface ResolvedFeatureSemantics {
168
- domainIds: OrganizationModelSemanticDomain['id'][]
169
148
  capabilityIds: string[]
170
149
  surfaceIds: string[]
171
150
  surfaces: OrganizationModelSurface[]
@@ -199,7 +178,7 @@ export type ShellNavSource = 'app' | 'feature'
199
178
  export interface ResolvedShellNavItem extends FeatureNavEntry {
200
179
  placement: ShellNavPlacement
201
180
  source: ShellNavSource
202
- accessFeatureKey?: string
181
+ featureId?: string
203
182
  }
204
183
  ```
205
184
 
@@ -262,7 +241,7 @@ export interface OrganizationGraphContextValue {
262
241
  surfaceId?: string
263
242
  surfacePath?: string
264
243
  surfaceType?: OrganizationModelSurface['surfaceType']
265
- featureKey?: OrganizationModelFeatureKey
244
+ featureId?: string
266
245
  }
267
246
  ```
268
247
 
@@ -13,7 +13,7 @@ This condensed version covers every ambiguity-prone term a template consumer or
13
13
 
14
14
  ## Terms
15
15
 
16
- **accessFeatureKey** -- the `OrganizationModelFeatureKey` a `FeatureModule` declares so the provider knows which org-level gate to check. Required field on every `FeatureModule`. Distinct from the nav-item `featureKey` (see below). Shell keys like `crm` and `lead-gen` map to grouped org-model keys (`acquisition`) via `FEATURE_KEY_ALIASES` -- this aliasing is automatic; callers do not need to translate manually.
16
+ **featureId** (on FeatureModule) -- the `id` string a `FeatureModule` declares so the provider knows which entry in `OrganizationModel.features` to check for access gating. Required field on every `FeatureModule`. Must match the `id` of an entry in the features array (e.g. `'crm'`, `'projects'`). Distinct from the nav-item `featureKey` (see below). No aliasing layer -- IDs are direct matches.
17
17
 
18
18
  **AdminGuard** -- route-level admin wrapper from `@elevasis/ui/features/auth`. Wraps routes restricted to admin members. Must nest inside `ProtectedRoute`. Does not replace `requiresAdmin` on nav entries -- use both when both route access and nav visibility need admin enforcement.
19
19
 
@@ -21,45 +21,41 @@ This condensed version covers every ambiguity-prone term a template consumer or
21
21
 
22
22
  **DeploymentSpec** -- the complete resource collection for one organization (`workflows`, `agents`, `triggers`, `integrations`, `relationships`, `externalResources`, `humanCheckpoints`). Defined in `@repo/core`. The `operations/src/index.ts` file in each external project exports one `DeploymentSpec`.
23
23
 
24
- **Domain** -- a semantic business area in `OrganizationModel.domains`. Four defaults: `crm`, `lead-gen`, `delivery`, `operations`. Domains group entity IDs, surface IDs, resource IDs, and capability IDs into a coherent business area. Semantic, not navigational -- describes what business area a thing belongs to, not which access key gates it. Distinct from "feature": a domain describes business meaning; a feature key describes access and enablement.
24
+ **Domain** -- removed concept. What was previously a `SemanticDomain` entry in `OrganizationModel.domains` is now expressed directly as a **Feature** in the `features` array. The `domains` field no longer exists on `OrganizationModel`. Semantic grouping (entity IDs, surface IDs, resource IDs, capability IDs) now lives on each `OrganizationModelFeature` object.
25
25
 
26
- **FEATURE_KEY_ALIASES** -- the alias map in `@elevasis/ui` that bridges shell feature-module keys to org-model grouped keys: `crm -> acquisition`, `lead-gen -> acquisition`, `projects -> delivery`. Used internally by `createFeatureAccessHook` so `isFeatureEnabled('crm')` resolves correctly against `MembershipFeatureConfig.features.acquisition`.
27
-
28
- **FoundationLegacyFeatureKey** -- template-local union for feature keys defined in `foundations/config/organization-model.ts`. Distinct from the published `OrganizationModelFeatureKey` (seven-key union from `@elevasis/core`). Extension point for template-specific feature identity without modifying the published core union. `FoundationFeatureKey` combines both (`OrganizationModelFeatureKey | FoundationLegacyFeatureKey`). `FEATURE_KEY_ALIASES` maps between shell keys and org-model grouped keys at runtime.
29
-
30
- **Feature** -- an overloaded term. Always qualify which layer is in scope:
26
+ **Feature** -- the unified concept replacing the former three-layer system (feature keys, semantic domains, feature modules). Always qualify which layer is in scope:
31
27
 
32
28
  - **Platform capability** -- a product area (Execution Engine, Workflows, Agents, etc.). Not one-to-one with shell features.
33
- - **Shell FeatureModule** -- a manifest-backed UI feature registered with `ElevasisFeaturesProvider`. Seven manifest-backed: `lead-gen`, `crm`, `delivery`, `operations`, `monitoring`, `settings`, `seo`. Two utility (no manifest): `auth`, `dashboard`.
34
- - **Organization-model feature key** (`OrganizationModelFeatureKey`) -- grouped access key. Seven values: `acquisition`, `delivery`, `operations`, `monitoring`, `settings`, `seo`, `calibration`. Controls org-level gating and membership overrides.
29
+ - **Organization-model feature** (`OrganizationModelFeature`) -- a single entry in `OrganizationModel.features`. Combines access gating (`enabled`), semantic grouping (`entityIds`, `surfaceIds`, `resourceIds`, `capabilityIds`), and display metadata (`label`, `description`, `color`, `icon`). Seven defaults: `crm`, `lead-gen`, `projects`, `operations`, `monitoring`, `settings`, `seo`.
30
+ - **Shell FeatureModule** -- a manifest-backed UI feature registered with `ElevasisFeaturesProvider`. Declares `featureId` matching an org-model feature ID. Seven manifest-backed: `lead-gen`, `crm`, `projects`, `operations`, `monitoring`, `settings`, `seo`. Two utility (no manifest): `auth`, `dashboard`.
35
31
 
36
- **featureKey** (nav-item) -- optional field on `FeatureNavEntry` and `FeatureNavLink` that gates a specific nav item's visibility independently of the module's `accessFeatureKey`. Resolved through `FEATURE_KEY_ALIASES`. May be more specific than the module's access key when a nested link needs a narrower gate.
32
+ **featureKey** (nav-item) -- optional field on `FeatureNavEntry` and `FeatureNavLink` that gates a specific nav item's visibility independently of the module's `featureId`. Must match a feature ID in the org model. May be more specific than the module's `featureId` when a nested link needs a narrower gate.
37
33
 
38
34
  **FeatureGuard** -- route-level feature gate from `@elevasis/ui/features/auth`. Blocks access to a route when the resolved org model has the relevant feature key disabled. Must nest inside `ProtectedRoute`. Distinct from `AdminGuard` (admin membership) and `ProtectedRoute` (authentication).
39
35
 
40
- **FeatureModule** -- the manifest contract each shell feature provides to `ElevasisFeaturesProvider`. Defined in `@repo/ui`, NOT `@repo/core`. Fields: `key`, `accessFeatureKey` (required), `domainIds`, `capabilityIds`, `navEntry`, `sidebar`, `subshellRoutes`, `organizationGraph` (Operations-only). Template consumers build a local manifest array and pass it to `ElevasisFeaturesProvider` in `__root.tsx`.
36
+ **FeatureModule** -- the manifest contract each shell feature provides to `ElevasisFeaturesProvider`. Defined in `@repo/ui`, NOT `@repo/core`. Fields: `key`, `featureId` (required -- replaces the former `accessFeatureKey`), `capabilityIds`, `navEntry`, `sidebar`, `subshellRoutes`, `organizationGraph` (Operations-only). Template consumers build a local manifest array and pass it to `ElevasisFeaturesProvider` in `__root.tsx`.
41
37
 
42
38
  **Foundations** -- the adapter layer in `foundations/` (two modules: `config/organization-model.ts` and `types/index.ts`). Source package with no build step. Depends only on `@elevasis/core` (npm) and `zod`. Never import `@repo/core` from foundations -- that would break standalone deployment.
43
39
 
44
40
  **Manifest** -- a `FeatureModule` instance that declares what one shell feature contributes at runtime (nav, sidebar, subshell routes, access key, semantic references). The provider registers an array of manifests at startup and validates them against the resolved org model.
45
41
 
46
- **MembershipFeatureConfig** -- per-member-per-org feature overrides stored in `org_memberships.config`. Six keys: `operations`, `monitoring`, `acquisition`, `delivery`, `calibration`, `seo`. Note: `settings` is absent -- see **Settings asymmetry** below.
42
+ **MembershipFeatureConfig** -- per-member-per-org feature overrides stored in `org_memberships.config`. The `features` field is now `Record<string, boolean>` -- any feature ID can be overridden per member. Previously hardcoded to five keys (`operations`, `monitoring`, `acquisition`, `delivery`, `seo`); now open to any feature ID in the org model.
47
43
 
48
44
  **OrganizationModel** -- the top-level semantic contract for an organization. Published from `@elevasis/core/organization-model`. In the template, authored in `foundations/config/organization-model.ts` and exported as `canonicalOrganizationModel` (passed to `ElevasisFeaturesProvider`) and `organizationModel` (enriched shape for app-local use).
49
45
 
50
- **OrganizationModelFeatureKey** -- seven values: `acquisition`, `delivery`, `operations`, `monitoring`, `settings`, `seo`, `calibration`. These appear in `OrganizationModel.features.enabled` and as `accessFeatureKey` on `FeatureModule` instances. `MembershipFeatureConfig` overrides six of them per member (no `settings` slot -- see **Settings asymmetry**).
46
+ **OrganizationModelFeature** -- the TypeScript type (`z.infer<typeof FeatureSchema>`) for a single entry in `OrganizationModel.features`. Replaces the former `OrganizationModelFeatureKey` (closed enum) and `OrganizationModelSemanticDomain`. Each feature has `id`, `label`, `enabled`, and semantic grouping arrays (`entityIds`, `surfaceIds`, `resourceIds`, `capabilityIds`). Exported from `@elevasis/core/organization-model`.
51
47
 
52
48
  **Provider / ElevasisFeaturesProvider** -- the runtime that registers manifests, resolves feature access against the org model, dispatches subshell routing via `FeatureShell`, and exposes resolved state through `useElevasisFeatures()`. Mounted in `__root.tsx`. Accepts `features`, optional `organizationModel`, and optional `appShellOverrides`.
53
49
 
54
- **Resolved types (ResolvedFeatureModule, ResolvedShellNavItem)** -- provider output. `ResolvedFeatureModule` extends `FeatureModule` with `access` (enabled state) and `semantics` (merged domain, capability, and surface IDs). `ResolvedShellNavItem` extends `FeatureNavEntry` with `placement`, `source`, and `accessFeatureKey`. Both from `@elevasis/ui`.
50
+ **Resolved types (ResolvedFeatureModule, ResolvedShellNavItem)** -- provider output. `ResolvedFeatureModule` extends `FeatureModule` with `access` (enabled state and `featureId`) and `semantics` (merged capability and surface IDs). `ResolvedShellNavItem` extends `FeatureNavEntry` with `placement`, `source`, and `featureId`. Both from `@elevasis/ui`.
55
51
 
56
52
  **Resource** -- an entry in `OrganizationModel.resourceMappings` linking a deployable automation resource into the semantic model. At the registry layer, resources are `WorkflowDefinition` or `AgentDefinition` instances in a `DeploymentSpec`.
57
53
 
58
- **Settings asymmetry** -- `settings` is a valid `OrganizationModelFeatureKey` (org-level, present in `OrganizationModel.features.enabled`). It is absent from `MembershipFeatureConfig` (six keys, no `settings`). The org can disable settings entirely, but per-member disabling is not supported. Settings visibility for individual members is controlled via `requiresAdmin` on the nav entry and `AdminGuard` on routes.
54
+ **Settings asymmetry** -- `settings` is a valid feature ID (org-level, present in `OrganizationModel.features` with `enabled: true` by default). `MembershipFeatureConfig.features` is now `Record<string, boolean>`, so `settings` can technically be overridden per member, but the convention is still to gate settings visibility via `requiresAdmin` on the nav entry and `AdminGuard` on routes rather than per-member feature overrides.
59
55
 
60
56
  **Subshell / Sidebar** -- the feature-scoped UI region rendered when the current route matches a manifest's `subshellRoutes`. Each `FeatureModule.sidebar` is a `ComponentType`. Consumers customize by composing the feature's published sidebar primitives (`CrmSidebar`, `CrmSidebarMiddle`, etc.) and assigning their component to `manifest.sidebar`.
61
57
 
62
- **Surface** -- a navigable view in `OrganizationModel.navigation.surfaces`. Identified by a dotted `id` (e.g., `crm.pipeline`). Has `path`, `surfaceType`, optional `featureKey` gate, and cross-reference arrays. Distinct from "page": a surface is the org-model declaration; a page is the React component rendered at the route.
58
+ **Surface** -- a navigable view in `OrganizationModel.navigation.surfaces`. Identified by a dotted `id` (e.g., `crm.pipeline`). Has `path`, `surfaceType`, optional `featureId` gate (replaces the former `featureKey` field), and cross-reference arrays (`featureIds`, `entityIds`, `resourceIds`, `capabilityIds`). Distinct from "page": a surface is the org-model declaration; a page is the React component rendered at the route.
63
59
 
64
60
  **Topology** -- resource relationships (`triggers`, `uses`, `approval`) declared in `DeploymentSpec.relationships` and `HumanCheckpointDefinition.routesTo`. Used for Command View graph edges.
65
61
 
@@ -69,7 +65,7 @@ This condensed version covers every ambiguity-prone term a template consumer or
69
65
 
70
66
  **`@elevasis/core`** (published npm)
71
67
 
72
- - `OrganizationModel`, `OrganizationModelFeatureKey`, `OrganizationModelSurface`, `OrganizationModelResourceMapping`
68
+ - `OrganizationModel`, `OrganizationModelFeature`, `OrganizationModelSurface`, `OrganizationModelResourceMapping`
73
69
  - `resolveOrganizationModel`, `defineOrganizationModel`, `DEFAULT_ORGANIZATION_MODEL`
74
70
  - `MembershipFeatureConfig`
75
71
 
@@ -79,7 +75,7 @@ This condensed version covers every ambiguity-prone term a template consumer or
79
75
  - `ResolvedFeatureModule`, `ResolvedShellNavItem`
80
76
  - `FeatureGuard`, `AdminGuard`, `ProtectedRoute`
81
77
  - `ElevasisFeaturesProvider`, `ElevasisCoreProvider`, `useElevasisFeatures`
82
- - `FEATURE_KEY_ALIASES`, `createFeatureAccessHook`
78
+ - `createFeatureAccessHook`
83
79
 
84
80
  **`foundations/`** (local source package -- not published)
85
81
 
@@ -1117,113 +1117,6 @@ export type Database = {
1117
1117
  },
1118
1118
  ]
1119
1119
  }
1120
- calibration_projects: {
1121
- Row: {
1122
- created_at: string | null
1123
- description: string | null
1124
- id: string
1125
- name: string
1126
- organization_id: string
1127
- resource_id: string
1128
- resource_type: string
1129
- updated_at: string | null
1130
- }
1131
- Insert: {
1132
- created_at?: string | null
1133
- description?: string | null
1134
- id?: string
1135
- name: string
1136
- organization_id: string
1137
- resource_id: string
1138
- resource_type: string
1139
- updated_at?: string | null
1140
- }
1141
- Update: {
1142
- created_at?: string | null
1143
- description?: string | null
1144
- id?: string
1145
- name?: string
1146
- organization_id?: string
1147
- resource_id?: string
1148
- resource_type?: string
1149
- updated_at?: string | null
1150
- }
1151
- Relationships: [
1152
- {
1153
- foreignKeyName: "calibration_projects_organization_id_fkey"
1154
- columns: ["organization_id"]
1155
- isOneToOne: false
1156
- referencedRelation: "organizations"
1157
- referencedColumns: ["id"]
1158
- },
1159
- ]
1160
- }
1161
- calibration_runs: {
1162
- Row: {
1163
- completed_at: string | null
1164
- config_variants: Json
1165
- created_at: string | null
1166
- description: string | null
1167
- execution_mode: string
1168
- grader_model: string | null
1169
- grading_rubric: Json | null
1170
- id: string
1171
- name: string
1172
- organization_id: string
1173
- project_id: string
1174
- results: Json
1175
- status: string
1176
- test_inputs: Json
1177
- }
1178
- Insert: {
1179
- completed_at?: string | null
1180
- config_variants: Json
1181
- created_at?: string | null
1182
- description?: string | null
1183
- execution_mode?: string
1184
- grader_model?: string | null
1185
- grading_rubric?: Json | null
1186
- id?: string
1187
- name: string
1188
- organization_id: string
1189
- project_id: string
1190
- results?: Json
1191
- status?: string
1192
- test_inputs: Json
1193
- }
1194
- Update: {
1195
- completed_at?: string | null
1196
- config_variants?: Json
1197
- created_at?: string | null
1198
- description?: string | null
1199
- execution_mode?: string
1200
- grader_model?: string | null
1201
- grading_rubric?: Json | null
1202
- id?: string
1203
- name?: string
1204
- organization_id?: string
1205
- project_id?: string
1206
- results?: Json
1207
- status?: string
1208
- test_inputs?: Json
1209
- }
1210
- Relationships: [
1211
- {
1212
- foreignKeyName: "calibration_runs_organization_id_fkey"
1213
- columns: ["organization_id"]
1214
- isOneToOne: false
1215
- referencedRelation: "organizations"
1216
- referencedColumns: ["id"]
1217
- },
1218
- {
1219
- foreignKeyName: "calibration_runs_project_id_fkey"
1220
- columns: ["project_id"]
1221
- isOneToOne: false
1222
- referencedRelation: "calibration_projects"
1223
- referencedColumns: ["id"]
1224
- },
1225
- ]
1226
- }
1227
1120
  command_queue: {
1228
1121
  Row: {
1229
1122
  action_payload: Json | null
@@ -68,8 +68,6 @@ export class RLSTestContext {
68
68
  notifications: string[]
69
69
  credentials: string[]
70
70
  activities: string[]
71
- calibration_projects: string[]
72
- calibration_runs: string[]
73
71
  }
74
72
 
75
73
  constructor() {
@@ -105,9 +103,7 @@ export class RLSTestContext {
105
103
  executionMetrics: [],
106
104
  notifications: [],
107
105
  credentials: [],
108
- activities: [],
109
- calibration_projects: [],
110
- calibration_runs: []
106
+ activities: []
111
107
  }
112
108
  }
113
109
 
@@ -396,8 +392,6 @@ export class RLSTestContext {
396
392
  * - credentials (FK: organizations, users)
397
393
  * - sessions (FK: organizations, users)
398
394
  * - activities (FK: organizations)
399
- * - calibration_runs (FK: calibration_projects, organizations)
400
- * - calibration_projects (FK: organizations)
401
395
  *
402
396
  * Level 3 (Organization/User dependencies):
403
397
  * - invitations (FK: organizations, users)
@@ -505,30 +499,6 @@ export class RLSTestContext {
505
499
  }
506
500
  }
507
501
 
508
- // Delete calibration_runs (FK: calibration_projects, organizations)
509
- if (this.createdIds.calibration_runs.length > 0) {
510
- const { error } = await this.adminClient
511
- .from('calibration_runs')
512
- .delete()
513
- .in('id', this.createdIds.calibration_runs)
514
-
515
- if (error) {
516
- errors.push(`Failed to delete calibration_runs: ${error.message}`)
517
- }
518
- }
519
-
520
- // Delete calibration_projects (FK: organizations)
521
- if (this.createdIds.calibration_projects.length > 0) {
522
- const { error } = await this.adminClient
523
- .from('calibration_projects')
524
- .delete()
525
- .in('id', this.createdIds.calibration_projects)
526
-
527
- if (error) {
528
- errors.push(`Failed to delete calibration_projects: ${error.message}`)
529
- }
530
- }
531
-
532
502
  // LEVEL 3: Delete organization/user relationship tables
533
503
 
534
504
  // Delete invitations