@elevasis/sdk 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +52 -149
- package/dist/index.d.ts +468 -198
- package/dist/index.js +225 -147
- package/dist/test-utils/index.d.ts +272 -99
- package/dist/test-utils/index.js +4756 -125
- package/dist/types/worker/adapters/llm.d.ts +1 -1
- package/dist/worker/index.js +14 -6
- package/package.json +2 -2
- package/reference/claude-config/rules/agent-start-here.md +14 -14
- package/reference/claude-config/skills/configure/SKILL.md +3 -3
- package/reference/claude-config/skills/setup/SKILL.md +6 -6
- package/reference/claude-config/sync-notes/2026-04-25-auth-role-system-and-settings-roles.md +55 -0
- package/reference/claude-config/sync-notes/2026-04-27-crm-hitl-action-layer-cutover.md +101 -0
- package/reference/cli.mdx +57 -0
- package/reference/deployment/provided-features.mdx +40 -267
- package/reference/examples/organization-model.ts +99 -564
- package/reference/packages/core/src/organization-model/README.md +102 -97
- package/reference/resources/types.mdx +72 -163
- package/reference/scaffold/core/organization-graph.mdx +92 -272
- package/reference/scaffold/core/organization-model.mdx +155 -320
- package/reference/scaffold/index.mdx +3 -0
- package/reference/scaffold/operations/propagation-pipeline.md +4 -1
- package/reference/scaffold/operations/scaffold-maintenance.md +3 -0
- package/reference/scaffold/operations/workflow-recipes.md +13 -10
- package/reference/scaffold/recipes/add-a-feature.md +105 -158
- package/reference/scaffold/recipes/add-a-resource.md +88 -158
- package/reference/scaffold/recipes/customize-organization-model.md +144 -400
- package/reference/scaffold/recipes/extend-a-base-entity.md +11 -8
- package/reference/scaffold/recipes/gate-by-feature-or-admin.md +117 -158
- package/reference/scaffold/recipes/index.md +3 -0
- package/reference/scaffold/reference/contracts.md +107 -435
- package/reference/scaffold/reference/feature-registry.md +11 -8
- package/reference/scaffold/reference/glossary.md +74 -105
- package/reference/scaffold/ui/composition-extensibility.mdx +3 -0
- package/reference/scaffold/ui/customization.md +3 -0
- package/reference/scaffold/ui/feature-flags-and-gating.md +29 -231
- package/reference/scaffold/ui/feature-shell.mdx +53 -219
- package/reference/scaffold/ui/recipes.md +65 -397
- package/reference/claude-config/logs/pre-edit-vibe-gate.log +0 -40
- package/reference/claude-config/logs/scaffold-registry-reminder.log +0 -38
|
@@ -1,400 +1,144 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Customize organization-model.ts
|
|
3
|
-
description: Annotated walkthrough for customizing the
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
contract
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
- `
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- `
|
|
75
|
-
- `
|
|
76
|
-
- `
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
id: 'my-mapping', // unique within resourceMappings
|
|
146
|
-
resourceId: 'my-lead-scraper-workflow', // the deployed resource ID from operations/
|
|
147
|
-
resourceType: 'workflow', // 'workflow' | 'agent' | 'trigger' | 'integration' | 'external' | 'human_checkpoint'
|
|
148
|
-
label: 'Lead Scraper',
|
|
149
|
-
featureIds: ['lead-gen'], // bidirectional: feature.resourceIds must include resourceId
|
|
150
|
-
entityIds: ['leadgen.company'],
|
|
151
|
-
surfaceIds: ['lead-gen.lists'], // bidirectional: surface.resourceIds must include resourceId
|
|
152
|
-
capabilityIds: []
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
The schema enforces full bidirectionality. If you add a resource mapping with `featureIds: ['lead-gen']`
|
|
158
|
-
then the `lead-gen` feature entry must include the `resourceId` in its own `resourceIds`. Both sides
|
|
159
|
-
must be consistent or `resolveOrganizationModel` throws at startup. See [add-a-resource.md](add-a-resource.md)
|
|
160
|
-
for the full resource authoring workflow.
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
### Domain-specific config
|
|
165
|
-
|
|
166
|
-
The `sales`, `prospecting`, and `projects` top-level keys configure pipeline stages, entity ID bindings,
|
|
167
|
-
and status vocabularies for those domains. These are consumed by the platform adapters and UI
|
|
168
|
-
components -- they are not just labels.
|
|
169
|
-
|
|
170
|
-
```ts
|
|
171
|
-
sales: {
|
|
172
|
-
entityId: 'crm.deal',
|
|
173
|
-
defaultPipelineId: 'default',
|
|
174
|
-
pipelines: [
|
|
175
|
-
{
|
|
176
|
-
id: 'default',
|
|
177
|
-
label: 'Default Pipeline',
|
|
178
|
-
entityId: 'crm.deal',
|
|
179
|
-
stages: [
|
|
180
|
-
{ id: 'interested', label: 'Interested', color: 'blue', order: 1, semanticClass: 'open', surfaceIds: ['crm.pipeline'], resourceIds: [] },
|
|
181
|
-
{ id: 'closed_won', label: 'Closed Won', color: 'green', order: 4, semanticClass: 'closed_won', surfaceIds: ['crm.pipeline'], resourceIds: [] }
|
|
182
|
-
// ... additional stages
|
|
183
|
-
]
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
},
|
|
187
|
-
prospecting: {
|
|
188
|
-
listEntityId: 'leadgen.list',
|
|
189
|
-
companyEntityId: 'leadgen.company',
|
|
190
|
-
contactEntityId: 'leadgen.contact',
|
|
191
|
-
companyStages: [
|
|
192
|
-
{ id: 'populated', label: 'Populated', order: 1 },
|
|
193
|
-
{ id: 'qualified', label: 'Qualified', order: 3 }
|
|
194
|
-
],
|
|
195
|
-
contactStages: [
|
|
196
|
-
{ id: 'discovered', label: 'Discovered', order: 1 },
|
|
197
|
-
{ id: 'uploaded', label: 'Uploaded', order: 4 }
|
|
198
|
-
]
|
|
199
|
-
},
|
|
200
|
-
projects: {
|
|
201
|
-
projectEntityId: 'delivery.project',
|
|
202
|
-
milestoneEntityId: 'delivery.milestone',
|
|
203
|
-
taskEntityId: 'delivery.task',
|
|
204
|
-
projectStatuses: [ /* ... */ ],
|
|
205
|
-
milestoneStatuses: [ /* ... */ ],
|
|
206
|
-
taskStatuses: [ /* ... */ ]
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
`semanticClass` on sales pipeline stages is significant: the platform uses `closed_won`, `closed_lost`,
|
|
211
|
-
`nurturing`, `open`, and `active` for funnel analytics and workflow triggers. Do not rename these
|
|
212
|
-
to arbitrary strings.
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
### Export pattern
|
|
217
|
-
|
|
218
|
-
The template exports two models: `canonicalOrganizationModel` for the provider (machine-facing)
|
|
219
|
-
and `organizationModel` for the foundation nav layer (UI-facing with the icon-augmented surface type).
|
|
220
|
-
|
|
221
|
-
```ts
|
|
222
|
-
const resolvedOrganizationModel = resolveOrganizationModel(foundationOrganizationModelOverride)
|
|
223
|
-
|
|
224
|
-
// Passed to ElevasisFeaturesProvider in ui/src/routes/__root.tsx
|
|
225
|
-
export const canonicalOrganizationModel: OrganizationModel = resolvedOrganizationModel
|
|
226
|
-
|
|
227
|
-
// Used by foundation nav components that need the augmented FoundationNavigationSurface type
|
|
228
|
-
export const organizationModel: FoundationOrganizationModel = {
|
|
229
|
-
...canonicalOrganizationModel,
|
|
230
|
-
navigation: {
|
|
231
|
-
defaultSurfaceId: 'operations',
|
|
232
|
-
homeLabel,
|
|
233
|
-
quickAccessSurfaceIds: [...quickAccessSurfaceIds],
|
|
234
|
-
surfaces: navigationSurfaces // FoundationNavigationSurface[] with icon field
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
The `requireCoreSurface` helper (defined in the template file) throws at startup if a surface ID
|
|
240
|
-
declared in the foundations layer is not present in the resolved model. Use it to catch mismatches
|
|
241
|
-
early rather than seeing silent `undefined` in the nav at runtime.
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## Common Customizations
|
|
246
|
-
|
|
247
|
-
### Adding a new feature
|
|
248
|
-
|
|
249
|
-
Add a new entry to the `features` array:
|
|
250
|
-
|
|
251
|
-
```ts
|
|
252
|
-
{
|
|
253
|
-
id: 'analytics',
|
|
254
|
-
label: 'Analytics',
|
|
255
|
-
enabled: false, // start disabled; enable per-org when ready
|
|
256
|
-
entityIds: [],
|
|
257
|
-
surfaceIds: [],
|
|
258
|
-
resourceIds: [],
|
|
259
|
-
capabilityIds: []
|
|
260
|
-
}
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
Then proceed with the full manifest, route, and gating steps in [add-a-feature.md](add-a-feature.md).
|
|
264
|
-
The org model change above is Step 2 of that recipe.
|
|
265
|
-
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
### Disabling a default feature
|
|
269
|
-
|
|
270
|
-
Set `enabled: false` on the feature entry. The nav item disappears and `FeatureGuard` blocks
|
|
271
|
-
direct URL access:
|
|
272
|
-
|
|
273
|
-
```ts
|
|
274
|
-
{
|
|
275
|
-
id: 'seo',
|
|
276
|
-
label: 'SEO',
|
|
277
|
-
enabled: false, // nav item hidden, routes redirect to home
|
|
278
|
-
entityIds: [],
|
|
279
|
-
surfaceIds: [],
|
|
280
|
-
resourceIds: [],
|
|
281
|
-
capabilityIds: []
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
The `seo` feature ships disabled by default. If your project does not use `monitoring` either,
|
|
286
|
-
set it to `enabled: false` as well.
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
### Adding entities to a feature
|
|
291
|
-
|
|
292
|
-
Entity IDs are dot-namespaced strings (`domain.type`). Add to both the feature and any surfaces
|
|
293
|
-
that display those entities:
|
|
294
|
-
|
|
295
|
-
```ts
|
|
296
|
-
// In features array:
|
|
297
|
-
{
|
|
298
|
-
id: 'crm',
|
|
299
|
-
entityIds: ['crm.deal', 'crm.contact'], // added crm.contact
|
|
300
|
-
// ...
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// In the surface that shows contacts:
|
|
304
|
-
{
|
|
305
|
-
id: 'crm.pipeline',
|
|
306
|
-
entityIds: ['crm.deal', 'crm.contact'], // mirror the addition
|
|
307
|
-
// ...
|
|
308
|
-
}
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
Entity IDs inform the organization graph node taxonomy. They do not require registration anywhere
|
|
312
|
-
else in the platform; just keep them consistent across feature and surface declarations.
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
### Customizing feature labels and descriptions
|
|
317
|
-
|
|
318
|
-
Edit `label` and `description` directly on the feature entry. These values propagate to nav
|
|
319
|
-
label resolution and the settings UI:
|
|
320
|
-
|
|
321
|
-
```ts
|
|
322
|
-
{
|
|
323
|
-
id: 'lead-gen',
|
|
324
|
-
label: 'Prospecting', // override the default 'Lead Gen' label
|
|
325
|
-
description: 'Find and qualify new opportunities',
|
|
326
|
-
// ...
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
If the `FeatureModule` manifest also declares a `navEntry.label`, the manifest label wins in the
|
|
331
|
-
nav sidebar. The feature `label` is the fallback when no manifest override exists.
|
|
332
|
-
|
|
333
|
-
---
|
|
334
|
-
|
|
335
|
-
### Adding surfaces
|
|
336
|
-
|
|
337
|
-
Declare the surface under `navigation.surfaces` and cross-reference it from the owning feature.
|
|
338
|
-
Both sides must be consistent (schema validates bidirectionality):
|
|
339
|
-
|
|
340
|
-
```ts
|
|
341
|
-
// In navigation.surfaces:
|
|
342
|
-
{
|
|
343
|
-
id: 'analytics.dashboard',
|
|
344
|
-
label: 'Analytics',
|
|
345
|
-
path: '/analytics',
|
|
346
|
-
surfaceType: 'dashboard',
|
|
347
|
-
featureId: 'analytics',
|
|
348
|
-
featureIds: ['analytics'],
|
|
349
|
-
entityIds: [],
|
|
350
|
-
resourceIds: [],
|
|
351
|
-
capabilityIds: ['analytics.view']
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// In the analytics feature entry:
|
|
355
|
-
{
|
|
356
|
-
id: 'analytics',
|
|
357
|
-
surfaceIds: ['analytics.dashboard'], // must reference the surface ID above
|
|
358
|
-
capabilityIds: ['analytics.view'],
|
|
359
|
-
// ...
|
|
360
|
-
}
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
---
|
|
364
|
-
|
|
365
|
-
## How It Connects
|
|
366
|
-
|
|
367
|
-
The organization model flows through the runtime in one direction:
|
|
368
|
-
|
|
369
|
-
1. `core/config/organization-model.ts` calls `resolveOrganizationModel` at module load.
|
|
370
|
-
The resolved `canonicalOrganizationModel` is exported.
|
|
371
|
-
2. `ui/src/routes/__root.tsx` imports `canonicalOrganizationModel` and passes it to
|
|
372
|
-
`ElevasisFeaturesProvider` along with the `FEATURE_MANIFESTS` array.
|
|
373
|
-
3. `ElevasisFeaturesProvider` validates each manifest's `featureId` against the model's `features`
|
|
374
|
-
array. Unknown `featureId` values throw at startup.
|
|
375
|
-
4. At runtime, the provider uses `features[n].enabled` to decide which nav entries to show.
|
|
376
|
-
`FeatureGuard` reads the same value to allow or block route access.
|
|
377
|
-
5. The organization graph reads `features`, `navigation.surfaces`, `entityIds`, and `resourceMappings`
|
|
378
|
-
to build the operational topology visualization.
|
|
379
|
-
|
|
380
|
-
The contract flows one way: model defines, runtime reads. No part of the UI runtime writes back
|
|
381
|
-
to the model.
|
|
382
|
-
|
|
383
|
-
---
|
|
384
|
-
|
|
385
|
-
## Resolve Behavior
|
|
386
|
-
|
|
387
|
-
`resolveOrganizationModel(override)` does the following:
|
|
388
|
-
|
|
389
|
-
1. Deep-merges the override onto `DEFAULT_ORGANIZATION_MODEL`. Plain objects are merged key by key.
|
|
390
|
-
**Arrays are replaced wholesale** -- if you supply `features: [...]`, your array replaces the
|
|
391
|
-
defaults entirely, not appends to them.
|
|
392
|
-
2. If you override `navigation.surfaces` without overriding `navigation.groups`, the resolver
|
|
393
|
-
automatically filters out any groups whose `surfaceIds` reference surfaces that no longer exist
|
|
394
|
-
in your override. This prevents dangling group references from causing a parse failure.
|
|
395
|
-
3. Parses the merged result through `OrganizationModelSchema`. Any bidirectional reference
|
|
396
|
-
inconsistency (feature \<-\> surface, surface \<-\> resource mapping) throws a Zod validation
|
|
397
|
-
error with a specific path and message pointing to the offending reference.
|
|
398
|
-
|
|
399
|
-
When you see a startup error from `resolveOrganizationModel`, read the Zod issue path carefully.
|
|
400
|
-
It will identify which specific field and array index contains the broken reference.
|
|
1
|
+
---
|
|
2
|
+
title: Customize organization-model.ts
|
|
3
|
+
description: Annotated walkthrough for customizing the flat Organization Model in template-derived projects.
|
|
4
|
+
---
|
|
5
|
+
<!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
|
|
6
|
+
<!-- Regenerate: pnpm scaffold:sync -->
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Customize organization-model.ts
|
|
10
|
+
|
|
11
|
+
`core/config/organization-model.ts` is the semantic contract between the UI shell and platform operations. The shell reads one flat feature list, derives hierarchy from dotted IDs, and binds resources through graph links declared on resource metadata.
|
|
12
|
+
|
|
13
|
+
## Imports
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import {
|
|
17
|
+
createFoundationOrganizationModel,
|
|
18
|
+
defineOrganizationModel,
|
|
19
|
+
type OrganizationModel
|
|
20
|
+
} from '@elevasis/core/organization-model'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `defineOrganizationModel` gives a typed override shape.
|
|
24
|
+
- `createFoundationOrganizationModel` resolves defaults and returns the canonical model plus UI-facing helpers.
|
|
25
|
+
- `OrganizationModel` is the fully resolved model type.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
The `features` array is the source of truth for navigation, labels, enabled state, admin visibility, and development-only visibility.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const override = defineOrganizationModel({
|
|
33
|
+
features: [
|
|
34
|
+
{
|
|
35
|
+
id: 'dashboard',
|
|
36
|
+
label: 'Dashboard',
|
|
37
|
+
enabled: true,
|
|
38
|
+
path: '/',
|
|
39
|
+
icon: 'dashboard',
|
|
40
|
+
uiPosition: 'sidebar-primary'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'sales',
|
|
44
|
+
label: 'Sales',
|
|
45
|
+
enabled: true,
|
|
46
|
+
icon: 'briefcase',
|
|
47
|
+
uiPosition: 'sidebar-primary'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'sales.crm',
|
|
51
|
+
label: 'CRM',
|
|
52
|
+
enabled: true,
|
|
53
|
+
path: '/crm'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'admin',
|
|
57
|
+
label: 'Admin',
|
|
58
|
+
enabled: true,
|
|
59
|
+
path: '/admin',
|
|
60
|
+
uiPosition: 'sidebar-bottom',
|
|
61
|
+
requiresAdmin: true
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Feature field reference:
|
|
68
|
+
|
|
69
|
+
- `id` -- lowercase dotted path. Parent features are inferred from prefix segments.
|
|
70
|
+
- `label` -- sidebar and breadcrumb label.
|
|
71
|
+
- `description` -- optional settings/help text.
|
|
72
|
+
- `enabled` -- organization default.
|
|
73
|
+
- `path` -- route path for leaf nodes. Containers omit it.
|
|
74
|
+
- `icon` and `color` -- optional display metadata.
|
|
75
|
+
- `uiPosition` -- `sidebar-primary` or `sidebar-bottom`; descendants inherit it.
|
|
76
|
+
- `requiresAdmin` -- hides the node for non-admin members; descendants inherit it.
|
|
77
|
+
- `devOnly` -- hides the node outside development contexts; descendants inherit it.
|
|
78
|
+
|
|
79
|
+
## Resource Binding
|
|
80
|
+
|
|
81
|
+
Resources do not live in `OrganizationModel`. Workflows, agents, triggers, integrations, and external resources declare semantic graph links in their resource metadata.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
config: {
|
|
85
|
+
resourceId: 'lead-import',
|
|
86
|
+
name: 'Lead Import',
|
|
87
|
+
type: 'workflow',
|
|
88
|
+
version: '1.0.0',
|
|
89
|
+
status: 'prod',
|
|
90
|
+
links: [{ nodeId: 'feature:sales.lead-gen', kind: 'operates-on' }],
|
|
91
|
+
category: 'production'
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use kind-prefixed graph IDs for cross-collection links:
|
|
96
|
+
|
|
97
|
+
- `feature:sales.crm`
|
|
98
|
+
- `integration:instantly`
|
|
99
|
+
- `resource:lead-import`
|
|
100
|
+
- `capability:operations.queue.review`
|
|
101
|
+
|
|
102
|
+
`category` is one of `production`, `diagnostic`, `internal`, or `testing`.
|
|
103
|
+
|
|
104
|
+
## Domain Config
|
|
105
|
+
|
|
106
|
+
The `sales`, `prospecting`, and `projects` top-level keys configure business semantics such as pipeline stages, entity IDs, and status vocabularies. They are not shell navigation.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
sales: {
|
|
110
|
+
entityId: 'crm.deal',
|
|
111
|
+
defaultPipelineId: 'default',
|
|
112
|
+
pipelines: [
|
|
113
|
+
{
|
|
114
|
+
id: 'default',
|
|
115
|
+
label: 'Default Pipeline',
|
|
116
|
+
entityId: 'crm.deal',
|
|
117
|
+
stages: [
|
|
118
|
+
{ id: 'interested', label: 'Interested', color: 'blue', order: 1, semanticClass: 'open' },
|
|
119
|
+
{ id: 'closed_won', label: 'Closed Won', color: 'green', order: 4, semanticClass: 'closed_won' }
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Keep `semanticClass` values stable; platform analytics and triggers depend on them.
|
|
127
|
+
|
|
128
|
+
## Export Pattern
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const foundation = createFoundationOrganizationModel(override)
|
|
132
|
+
|
|
133
|
+
export const canonicalOrganizationModel: OrganizationModel = foundation.canonical
|
|
134
|
+
export const organizationModel = foundation.model
|
|
135
|
+
export const { getOrganizationSurface } = foundation
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Pass `canonicalOrganizationModel` to `ElevasisFeaturesProvider` in `ui/src/routes/__root.tsx`.
|
|
139
|
+
|
|
140
|
+
## Resolve Behavior
|
|
141
|
+
|
|
142
|
+
`createFoundationOrganizationModel(override)` resolves defaults, validates feature hierarchy, and exposes helper functions used by the shell. Arrays are replaced wholesale, so if you provide `features`, include every feature the app should expose.
|
|
143
|
+
|
|
144
|
+
Validation catches duplicate feature IDs, missing parent containers, invalid graph IDs, and invalid sidebar positions.
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
title: Extend a Base Entity
|
|
3
3
|
description: Add project-specific metadata to the canonical entity shapes (Project, Deal, Company, etc.) from @elevasis/core/entities using the TMeta extension slot.
|
|
4
4
|
---
|
|
5
|
+
<!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
|
|
6
|
+
<!-- Regenerate: pnpm scaffold:sync -->
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
# Extend a Base Entity
|
|
7
10
|
|
|
8
11
|
Workflows and UI features often operate on domain entities such as projects, deals, companies, or contacts. Rather than each project declaring its own shape from scratch, `@elevasis/core/entities` provides typed base interfaces generic over a `<TMeta>` slot. External projects extend these to add project-specific fields while keeping the canonical shape stable and interoperable with platform tooling.
|
|
9
12
|
|
|
10
|
-
The canonical demo lives in `
|
|
13
|
+
The canonical demo lives in `core/types/entities.ts` of any scaffold project.
|
|
11
14
|
|
|
12
15
|
---
|
|
13
16
|
|
|
@@ -30,7 +33,7 @@ All imports come from `@elevasis/core/entities`.
|
|
|
30
33
|
|
|
31
34
|
## Recipe 1 -- Extend a base entity with custom metadata
|
|
32
35
|
|
|
33
|
-
Place this in `
|
|
36
|
+
Place this in `core/types/entities.ts`. This is the primary pattern -- the scaffold template ships this exact example.
|
|
34
37
|
|
|
35
38
|
```ts
|
|
36
39
|
import { z } from 'zod'
|
|
@@ -56,7 +59,7 @@ Key points:
|
|
|
56
59
|
- `BaseProjectSchema.extend({ metadata: ... })` merges your metadata Zod schema into the validated shape. Zod handles the rest.
|
|
57
60
|
- `BaseProject<ProjectMeta>` infers the TypeScript type with your metadata typed correctly.
|
|
58
61
|
- Only the `metadata` field is extended -- all other base fields (`id`, `organizationId`, `name`, `status`, `createdAt`, `updatedAt`, etc.) are inherited unchanged.
|
|
59
|
-
- This file is the single source of truth for entity shapes across `
|
|
62
|
+
- This file is the single source of truth for entity shapes across `core/`, `operations/`, and `ui/`.
|
|
60
63
|
|
|
61
64
|
---
|
|
62
65
|
|
|
@@ -84,7 +87,7 @@ Workflows that operate on projects or deals should reference the project-local e
|
|
|
84
87
|
```ts
|
|
85
88
|
import { z } from 'zod'
|
|
86
89
|
import type { WorkflowDefinition } from '@elevasis/sdk'
|
|
87
|
-
import { ProjectSchema } from '@
|
|
90
|
+
import { ProjectSchema } from '@core/types/entities'
|
|
88
91
|
|
|
89
92
|
export const myWorkflow: WorkflowDefinition = {
|
|
90
93
|
id: 'my-workflow',
|
|
@@ -96,16 +99,16 @@ export const myWorkflow: WorkflowDefinition = {
|
|
|
96
99
|
}
|
|
97
100
|
```
|
|
98
101
|
|
|
99
|
-
The `@
|
|
102
|
+
The `@core/*` alias maps to the `core/` workspace package. This keeps entity contracts in one place and ensures the workflow input type matches whatever the UI passes as the execution payload.
|
|
100
103
|
|
|
101
104
|
---
|
|
102
105
|
|
|
103
106
|
## Recipe 4 -- Reference entity types from the UI
|
|
104
107
|
|
|
105
|
-
UI components accept entity types directly in props. The entity type flows from the `
|
|
108
|
+
UI components accept entity types directly in props. The entity type flows from the `core` package through the component into the workflow execution payload:
|
|
106
109
|
|
|
107
110
|
```tsx
|
|
108
|
-
import type { Project } from '@
|
|
111
|
+
import type { Project } from '@core/types/entities'
|
|
109
112
|
|
|
110
113
|
interface ProjectCardProps {
|
|
111
114
|
project: Project
|
|
@@ -127,7 +130,7 @@ When wiring this to a `RunResourceButton`, the entity instance becomes the workf
|
|
|
127
130
|
|
|
128
131
|
## Verification
|
|
129
132
|
|
|
130
|
-
- **
|
|
133
|
+
- **Test core contracts:** `pnpm -C core test` runs the Vitest suite. The template ships `core/types/entities.test.ts` as a working smoke-check -- it `safeParse`s a valid project (expects success) and an invalid one (expects failure). Run this after any schema change.
|
|
131
134
|
- **Round-trip safeParse:** Call `ProjectSchema.safeParse(candidateObject)` in a test or REPL to confirm the Zod shape accepts the data your workflows and UI will produce.
|
|
132
135
|
- **Cross-package type check:** `pnpm -C ui build` will surface any TypeScript errors if a UI component or hook passes a mismatched entity type to a workflow input.
|
|
133
136
|
|