@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.
Files changed (40) hide show
  1. package/dist/cli.cjs +52 -149
  2. package/dist/index.d.ts +468 -198
  3. package/dist/index.js +225 -147
  4. package/dist/test-utils/index.d.ts +272 -99
  5. package/dist/test-utils/index.js +4756 -125
  6. package/dist/types/worker/adapters/llm.d.ts +1 -1
  7. package/dist/worker/index.js +14 -6
  8. package/package.json +2 -2
  9. package/reference/claude-config/rules/agent-start-here.md +14 -14
  10. package/reference/claude-config/skills/configure/SKILL.md +3 -3
  11. package/reference/claude-config/skills/setup/SKILL.md +6 -6
  12. package/reference/claude-config/sync-notes/2026-04-25-auth-role-system-and-settings-roles.md +55 -0
  13. package/reference/claude-config/sync-notes/2026-04-27-crm-hitl-action-layer-cutover.md +101 -0
  14. package/reference/cli.mdx +57 -0
  15. package/reference/deployment/provided-features.mdx +40 -267
  16. package/reference/examples/organization-model.ts +99 -564
  17. package/reference/packages/core/src/organization-model/README.md +102 -97
  18. package/reference/resources/types.mdx +72 -163
  19. package/reference/scaffold/core/organization-graph.mdx +92 -272
  20. package/reference/scaffold/core/organization-model.mdx +155 -320
  21. package/reference/scaffold/index.mdx +3 -0
  22. package/reference/scaffold/operations/propagation-pipeline.md +4 -1
  23. package/reference/scaffold/operations/scaffold-maintenance.md +3 -0
  24. package/reference/scaffold/operations/workflow-recipes.md +13 -10
  25. package/reference/scaffold/recipes/add-a-feature.md +105 -158
  26. package/reference/scaffold/recipes/add-a-resource.md +88 -158
  27. package/reference/scaffold/recipes/customize-organization-model.md +144 -400
  28. package/reference/scaffold/recipes/extend-a-base-entity.md +11 -8
  29. package/reference/scaffold/recipes/gate-by-feature-or-admin.md +117 -158
  30. package/reference/scaffold/recipes/index.md +3 -0
  31. package/reference/scaffold/reference/contracts.md +107 -435
  32. package/reference/scaffold/reference/feature-registry.md +11 -8
  33. package/reference/scaffold/reference/glossary.md +74 -105
  34. package/reference/scaffold/ui/composition-extensibility.mdx +3 -0
  35. package/reference/scaffold/ui/customization.md +3 -0
  36. package/reference/scaffold/ui/feature-flags-and-gating.md +29 -231
  37. package/reference/scaffold/ui/feature-shell.mdx +53 -219
  38. package/reference/scaffold/ui/recipes.md +65 -397
  39. package/reference/claude-config/logs/pre-edit-vibe-gate.log +0 -40
  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 organization model in template-derived projects, covering the unified feature API, surfaces, domain config, and the resolve pipeline.
