@elevasis/sdk 1.5.3 → 1.5.4
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/cli.cjs +899 -57
- package/dist/index.d.ts +94 -110
- package/package.json +3 -3
- package/reference/_navigation.md +11 -1
- package/reference/_reference-manifest.json +70 -0
- package/reference/claude-config/commands/submit-issue.md +12 -0
- package/reference/claude-config/hooks/post-edit-validate.mjs +109 -0
- package/reference/claude-config/hooks/tool-failure-recovery.mjs +73 -0
- package/reference/claude-config/rules/deployment.md +57 -0
- package/reference/claude-config/rules/docs.md +26 -0
- package/reference/claude-config/rules/error-handling.md +56 -0
- package/reference/claude-config/rules/execution.md +40 -0
- package/reference/claude-config/rules/frontend.md +43 -0
- package/reference/claude-config/rules/observability.md +31 -0
- package/reference/claude-config/rules/organization-os.md +62 -0
- package/reference/claude-config/rules/platform.md +41 -0
- package/reference/claude-config/rules/shared-types.md +46 -0
- package/reference/claude-config/rules/task-tracking.md +47 -0
- package/reference/claude-config/scripts/statusline-command.js +18 -0
- package/reference/claude-config/settings.json +30 -0
- package/reference/claude-config/skills/deploy/SKILL.md +166 -0
- package/reference/claude-config/skills/dsp/SKILL.md +66 -0
- package/reference/claude-config/skills/elevasis/SKILL.md +239 -0
- package/reference/claude-config/skills/explore/SKILL.md +78 -0
- package/reference/claude-config/skills/project/SKILL.md +918 -0
- package/reference/claude-config/skills/save/SKILL.md +197 -0
- package/reference/claude-config/skills/setup/SKILL.md +210 -0
- package/reference/claude-config/skills/status/SKILL.md +60 -0
- package/reference/claude-config/skills/submit-issue/SKILL.md +179 -0
- package/reference/claude-config/skills/sync/SKILL.md +81 -0
- package/reference/cli.mdx +19 -4
- package/reference/deployment/provided-features.mdx +24 -2
- package/reference/framework/agent.mdx +12 -4
- package/reference/framework/project-structure.mdx +9 -3
- package/reference/packages/core/src/README.md +1 -1
- package/reference/packages/core/src/business/README.md +52 -0
- package/reference/packages/core/src/organization-model/README.md +25 -26
- package/reference/packages/ui/src/app/README.md +24 -0
- package/reference/platform-tools/type-safety.mdx +0 -10
- package/reference/scaffold/core/organization-graph.mdx +37 -28
- package/reference/scaffold/core/organization-model.mdx +34 -36
- package/reference/scaffold/index.mdx +1 -0
- package/reference/scaffold/operations/propagation-pipeline.md +7 -3
- package/reference/scaffold/operations/scaffold-maintenance.md +2 -2
- package/reference/scaffold/operations/workflow-recipes.md +18 -1
- package/reference/scaffold/recipes/add-a-feature.md +37 -21
- package/reference/scaffold/recipes/add-a-resource.md +4 -2
- package/reference/scaffold/recipes/customize-organization-model.md +400 -0
- package/reference/scaffold/recipes/extend-a-base-entity.md +140 -0
- package/reference/scaffold/recipes/gate-by-feature-or-admin.md +18 -12
- package/reference/scaffold/recipes/index.md +3 -3
- package/reference/scaffold/reference/contracts.md +11 -32
- package/reference/scaffold/reference/feature-registry.md +10 -9
- package/reference/scaffold/reference/glossary.md +14 -18
- package/reference/scaffold/ui/customization.md +2 -2
- package/reference/scaffold/ui/feature-flags-and-gating.md +40 -54
- package/reference/scaffold/ui/feature-shell.mdx +22 -23
- package/reference/scaffold/ui/recipes.md +118 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- Auto-generated on 2026-04-19T10:24:34.890Z 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
|
-
### `
|
|
41
|
+
### `OrganizationModelFeature`
|
|
41
42
|
|
|
42
43
|
```typescript
|
|
43
|
-
export type
|
|
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'`, `'
|
|
109
|
+
/** Unique stable identifier for this feature (e.g. `'crm'`, `'projects'`). */
|
|
125
110
|
key: string
|
|
126
|
-
/** Feature
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
244
|
+
featureId?: string
|
|
266
245
|
}
|
|
267
246
|
```
|
|
268
247
|
|
|
@@ -500,7 +479,7 @@ export interface ResourceList {
|
|
|
500
479
|
|
|
501
480
|
```typescript
|
|
502
481
|
/** Webhook provider identifiers */
|
|
503
|
-
export type WebhookProviderType = 'cal-com' | 'stripe' | 'signature-api' | 'instantly' | 'apify'
|
|
482
|
+
export type WebhookProviderType = 'cal-com' | 'stripe' | 'signature-api' | 'instantly' | 'apify' | 'test'
|
|
504
483
|
|
|
505
484
|
/** Webhook trigger configuration */
|
|
506
485
|
```
|
|
@@ -7,24 +7,25 @@ description: Auto-generated catalog of registered feature manifests. Do not edit
|
|
|
7
7
|
|
|
8
8
|
## Registered Feature Manifests
|
|
9
9
|
|
|
10
|
-
| Feature Key |
|
|
10
|
+
| Feature Key | Feature ID | Nav Label | Domains | Routes |
|
|
11
11
|
| --- | --- | --- | --- | --- |
|
|
12
|
-
| `crm` | `
|
|
13
|
-
| `delivery` |
|
|
14
|
-
| `lead-gen` | `
|
|
12
|
+
| `crm` | `crm` | CRM | — | /crm |
|
|
13
|
+
| `delivery` | `—` | Projects | — | /projects |
|
|
14
|
+
| `lead-gen` | `lead-gen` | Lead Gen | — | /lead-gen |
|
|
15
15
|
| `monitoring` | `monitoring` | Monitoring | — | — |
|
|
16
|
-
| `operations` | `operations` | Operations |
|
|
16
|
+
| `operations` | `operations` | Operations | — | /operations |
|
|
17
17
|
| `seo` | `seo` | — | — | /seo |
|
|
18
18
|
| `settings` | `settings` | Settings | — | — |
|
|
19
19
|
|
|
20
20
|
> **Note:** `auth` and `dashboard` are not in the registry — they are app-shell concerns, not gated features.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Default Feature IDs
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Feature IDs defined in `DEFAULT_ORGANIZATION_MODEL.features` (`packages/core/src/organization-model/defaults.ts`):
|
|
25
25
|
|
|
26
26
|
```typescript
|
|
27
|
-
|
|
27
|
+
// FeatureSchema: { id, label, enabled, color?, icon?, entityIds, surfaceIds, resourceIds, capabilityIds }
|
|
28
|
+
type DefaultFeatureId =
|
|
28
29
|
```
|
|
29
30
|
|
|
30
|
-
These are the
|
|
31
|
+
These are the built-in feature IDs. The `featureId` field on a `FeatureModule` manifest must match the `id` of a feature in the organization model to enable access-flag gating.
|
|
@@ -13,7 +13,7 @@ This condensed version covers every ambiguity-prone term a template consumer or
|
|
|
13
13
|
|
|
14
14
|
## Terms
|
|
15
15
|
|
|
16
|
-
**
|
|
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** --
|
|
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
|
-
**
|
|
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
|
-
- **
|
|
34
|
-
- **
|
|
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 `
|
|
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`, `
|
|
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`.
|
|
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
|
-
**
|
|
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
|
|
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
|
|
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`
|
|
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`, `
|
|
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
|
-
- `
|
|
78
|
+
- `createFeatureAccessHook`
|
|
83
79
|
|
|
84
80
|
**`foundations/`** (local source package -- not published)
|
|
85
81
|
|
|
@@ -113,7 +113,7 @@ const MyCrmSidebar = () => (
|
|
|
113
113
|
const customCrmManifest = { ...crmManifest, sidebar: MyCrmSidebar }
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
`PipelineHealthWidget` is a component you define in `ui/src/features/crm/` or `ui/src/
|
|
116
|
+
`PipelineHealthWidget` is a component you define in `ui/src/features/crm/` or `ui/src/lib/components/`. Keep route files and `__root.tsx` thin -- push component logic into feature modules.
|
|
117
117
|
|
|
118
118
|
## Worked Example 3: Page Wrapping
|
|
119
119
|
|
|
@@ -127,7 +127,7 @@ The exported CRM page components are `DealsListPage` and `DealDetailPage`:
|
|
|
127
127
|
import { createFileRoute } from '@tanstack/react-router'
|
|
128
128
|
import { DealsListPage } from '@elevasis/ui/features/crm'
|
|
129
129
|
import { ProtectedRoute } from '@/features/auth'
|
|
130
|
-
import { ProjectAnnouncementBanner } from '@/
|
|
130
|
+
import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
|
|
131
131
|
import { Stack } from '@mantine/core'
|
|
132
132
|
|
|
133
133
|
export const Route = createFileRoute('/crm/deals/')({
|
|
@@ -8,8 +8,8 @@ description: End-to-end recipe for the three gating concepts -- featureKey nav v
|
|
|
8
8
|
**Status: 🟢 Stable**
|
|
9
9
|
|
|
10
10
|
This doc resolves the three-concept confusion that trips up most agents building new features. Read
|
|
11
|
-
the overview table first, then follow the end-to-end chain for the template's
|
|
12
|
-
|
|
11
|
+
the overview table first, then follow the end-to-end chain for the template's unified feature ID
|
|
12
|
+
flow.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -29,47 +29,32 @@ The three concepts work together but are independent. Hiding a nav item does not
|
|
|
29
29
|
|
|
30
30
|
## End-to-End Chain
|
|
31
31
|
|
|
32
|
-
This section walks through the template's
|
|
33
|
-
|
|
32
|
+
This section walks through the template's feature gating flow. Features are declared as objects in
|
|
33
|
+
the org model with a direct `id` field. `FeatureModule.featureId` matches that `id` exactly -- no
|
|
34
|
+
alias layer exists.
|
|
34
35
|
|
|
35
|
-
If you need a brand-new
|
|
36
|
-
|
|
37
|
-
`@elevasis/ui` feature manifests that should consume it.
|
|
36
|
+
If you need a brand-new feature such as `analytics`, add a feature object to the `features[]`
|
|
37
|
+
defaults array in the org model and author a matching manifest. See [add-a-feature.md](../recipes/add-a-feature.md).
|
|
38
38
|
|
|
39
|
-
### Step 1: Declare the
|
|
39
|
+
### Step 1: Declare the feature in `organization-model.ts`
|
|
40
40
|
|
|
41
41
|
File: `foundations/config/organization-model.ts`
|
|
42
42
|
|
|
43
43
|
```ts
|
|
44
44
|
const foundationOrganizationModelOverride = defineOrganizationModel({
|
|
45
|
-
features:
|
|
46
|
-
enabled:
|
|
47
|
-
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
},
|
|
45
|
+
features: [
|
|
46
|
+
{ id: 'crm', label: 'CRM', enabled: true, entityIds: [...], surfaceIds: [...] },
|
|
47
|
+
{ id: 'lead-gen', label: 'Lead Gen', enabled: true, entityIds: [...], surfaceIds: [...] },
|
|
48
|
+
{ id: 'projects', label: 'Projects', enabled: true, entityIds: [...], surfaceIds: [...] },
|
|
49
|
+
// ... other features
|
|
50
|
+
],
|
|
53
51
|
// ... rest of model
|
|
54
52
|
})
|
|
55
53
|
```
|
|
56
54
|
|
|
57
|
-
The `features
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Further down in the same file, the template adapts that canonical model into shell-friendly aliases:
|
|
62
|
-
|
|
63
|
-
```ts
|
|
64
|
-
const featuresEnabled: Record<FoundationFeatureKey, boolean> = {
|
|
65
|
-
acquisition: resolvedOrganizationModel.features.enabled.acquisition,
|
|
66
|
-
crm: resolvedOrganizationModel.features.enabled.acquisition,
|
|
67
|
-
'lead-gen': resolvedOrganizationModel.features.enabled.acquisition
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
That alias layer is why route guards can ask for `crm` or `lead-gen` even though the published
|
|
72
|
-
organization-model contract only knows about `acquisition`.
|
|
55
|
+
The `features[]` array is the source of truth for org-model gates. The 7 default feature IDs are
|
|
56
|
+
`crm`, `lead-gen`, `projects`, `operations`, `monitoring`, `settings`, and `seo`. Each feature
|
|
57
|
+
object's `enabled` field controls whether that feature is on by default for the org.
|
|
73
58
|
|
|
74
59
|
### Step 2: Wire `featureKey` on a nav item
|
|
75
60
|
|
|
@@ -85,10 +70,11 @@ import { crmManifest } from '@elevasis/ui/features/crm'
|
|
|
85
70
|
const manifest: FeatureModule = crmManifest
|
|
86
71
|
```
|
|
87
72
|
|
|
88
|
-
`
|
|
89
|
-
|
|
90
|
-
`
|
|
91
|
-
the nav entry when org or membership
|
|
73
|
+
`featureId` on the `FeatureModule` is what drives whether the entire shared feature (including its
|
|
74
|
+
nav entry) is visible. Each manifest sets `featureId` to the matching `feature.id` value from the
|
|
75
|
+
org model (e.g., `crmManifest.featureId = 'crm'`, `leadGenManifest.featureId = 'lead-gen'`,
|
|
76
|
+
`projectsManifest.featureId = 'projects'`). The shell hides the nav entry when org or membership
|
|
77
|
+
access disables that feature ID.
|
|
92
78
|
|
|
93
79
|
**Local nav-items.ts** (for one-off items not part of a registered feature):
|
|
94
80
|
|
|
@@ -101,8 +87,8 @@ export const navItems: ExtendedLinksGroupProps[] = [
|
|
|
101
87
|
```
|
|
102
88
|
|
|
103
89
|
When `featureKey` is set on a nav item in `nav-items.ts`, the shell hides that item if
|
|
104
|
-
`isFeatureEnabled(featureKey)` returns false.
|
|
105
|
-
|
|
90
|
+
`isFeatureEnabled(featureKey)` returns false. The key must match a `feature.id` value in the org
|
|
91
|
+
model -- no alias mapping is applied.
|
|
106
92
|
|
|
107
93
|
### Step 3: Wrap the route in `FeatureGuard`
|
|
108
94
|
|
|
@@ -141,7 +127,7 @@ All existing feature routes in the template follow this exact pattern: `operatio
|
|
|
141
127
|
For conditional UI within a component (showing or hiding a button, panel, or section based on a feature flag), use the `useFeatureAccess` hook directly.
|
|
142
128
|
|
|
143
129
|
```tsx
|
|
144
|
-
import { useFeatureAccess } from '@/
|
|
130
|
+
import { useFeatureAccess } from '@/lib/hooks/useFeatureAccess'
|
|
145
131
|
|
|
146
132
|
function Dashboard() {
|
|
147
133
|
const { hasFeature } = useFeatureAccess()
|
|
@@ -157,9 +143,9 @@ function Dashboard() {
|
|
|
157
143
|
|
|
158
144
|
`hasFeature` returns a boolean. Use it for render-conditional logic only. Do not use it as a substitute for `FeatureGuard` on a route -- hiding a component does not prevent direct URL access.
|
|
159
145
|
|
|
160
|
-
The hook is defined in `ui/src/
|
|
161
|
-
`createFeatureAccessHook` from `@elevasis/ui/hooks` and
|
|
162
|
-
|
|
146
|
+
The hook is defined in `ui/src/lib/hooks/useFeatureAccess.ts`. It wraps
|
|
147
|
+
`createFeatureAccessHook` from `@elevasis/ui/hooks` and resolves feature IDs directly against the
|
|
148
|
+
org model's `features[]` array -- no alias mapping is applied.
|
|
163
149
|
|
|
164
150
|
---
|
|
165
151
|
|
|
@@ -235,24 +221,24 @@ Use this pattern when only a section of a page is admin-only, not the entire rou
|
|
|
235
221
|
|
|
236
222
|
## Import Paths
|
|
237
223
|
|
|
238
|
-
| Symbol | Import path | Notes
|
|
239
|
-
| ------------------------- | ---------------------------------------- |
|
|
240
|
-
| `FeatureGuard` | `@/features/auth/guards/FeatureGuard` | Template-local wrapper; uses `useFeatureAccess` from `@/
|
|
241
|
-
| `AdminGuard` | `@elevasis/ui/auth` | Published from `@elevasis/ui`
|
|
242
|
-
| `useFeatureAccess` | `@/
|
|
243
|
-
| `createFeatureAccessHook` | `@elevasis/ui/hooks` | Factory for building a feature-access hook; already consumed by the template wrapper
|
|
244
|
-
| `ProtectedRoute` | `@elevasis/ui/auth` or `@/features/auth` | Ensures user is authenticated before any guard runs
|
|
224
|
+
| Symbol | Import path | Notes |
|
|
225
|
+
| ------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
226
|
+
| `FeatureGuard` | `@/features/auth/guards/FeatureGuard` | Template-local wrapper; uses `useFeatureAccess` from `@/lib/hooks/useFeatureAccess` |
|
|
227
|
+
| `AdminGuard` | `@elevasis/ui/auth` | Published from `@elevasis/ui` |
|
|
228
|
+
| `useFeatureAccess` | `@/lib/hooks/useFeatureAccess` | Template-local wrapper around `createFeatureAccessHook` from `@elevasis/ui/hooks` |
|
|
229
|
+
| `createFeatureAccessHook` | `@elevasis/ui/hooks` | Factory for building a feature-access hook; already consumed by the template wrapper |
|
|
230
|
+
| `ProtectedRoute` | `@elevasis/ui/auth` or `@/features/auth` | Ensures user is authenticated before any guard runs |
|
|
245
231
|
|
|
246
|
-
The template's `FeatureGuard` (`@/features/auth/guards/FeatureGuard`) is not the same as the published `FeatureGuard` from `@elevasis/ui/features`. The template version closes over the local `useFeatureAccess` hook and the
|
|
232
|
+
The template's `FeatureGuard` (`@/features/auth/guards/FeatureGuard`) is not the same as the published `FeatureGuard` from `@elevasis/ui/features`. The template version closes over the local `useFeatureAccess` hook and the resolved org model, so it checks feature IDs against your project's feature array. Use the template-local version.
|
|
247
233
|
|
|
248
234
|
---
|
|
249
235
|
|
|
250
236
|
## Common Mistakes
|
|
251
237
|
|
|
252
|
-
- **
|
|
253
|
-
`
|
|
254
|
-
|
|
255
|
-
|
|
238
|
+
- **Using a feature ID that does not exist in the org model.** `FeatureModule.featureId` and
|
|
239
|
+
`featureKey` on nav entries must match a `feature.id` value in the org model's `features[]`
|
|
240
|
+
array. The provider throws at startup if an unknown ID is declared. To add a brand-new feature
|
|
241
|
+
such as `analytics`, first add it to the `features[]` array in `foundations/config/organization-model.ts`.
|
|
256
242
|
|
|
257
243
|
- **Using `FeatureGuard` on a nav item instead of a route.** `FeatureGuard` is a React component that redirects on mount. Placing it inside a nav item or link will fire a redirect whenever the sidebar renders. Gate nav visibility with `featureKey` on the nav entry or `accessFeatureKey` on the manifest; gate route access with `FeatureGuard` in the route layout.
|
|
258
244
|
|
|
@@ -10,10 +10,10 @@ Within Organization OS, the feature shell spans two layers: the **UI Shell Runti
|
|
|
10
10
|
Three layers participate, and the word "feature" means something different in each:
|
|
11
11
|
|
|
12
12
|
- **Platform capabilities** -- product-facing areas documented under `technical/features/` (Execution Engine, Workflows, Agents, Operations, etc.). These are not one-to-one with shell features.
|
|
13
|
-
- **Shell features** -- features in `packages/ui/src/features/*`. Seven are manifest-backed (`lead-gen`, `
|
|
14
|
-
- **Organization-model features** --
|
|
13
|
+
- **Shell features** -- features in `packages/ui/src/features/*`. Seven are manifest-backed (`crm`, `lead-gen`, `projects`, `operations`, `monitoring`, `settings`, `seo`); two are utility features without manifests (`auth`, `dashboard`).
|
|
14
|
+
- **Organization-model features** -- feature objects in `@repo/core/organization-model` with shape `{ id, label, enabled, entityIds, surfaceIds, resourceIds, capabilityIds }`. The 7 defaults are `crm`, `lead-gen`, `projects`, `operations`, `monitoring`, `settings`, `seo`. See [Organization Model](../core/organization-model.mdx).
|
|
15
15
|
|
|
16
|
-
One shell feature can contain several platform capabilities;
|
|
16
|
+
One shell feature can contain several platform capabilities; a manifest's `featureId` directly matches `feature.id` in the org model with no alias layer.
|
|
17
17
|
|
|
18
18
|
## Source of Truth
|
|
19
19
|
|
|
@@ -30,24 +30,24 @@ One shell feature can contain several platform capabilities; shell and organizat
|
|
|
30
30
|
|
|
31
31
|
A `FeatureModule` describes one shell feature's contribution. Fields:
|
|
32
32
|
|
|
33
|
-
- `key` -- unique stable identifier (e.g., `'crm'`, `'
|
|
34
|
-
- `
|
|
35
|
-
- `
|
|
33
|
+
- `key` -- unique stable identifier (e.g., `'crm'`, `'projects'`)
|
|
34
|
+
- `featureId` -- **required**; the `feature.id` value from the org model that this module maps to; used by `useFeatureAccess()` for gating
|
|
35
|
+
- `capabilityIds` -- semantic references into the organization model
|
|
36
36
|
- `navEntry` -- top-level nav contribution (label, icon, optional `link`, optional nested `links`, optional `requiresAdmin`, optional onboarding-tour ID, optional `featureKey` override when nav identity must diverge from access identity)
|
|
37
37
|
- `sidebar` -- optional `ComponentType` for the feature's subshell sidebar
|
|
38
38
|
- `subshellRoutes` -- routes the feature owns under its subshell
|
|
39
39
|
- `organizationGraph` -- **Operations-only**; bridges a manifest to an organization-model surface (ignored by other features)
|
|
40
40
|
|
|
41
|
-
`FeatureModule.label` has been removed; nav labels resolve from `navEntry.label` or from organization-model feature
|
|
41
|
+
`FeatureModule.label` has been removed; nav labels resolve from `navEntry.label` or from the matching organization-model feature's `label` field.
|
|
42
42
|
|
|
43
|
-
Manifests are validated at provider registration via `validateManifests()`. Unknown `
|
|
43
|
+
Manifests are validated at provider registration via `validateManifests()`. Unknown `featureId` or `capabilityIds` (measured against the resolved organization model) throw with all violations collected into one error.
|
|
44
44
|
|
|
45
45
|
### ResolvedFeatureModule
|
|
46
46
|
|
|
47
47
|
`ResolvedFeatureModule` extends `FeatureModule` with two additional fields added during provider resolution:
|
|
48
48
|
|
|
49
|
-
- `access: ResolvedFeatureAccess` -- `{
|
|
50
|
-
- `semantics: ResolvedFeatureSemantics` -- `{
|
|
49
|
+
- `access: ResolvedFeatureAccess` -- `{ featureId: string, enabled: boolean }` -- the resolved access state for this feature
|
|
50
|
+
- `semantics: ResolvedFeatureSemantics` -- `{ capabilityIds, surfaceIds, surfaces }` -- merged semantic identifiers derived from both manifest declarations and organization-model surface data
|
|
51
51
|
|
|
52
52
|
`ResolvedFeatureSemantics.surfaces` carries the full `OrganizationModelSurface[]` objects (not just IDs), so consumers can inspect surface metadata without a separate lookup.
|
|
53
53
|
|
|
@@ -108,8 +108,8 @@ Consumers keep thin local route wrappers and app-local nav where needed.
|
|
|
108
108
|
|
|
109
109
|
1. The app defines a manifest list (often by spreading `FEATURE_MANIFESTS` and overriding entries).
|
|
110
110
|
2. The app optionally resolves an organization model and passes it to the provider.
|
|
111
|
-
3. The provider combines `useFeatureAccess()` with
|
|
112
|
-
4. Nav labels and nav paths resolve from `organizationModel.features
|
|
111
|
+
3. The provider combines `useFeatureAccess()` with the org model's `features[]` array to compute enabled features.
|
|
112
|
+
4. Nav labels and nav paths resolve from the matching `feature.label` in `organizationModel.features` and from `organizationModel.navigation.surfaces` when present.
|
|
113
113
|
5. The provider exposes `shellModel`, `shellRuntime`, resolved feature access, `organizationGraph`, and shared runtime inputs via `useElevasisFeatures()`.
|
|
114
114
|
6. `FeatureShell` calls `shellRuntime.resolveRoute(currentPath)`:
|
|
115
115
|
- `matched` -- render the feature's sidebar subshell
|
|
@@ -144,7 +144,7 @@ Consumer nav derivation runs locally from `shellModel.navItems`; the provider no
|
|
|
144
144
|
|
|
145
145
|
- `placement: 'primary' | 'bottom'` -- where the item appears in the shell nav; feature manifests always produce `'primary'`; `appShellOverrides.bottomNavItems` produce `'bottom'`
|
|
146
146
|
- `source: 'app' | 'feature'` -- whether the item came from a manifest (`'feature'`) or from `appShellOverrides` (`'app'`)
|
|
147
|
-
- `
|
|
147
|
+
- `featureId?: string` -- the feature ID associated with this nav item for gating checks
|
|
148
148
|
|
|
149
149
|
`shellModel.navItems` contains the merged and filtered list of all `ResolvedShellNavItem`s from both manifest features and `appShellOverrides`.
|
|
150
150
|
|
|
@@ -152,14 +152,14 @@ Consumer nav derivation runs locally from `shellModel.navItems`; the provider no
|
|
|
152
152
|
|
|
153
153
|
`filterNavLinks()` in `ElevasisFeaturesProvider.tsx` recursively removes gated and disabled-subsection links from a `FeatureNavLink[]` array. A link is removed if its `featureKey` fails `isFeatureEnabled()` or if its path matches any entry in `disabledSubsectionPaths`. The function recurses into nested `links` arrays before deciding whether a parent with now-empty children should be retained or dropped.
|
|
154
154
|
|
|
155
|
-
## Access Resolution
|
|
155
|
+
## Access Resolution
|
|
156
156
|
|
|
157
|
-
Shell feature-module keys
|
|
157
|
+
Shell feature-module keys map directly to feature IDs in the organization model. There is no alias layer:
|
|
158
158
|
|
|
159
159
|
- `createFeatureAccessHook` is the factory that produces `useFeatureAccess`. (Old name `createUseFeatureAccess` is re-exported as `@deprecated`.)
|
|
160
|
-
- `
|
|
161
|
-
-
|
|
162
|
-
- The provider fallback for missing feature access identity is retired -- explicit `
|
|
160
|
+
- `FeatureModule.featureId` must match a `feature.id` value in the org model's `features[]` array. The provider throws at startup if the ID is not found.
|
|
161
|
+
- `MembershipFeatureConfig.features` is `Record<string, boolean>` -- a dynamic map keyed by feature ID. Any feature can be overridden per member without schema changes.
|
|
162
|
+
- The provider fallback for missing feature access identity is retired -- explicit `featureId` is required.
|
|
163
163
|
|
|
164
164
|
## Published Surface
|
|
165
165
|
|
|
@@ -170,8 +170,8 @@ All nine features are published as individual subpath exports from `@elevasis/ui
|
|
|
170
170
|
- `@elevasis/ui/features/auth` -- `ProtectedRoute`, `AdminGuard`, `FeatureGuard`, `useUserProfile` (utility feature; no manifest)
|
|
171
171
|
- `@elevasis/ui/features/dashboard` -- `Dashboard`, `ResourceOverview`, `RecentExecutionsByResource`, `UnresolvedErrorsTeaser` (utility feature; no manifest)
|
|
172
172
|
- `@elevasis/ui/features/crm`
|
|
173
|
-
- `@elevasis/ui/features/delivery`
|
|
174
173
|
- `@elevasis/ui/features/lead-gen`
|
|
174
|
+
- `@elevasis/ui/features/projects`
|
|
175
175
|
- `@elevasis/ui/features/monitoring`
|
|
176
176
|
- `@elevasis/ui/features/operations`
|
|
177
177
|
- `@elevasis/ui/features/seo`
|
|
@@ -201,7 +201,7 @@ The `auth` and `dashboard` features are not in `FEATURE_MANIFESTS` and are not r
|
|
|
201
201
|
|
|
202
202
|
Both arrays go through the same `isFeatureEnabled()` and `disabledSubsectionPaths` filtering as manifest-derived items. Items whose `featureKey` is disabled, or that have only disabled nested links and no direct `link`, are dropped entirely.
|
|
203
203
|
|
|
204
|
-
Nine feature modules are published (see **Published Surface** above). Six are currently mounted in command-center: `leadGenManifest`, `crmManifest`, `
|
|
204
|
+
Nine feature modules are published (see **Published Surface** above). Six are currently mounted in command-center: `leadGenManifest`, `crmManifest`, `projectsManifest`, `operationsManifest`, `monitoringManifest`, `settingsManifest`. The remaining three (`seoManifest`, `auth`, `dashboard`) are published but not mounted through `ElevasisFeaturesProvider` in command-center -- `auth` and `dashboard` are utility features consumed directly, while `seoManifest` is available for composition but not wired into the current command-center shell.
|
|
205
205
|
|
|
206
206
|
### External-consumer composition
|
|
207
207
|
|
|
@@ -221,7 +221,6 @@ The CRM sidebar (`packages/ui/src/features/crm/sidebar/`) exports `CrmSidebar`,
|
|
|
221
221
|
|
|
222
222
|
- Returns `null` for sessions (`/operations/sessions`) and command-view (`/operations/command-view`) routes -- these sections own their own top-area UI
|
|
223
223
|
- Shows a "Resource Overview" navigation button for the resources section (`/operations/resources`)
|
|
224
|
-
- Shows a "Calibration Projects" navigation button for the calibration section (`/operations/calibration`)
|
|
225
224
|
- Returns `null` for all other paths (e.g., the operations index)
|
|
226
225
|
|
|
227
226
|
This pattern avoids hardcoding sidebar top content in the parent and lets each operations sub-section declare its own top panel entry point.
|
|
@@ -236,6 +235,6 @@ This pattern avoids hardcoding sidebar top content in the parent and lets each o
|
|
|
236
235
|
## Key Conceptual Distinctions
|
|
237
236
|
|
|
238
237
|
- **Platform capability vs. shell feature** -- capabilities are the product map; shell features are manifest-backed UI surfaces.
|
|
239
|
-
- **Shell feature key vs.
|
|
240
|
-
- **
|
|
238
|
+
- **Shell feature key vs. org-model feature id** -- both are the same value (e.g., `crm`). `FeatureModule.featureId` directly matches `feature.id` in the org model; there is no alias layer.
|
|
239
|
+
- **Feature vs. capability** -- a feature object in the org model carries `entityIds`, `surfaceIds`, `resourceIds`, and `capabilityIds`; capabilities are one kind of semantic reference a feature can hold.
|
|
241
240
|
- **Provider-owned vs. consumer-owned** -- provider owns shared manifests, nav contribution, sidebar dispatch, shared runtime context; consumers own route files, branding, topbar behavior, admin entries.
|