4
- ---
5
-
6
- # Customize organization-model.ts
7
-
8
- `core/config/organization-model.ts` is the semantic contract between your UI runtime
9
- and your platform operations. Every feature your users can access, every nav surface they can
10
- navigate to, and every resource backed by a workflow is declared here. The provider reads this
11
- contract at startup; everything downstream -- routing, gating, nav rendering -- derives from it.
12
-
13
- This recipe walks through the file section by section. For adding a net-new feature from scratch
14
- (manifest, routes, gating) see [add-a-feature.md](add-a-feature.md).
15
-
16
- ---
17
-
18
- ## Anatomy
19
-
20
- ### Import and setup
21
-
22
- ```ts
23
- import {
24
- defineOrganizationModel,
25
- resolveOrganizationModel,
26
- type OrganizationModel,
27
- type OrganizationModelSurface
28
- } from '@elevasis/core/organization-model'
29
- ```
30
-
31
- Two functions and two types are all you need:
32
-
33
- - `defineOrganizationModel` -- identity helper that gives TypeScript a typed override shape.
34
- Pass your override object through it; you get type-checking against `DeepPartial<OrganizationModel>`
35
- without having to fill every field.
36
- - `resolveOrganizationModel` -- merges your override with the platform defaults and validates the
37
- result through `OrganizationModelSchema`. Call once at module init; export the result.
38
- - `OrganizationModel` -- the fully-resolved model type (after defaults are merged in).
39
- - `OrganizationModelSurface` -- the surface definition type; useful when building the foundation
40
- navigation surface layer (see the export pattern below).
41
-
42
- ---
43
-
44
- ### Features array
45
-
46
- The `features` array is the sole source of truth for which capabilities the organization has
47
- and whether they are on or off. Each entry is an `OrganizationModelFeature` (inferred from
48
- `FeatureSchema`).
49
-
50
- ```ts
51
- const foundationOrganizationModelOverride = defineOrganizationModel({
52
- features: [
53
- {
54
- id: 'crm', // stable kebab-case ID; FeatureModule.featureId must match
55
- label: 'CRM', // display name for nav label resolution
56
- description: 'Relationship pipeline and deal management', // optional; shown in settings UI
57
- enabled: true, // org-wide default; false hides from nav and blocks routes
58
- color: 'blue', // Mantine color token; optional
59
- entityIds: ['crm.deal'], // logical entity types this feature owns
60
- surfaceIds: ['crm.pipeline'], // navigation surfaces that belong to this feature
61
- resourceIds: [], // workflow/agent resource IDs mapped through resourceMappings
62
- capabilityIds: ['crm.pipeline.manage'] // fine-grained capability strings for gating
63
- },
64
- // ... other features
65
- ]
66
- })
67
- ```
68
-
69
- Field reference:
70
-
71
- - `id` -- lowercase, kebab-case, dot-separated segments allowed (`crm`, `lead-gen`, `operations`).
72
- This value must exactly match the `featureId` on any `FeatureModule` manifest that gates against
73
- this feature. The provider throws at startup if a manifest references an unknown feature ID.
74
- - `label` -- shown in nav when no manifest override is provided. Keep short.
75
- - `description` -- optional. Appears in membership/settings surfaces.
76
- - `enabled` -- `false` suppresses the nav entry and triggers `FeatureGuard` redirects. Default is
77
- `true` per the schema. To ship a feature but keep it off by default, set this to `false` here
78
- and enable it per-org through membership config.
79
- - `color` -- any Mantine color token string (`blue`, `cyan`, `violet`, `orange`). Optional.
80
- - `icon` -- icon name string. Optional. The foundation layer re-maps this via `FoundationSurfaceIcon`.
81
- - `entityIds` -- dot-namespaced entity type IDs (`crm.deal`, `leadgen.list`). Purely semantic; used
82
- by the organization graph to classify nodes.
83
- - `surfaceIds` -- must reference IDs declared in `navigation.surfaces`. Bidirectional: the schema
84
- validates that every surface listed here also lists this feature in its own `featureIds`.
85
- - `resourceIds` -- must reference `resourceId` values in `resourceMappings`. Bidirectional: the
86
- schema validates the reverse link exists. Leave empty until you have actual resource mappings.
87
- - `capabilityIds` -- dot-namespaced strings (`crm.pipeline.manage`). Used by `FeatureGuard` and
88
- surface-level access checks. No central registry; name them descriptively.
89
-
90
- The 7 platform defaults are: `crm`, `lead-gen`, `projects`, `operations`, `monitoring`, `settings`,
91
- `seo`. The template's `features` array replaces the defaults wholesale (arrays are not merged field
92
- by field -- see Resolve Behavior below).
93
-
94
- ---
95
-
96
- ### Navigation surfaces
97
-
98
- Surfaces are the navigable pages of your application. Declare them under `navigation.surfaces`.
99
-
100
- ```ts
101
- navigation: {
102
- defaultSurfaceId: 'operations.organization-graph', // surface shown on first load
103
- surfaces: [
104
- {
105
- id: 'crm.pipeline', // stable ID; referenced by feature.surfaceIds and group.surfaceIds
106
- label: 'CRM', // nav label
107
- path: '/crm', // TanStack Router path prefix
108
- surfaceType: 'graph', // 'page' | 'dashboard' | 'graph' | 'detail' | 'list' | 'settings'
109
- featureId: 'crm', // primary owning feature (optional but conventional)
110
- featureIds: ['crm'], // all features this surface belongs to (bidirectional with feature.surfaceIds)
111
- entityIds: ['crm.deal'], // entity types rendered on this surface
112
- resourceIds: [], // resource IDs rendered on this surface (from resourceMappings)
113
- capabilityIds: ['crm.pipeline.manage'] // capabilities required to access this surface
114
- }
115
- ]
116
- }
117
- ```
118
-
119
- Surface field notes:
120
-
121
- - `surfaceType` -- drives how the organization graph classifies and renders the surface node.
122
- Use `graph` for pipeline/topology views, `list` for index pages, `settings` for settings pages.
123
- - `featureId` vs `featureIds` -- `featureId` is the single primary owner (optional). `featureIds`
124
- is the bidirectional array that the schema validates against. In practice, set both to the same
125
- single value for most surfaces.
126
- - `parentId` -- optional. Lets you declare a surface as a child of another (for nested nav or
127
- detail surfaces). Must reference a declared surface ID.
128
- - Bidirectional constraint: if `crm.pipeline` is in `feature.surfaceIds`, then `crm.pipeline`'s
129
- `featureIds` must include `crm`. The schema parse throws if either side is missing.
130
-
131
- The template adds a `FoundationNavigationSurface` extension type that augments the core surface
132
- with a typed `icon` field (`FoundationSurfaceIcon`). This is used by the foundation nav layer to
133
- render icon components in the sidebar.
134
-
135
- ---
136
-
137
- ### Resources and resourceMappings
138
-
139
- Resource mappings connect platform workflows and agents to features and surfaces. They are optional
140
- until you have deployed resources to map.
141
-
142
- ```ts
143
- resourceMappings: [
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 `foundations/types/entities.ts` of any scaffold project.
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 `foundations/types/entities.ts`. This is the primary pattern -- the scaffold template ships this exact example.
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 `foundations/`, `operations/`, and `ui/`.
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 '@foundation/types/entities'
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 `@foundation/*` alias maps to the `foundations/` workspace package. This keeps entity contracts in one place and ensures the workflow input type matches whatever the UI passes as the execution payload.
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 `foundations` package through the component into the workflow execution payload:
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 '@foundation/types/entities'
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
- - **Type-check foundations:** `pnpm -C foundations test` runs the Vitest suite. The template ships `foundations/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.
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