@elevasis/core 0.10.0 → 0.11.1
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/index.d.ts +69 -159
- package/dist/index.js +324 -613
- package/dist/organization-model/index.d.ts +69 -159
- package/dist/organization-model/index.js +324 -613
- package/dist/test-utils/index.d.ts +192 -45
- package/dist/test-utils/index.js +260 -600
- package/package.json +1 -1
- package/src/__tests__/template-core-compatibility.test.ts +73 -91
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +94 -182
- package/src/auth/multi-tenancy/index.ts +20 -17
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +142 -126
- package/src/auth/multi-tenancy/memberships/index.ts +26 -22
- package/src/auth/multi-tenancy/permissions.test.ts +42 -0
- package/src/auth/multi-tenancy/permissions.ts +104 -0
- package/src/organization-model/README.md +102 -97
- package/src/organization-model/__tests__/defaults.test.ts +19 -6
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +24 -93
- package/src/organization-model/__tests__/graph.test.ts +82 -894
- package/src/organization-model/__tests__/resolve.test.ts +59 -690
- package/src/organization-model/__tests__/schema.test.ts +83 -407
- package/src/organization-model/contracts.ts +4 -3
- package/src/organization-model/defaults.ts +277 -141
- package/src/organization-model/domains/features.ts +31 -22
- package/src/organization-model/domains/navigation.ts +27 -20
- package/src/organization-model/foundation.ts +42 -54
- package/src/organization-model/graph/build.ts +42 -217
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/link.ts +10 -0
- package/src/organization-model/graph/schema.ts +21 -16
- package/src/organization-model/graph/types.ts +10 -10
- package/src/organization-model/helpers.ts +74 -0
- package/src/organization-model/index.ts +7 -7
- package/src/organization-model/organization-graph.mdx +89 -272
- package/src/organization-model/organization-model.mdx +152 -320
- package/src/organization-model/published.ts +20 -19
- package/src/organization-model/resolve.ts +8 -33
- package/src/organization-model/schema.ts +63 -205
- package/src/organization-model/types.ts +12 -11
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/registry/__tests__/command-view.test.ts +6 -5
- package/src/platform/registry/__tests__/resource-link.test.ts +30 -0
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +15 -15
- package/src/platform/registry/command-view.ts +10 -12
- package/src/platform/registry/index.ts +93 -93
- package/src/platform/registry/resource-link.ts +32 -0
- package/src/platform/registry/resource-registry.ts +917 -876
- package/src/platform/registry/serialization.ts +56 -73
- package/src/platform/registry/serialized-types.ts +17 -12
- package/src/platform/registry/types.ts +14 -43
- package/src/reference/_generated/contracts.md +94 -182
- package/src/reference/glossary.md +71 -105
- package/src/scaffold-registry/__tests__/index.test.ts +125 -1
- package/src/scaffold-registry/__tests__/schema.test.ts +48 -20
- package/src/scaffold-registry/index.ts +236 -188
- package/src/scaffold-registry/schema.ts +47 -22
- package/src/supabase/database.types.ts +2880 -2719
- package/src/test-utils/fixtures/memberships.ts +82 -80
- package/src/platform/registry/domains.ts +0 -165
|
@@ -1,320 +1,152 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Organization Model
|
|
3
|
-
description: Organization OS Model layer documentation for the
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
The model is authored in `@repo/core` and published
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- `
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- `packages/core/src/
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
- `'primary'` -- main workspace or operations nav rail
|
|
154
|
-
- `'secondary'` -- secondary grouping (below primary)
|
|
155
|
-
- `'bottom'` -- pinned to the bottom of the nav rail
|
|
156
|
-
|
|
157
|
-
The default groups (`primary-workspace`, `primary-operations`) both use `placement: 'primary'`.
|
|
158
|
-
|
|
159
|
-
### Feature-Specific Semantics
|
|
160
|
-
|
|
161
|
-
Three renamed top-level domain fields carry feature-specific semantic shapes (pipeline stages, lifecycle stages, project statuses). These are named fields on `OrganizationModel`, not embedded in per-feature config:
|
|
162
|
-
|
|
163
|
-
- `sales` -- pipeline stages and stage semantics (formerly `crm`)
|
|
164
|
-
- `prospecting` -- company and contact lifecycle stages (formerly `leadGen`)
|
|
165
|
-
- `projects` -- project, milestone, and task statuses (formerly `delivery`)
|
|
166
|
-
|
|
167
|
-
This is why the organization model is semantic, not just nav config -- it owns product meaning for the business objects the shell surfaces expose.
|
|
168
|
-
|
|
169
|
-
### Reality Domains (New in 2026-04-20 Expansion)
|
|
170
|
-
|
|
171
|
-
Five new domains capture organizational reality that was absent from the original model. They sit alongside the existing platform-configuration domains and follow the same `domains/*.ts` pattern:
|
|
172
|
-
|
|
173
|
-
**`identity`** -- legal identity distinct from `branding` (which is display identity). Fields: `mission`, `vision`, `legalName`, `entityType`, `jurisdiction`, `industryCategory`, `geographicFocus`, `timeZone`, `businessHours`. All fields default to empty strings; `timeZone` defaults to `'UTC'`; `businessHours` defaults to `{}`.
|
|
174
|
-
|
|
175
|
-
**`customers`** -- customer segments. Each `CustomerSegment` entry has: `id`, `name`, `description`, `jobsToBeDone`, `pains`, `gains`, `valueProp` (all plain-language strings), and `firmographics` (optional object with `companySize`, `industry`, `revenue`, `geography`). Domain defaults to `{ segments: [] }`.
|
|
176
|
-
|
|
177
|
-
**`offerings`** -- products and services. Each `Product` entry has: `id`, `name`, `description`, `pricingModel` (enum: `'one-time' | 'subscription' | 'usage-based' | 'custom'`), `price` (optional number), `currency` (optional string), `targetSegmentIds` (cross-ref validated against `customers.segments[].id`), `deliveryFeatureId` (optional cross-ref validated against `features[].id`). Domain defaults to `{ products: [] }`.
|
|
178
|
-
|
|
179
|
-
**`roles`** -- role chart. Each `Role` entry has: `id`, `title`, `responsibilities` (string array, default `[]`), `reportsToId` (optional, cross-ref validated within the same collection), `heldBy` (optional name or email string). All field names are plain-language; no EOS jargon (`seats`, `accountabilities`) survives. Domain defaults to `{ roles: [] }`.
|
|
180
|
-
|
|
181
|
-
**`goals`** -- organizational goals. Each `Objective` entry has: `id`, `description`, `periodStart` (ISO 8601 date), `periodEnd` (ISO 8601 date, must be strictly after `periodStart`), `keyResults` (array of `{ id, description, targetValue?, currentValue?, unit? }`). User-facing text uses "goals" and "measurable outcomes" -- never "OKR" or "key results". Domain defaults to `{ objectives: [] }`.
|
|
182
|
-
|
|
183
|
-
### Statuses and Operations Domains (Vibe Layer)
|
|
184
|
-
|
|
185
|
-
Two domains support the ambient vibe layer's plain-language rendering:
|
|
186
|
-
|
|
187
|
-
**`statuses`** -- flat registry of `StatusEntry` objects. Each entry: `id`, `label`, `semanticClass` (one of `'delivery.task' | 'delivery.project' | 'delivery.milestone' | 'queue' | 'execution' | 'schedule' | 'schedule.run' | 'request'`), optional `category`. Labels are vibe-readable: the ambient classifier narrates state using `label` from the model, never hardcoded strings. `QueueTaskStatus` in the queue domain routes through this registry. Defaults to a seed set covering all semantic classes.
|
|
188
|
-
|
|
189
|
-
**`operations`** -- catalog of stateful runtime entities. Each `OperationEntry`: `id`, `label`, `semanticClass` (one of `'queue' | 'executions' | 'sessions' | 'notifications' | 'schedules'`), optional `featureId`, optional `supportedStatusSemanticClass` (ties an operation entry back to which status semantic classes apply). Default entries: `operations.queue` (HITL queue), `operations.executions`, `operations.sessions`, `operations.notifications`, `operations.schedules`.
|
|
190
|
-
|
|
191
|
-
## Authoring & Resolution
|
|
192
|
-
|
|
193
|
-
- `defineOrganizationModel()` -- typed authoring helper; returns the input unchanged but constrains the type.
|
|
194
|
-
- `resolveOrganizationModel(partial)` -- deep-merges a partial override into `DEFAULT_ORGANIZATION_MODEL` and validates the result against `OrganizationModelSchema`.
|
|
195
|
-
|
|
196
|
-
Merge semantics:
|
|
197
|
-
|
|
198
|
-
- plain objects merge recursively
|
|
199
|
-
- arrays **replace** defaults (no element-by-element merge)
|
|
200
|
-
- if `navigation.surfaces` is replaced without also supplying `navigation.groups`, inherited groups are normalized so they only keep still-valid surface IDs and any now-empty groups are dropped
|
|
201
|
-
- missing fields fall back to defaults
|
|
202
|
-
|
|
203
|
-
### Referential Integrity
|
|
204
|
-
|
|
205
|
-
`OrganizationModelSchema` validates more than top-level shape. The `superRefine` pass enforces:
|
|
206
|
-
|
|
207
|
-
**Uniqueness checks** -- IDs must be unique within their respective collections:
|
|
208
|
-
|
|
209
|
-
- `features[].id`
|
|
210
|
-
- `navigation.surfaces[].id`
|
|
211
|
-
- `navigation.groups[].id`
|
|
212
|
-
- `resourceMappings[].id`
|
|
213
|
-
- `resourceMappings[].resourceId` (separate uniqueness check -- two mappings cannot share a `resourceId`)
|
|
214
|
-
|
|
215
|
-
**Dangling reference detection** -- every cross-reference must resolve:
|
|
216
|
-
|
|
217
|
-
- `navigation.defaultSurfaceId` must point at a declared surface
|
|
218
|
-
- every `surfaceId` in a navigation group must resolve to a declared surface
|
|
219
|
-
- every `surfaceId` in a feature must resolve to a declared surface
|
|
220
|
-
- every `resourceId` in a feature must resolve to a declared resource mapping (by `resourceId`)
|
|
221
|
-
- surface `parentId` must reference an existing surface
|
|
222
|
-
- every `featureId` on a surface must resolve to a declared feature
|
|
223
|
-
- every `resourceId` on a surface must resolve to a declared resource mapping
|
|
224
|
-
- every `featureId` on a resource mapping must resolve to a declared feature
|
|
225
|
-
- every `surfaceId` on a resource mapping must resolve to a declared surface
|
|
226
|
-
|
|
227
|
-
**Bidirectional reference enforcement** -- cross-references must be mutual, not one-sided:
|
|
228
|
-
|
|
229
|
-
- a feature listing `surfaceId` S requires surface S to list that feature's ID in its `featureIds`
|
|
230
|
-
- a surface listing `featureId` F requires feature F to list that surface's ID in its `surfaceIds`
|
|
231
|
-
- a feature listing `resourceId` R requires resource mapping R to list that feature's ID in its `featureIds`
|
|
232
|
-
- a resource mapping listing `featureId` F requires feature F to list that resource's `resourceId` in its `resourceIds`
|
|
233
|
-
- a surface listing `resourceId` R requires resource mapping R to list that surface's ID in its `surfaceIds`
|
|
234
|
-
- a resource mapping listing `surfaceId` S requires surface S to list that resource's `resourceId` in its `resourceIds`
|
|
235
|
-
|
|
236
|
-
This bidirectional enforcement is what keeps the organization model semantically consistent rather than loosely structured config.
|
|
237
|
-
|
|
238
|
-
**Reality domain cross-refs** (validated in the same `superRefine` pass):
|
|
239
|
-
|
|
240
|
-
- each `offerings.products[].targetSegmentIds[]` must resolve to a `customers.segments[].id`
|
|
241
|
-
- each `offerings.products[].deliveryFeatureId` (when present) must resolve to a `features[].id`
|
|
242
|
-
- each `roles.roles[].reportsToId` (when present) must resolve to another `roles.roles[].id` in the same collection
|
|
243
|
-
- each `goals.objectives[].periodEnd` must be strictly after `periodStart` (ISO 8601 string comparison)
|
|
244
|
-
|
|
245
|
-
## Provider Integration
|
|
246
|
-
|
|
247
|
-
`ElevasisFeaturesProvider` uses the organization model in three ways:
|
|
248
|
-
|
|
249
|
-
1. **Feature resolution** -- the provider looks up each manifest's `featureId` in `organizationModel.features` to determine `access.enabled`. If no matching feature entry is found, `access.enabled` defaults to `false`.
|
|
250
|
-
2. **Nav label and path resolution** -- when `organizationModel` is present, feature labels resolve from the matching feature entry's `label` field, and surface labels and paths resolve from `organizationModel.navigation.surfaces`. Semantic customization without changing manifest code.
|
|
251
|
-
3. **Organization-graph bridge** -- the `operationsManifest` declares `organizationGraph.surfaceId = 'operations.organization-graph'`. The provider resolves that against organization-model surfaces and exposes the result as `organizationGraph` in context. See [Organization Graph](./organization-graph.mdx).
|
|
252
|
-
|
|
253
|
-
## Published Package: `@elevasis/core`
|
|
254
|
-
|
|
255
|
-
`@elevasis/core` is a curated publish wrapper around `packages/core`, released through `$sdk release-core`. It exists so external foundations (`_template/foundations`, `nirvana-marketing/foundations`, `ZentaraHQ/foundations`) can depend on a stable, browser-safe organization-model contract without reaching into monorepo-only `@repo/core`.
|
|
256
|
-
|
|
257
|
-
### Published surface
|
|
258
|
-
|
|
259
|
-
Intentionally narrow:
|
|
260
|
-
|
|
261
|
-
- `.` -- curated root barrel (`packages/core/src/published.ts`)
|
|
262
|
-
- `./organization-model` -- curated organization-model barrel (`packages/core/src/organization-model/published.ts`)
|
|
263
|
-
|
|
264
|
-
Exports on the first published surface:
|
|
265
|
-
|
|
266
|
-
- `OrganizationModelSchema`
|
|
267
|
-
- top-level organization-model types
|
|
268
|
-
- `DEFAULT_ORGANIZATION_MODEL`
|
|
269
|
-
- `defineOrganizationModel`
|
|
270
|
-
- `resolveOrganizationModel`
|
|
271
|
-
|
|
272
|
-
### Intentionally excluded
|
|
273
|
-
|
|
274
|
-
- wider `@repo/core` browser barrel
|
|
275
|
-
- organization-graph types/builder (`buildOrganizationGraph`, DTO types) -- graph work remains monorepo-first; external consumers should not assume graph contracts are published
|
|
276
|
-
|
|
277
|
-
### Build
|
|
278
|
-
|
|
279
|
-
- `pnpm --filter @repo/core build:publish` emits `dist/index.{js,d.ts}` and `dist/organization-model/index.{js,d.ts}` via tsup + `rollup-plugin-dts`.
|
|
280
|
-
- Shipped types are browser-safe and verified to contain no `@repo/*` imports.
|
|
281
|
-
|
|
282
|
-
### Release
|
|
283
|
-
|
|
284
|
-
- `.claude/sub-commands/sdk/release-core.md` -- dedicated publish flow
|
|
285
|
-
- `$sdk verify` -- covers publish metadata, bundled declarations, and the first template-foundation compatibility guard
|
|
286
|
-
|
|
287
|
-
## Downstream Adoption
|
|
288
|
-
|
|
289
|
-
External foundations consume `@elevasis/core/organization-model` via an **adapter**, not a fork. The adapter pattern in `external/_template/foundations/config/organization-model.ts`:
|
|
290
|
-
|
|
291
|
-
1. Define a canonical override with `defineOrganizationModel(...)`.
|
|
292
|
-
2. Resolve it with `resolveOrganizationModel(...)`.
|
|
293
|
-
3. Adapt the result into a template-local helper shape that adds consumer-only fields (`homeLabel`, `quickAccessSurfaceIds`, icon aliases, `getOrganizationSurface()`).
|
|
294
|
-
|
|
295
|
-
Exports the foundations module provides to consumers:
|
|
296
|
-
|
|
297
|
-
- `canonicalOrganizationModel` -- passed to `ElevasisFeaturesProvider`
|
|
298
|
-
- `organizationModel` -- widened adapter shape for template-local helpers
|
|
299
|
-
- `FoundationFeatureKey`, `FoundationSurfaceIcon`, `FoundationNavigationSurface`, `FoundationOrganizationModel`
|
|
300
|
-
- `homeLabel`, `quickAccessSurfaceIds`, `getOrganizationSurface(surfaceId)`
|
|
301
|
-
|
|
302
|
-
Downstream template shells pass `canonicalOrganizationModel` into `ElevasisFeaturesProvider`, preserving host-local dashboard/nav customizations alongside the shared runtime. Feature IDs are direct matches -- `crm`, `lead-gen`, `projects` in the org model correspond directly to the same IDs on `FeatureModule.featureId`. No alias layer is needed. The domain rename wave (crm→sales, leadGen→prospecting, delivery→projects) affects the schema field names, not the feature ID constants; adapters using `CRM_FEATURE_ID`, `LEAD_GEN_FEATURE_ID`, etc. require no changes.
|
|
303
|
-
|
|
304
|
-
Adapters that override the new reality domains (`identity`, `customers`, `offerings`, `roles`, `goals`) or add `techStack` metadata to resource mappings should use `/configure` as the structured entry point for those edits in external projects. `/configure` runs a layered QA flow, proposes changes, and gates the write through `resolveOrganizationModel()` + `OrganizationModelSchema.parse()` before committing.
|
|
305
|
-
|
|
306
|
-
Derivative projects (`external/nirvana-marketing`, `external/ZentaraHQ`) follow the same adapter + provider-wiring baseline with their own project-local customizations.
|
|
307
|
-
|
|
308
|
-
## Verification
|
|
309
|
-
|
|
310
|
-
- `pnpm --filter @repo/core build:publish` -- published bundle
|
|
311
|
-
- `pnpm --filter @repo/core test -- publish template-foundations-compatibility` -- published surface + adapter guard
|
|
312
|
-
- `pnpm -C external/_template/foundations test` -- foundations adapter and import-boundary guard
|
|
313
|
-
- `pnpm -C external/_template/ui test -- src/routes/__tests__/__root.test.tsx` -- provider-level `organizationModel` wiring in the template shell
|
|
314
|
-
|
|
315
|
-
## Design Constraints
|
|
316
|
-
|
|
317
|
-
- `@elevasis/core` must stay browser-safe. Build output inlines or eliminates `@repo/*` references from shipped types.
|
|
318
|
-
- The published surface stays curated -- do not reflexively re-export new `@repo/core` internals. Widening the surface is a deliberate decision, not a default.
|
|
319
|
-
- Organization-graph helpers remain internal until a concrete external consumer needs them.
|
|
320
|
-
- Foundations adapters preserve the semantic contract; they do not fork it. If a template needs a concept the canonical model can't express, extend the canonical model first.
|
|
1
|
+
---
|
|
2
|
+
title: Organization Model
|
|
3
|
+
description: Organization OS Model layer documentation for the flat feature hierarchy, semantic domains, resource graph links, and curated @elevasis/core public API.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
The organization model is the semantic contract that maps an organization's product shape to flat feature hierarchy, business semantics, shell navigation, and graph binding. It is schema-first, versioned, and validated.
|
|
9
|
+
|
|
10
|
+
The model is authored in `@repo/core` and published through the curated `@elevasis/core/organization-model` surface.
|
|
11
|
+
|
|
12
|
+
## Source of Truth
|
|
13
|
+
|
|
14
|
+
- `packages/core/src/organization-model/schema.ts`
|
|
15
|
+
- `packages/core/src/organization-model/types.ts`
|
|
16
|
+
- `packages/core/src/organization-model/defaults.ts`
|
|
17
|
+
- `packages/core/src/organization-model/helpers.ts`
|
|
18
|
+
- `packages/core/src/organization-model/graph/link.ts`
|
|
19
|
+
- `packages/core/src/organization-model/published.ts`
|
|
20
|
+
- `packages/core/src/__tests__/template-core-compatibility.test.ts`
|
|
21
|
+
|
|
22
|
+
## Contract Shape
|
|
23
|
+
|
|
24
|
+
Top-level fields on `OrganizationModel`:
|
|
25
|
+
|
|
26
|
+
- `version`
|
|
27
|
+
- `features`
|
|
28
|
+
- `branding`
|
|
29
|
+
- `navigation`
|
|
30
|
+
- `sales`
|
|
31
|
+
- `prospecting`
|
|
32
|
+
- `projects`
|
|
33
|
+
- `identity`
|
|
34
|
+
- `customers`
|
|
35
|
+
- `offerings`
|
|
36
|
+
- `roles`
|
|
37
|
+
- `goals`
|
|
38
|
+
- `statuses`
|
|
39
|
+
- `operations`
|
|
40
|
+
|
|
41
|
+
Resources are not authored inside `OrganizationModel`. Deployable workflows, agents, triggers, integrations, external resources, and human checkpoints declare graph links on their resource metadata.
|
|
42
|
+
|
|
43
|
+
## Feature Shape
|
|
44
|
+
|
|
45
|
+
`OrganizationModel.features` is a flat array. Hierarchy is derived from dotted IDs:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
features: [
|
|
49
|
+
{ id: 'dashboard', label: 'Dashboard', enabled: true, path: '/', uiPosition: 'sidebar-primary' },
|
|
50
|
+
{ id: 'sales', label: 'Sales', enabled: true, uiPosition: 'sidebar-primary' },
|
|
51
|
+
{ id: 'sales.crm', label: 'CRM', enabled: true, path: '/crm' },
|
|
52
|
+
{ id: 'operations.resources', label: 'Resources', enabled: true, path: '/operations/resources' }
|
|
53
|
+
]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Authored fields:
|
|
57
|
+
|
|
58
|
+
- `id`
|
|
59
|
+
- `label`
|
|
60
|
+
- `description`
|
|
61
|
+
- `enabled`
|
|
62
|
+
- `path`
|
|
63
|
+
- `icon`
|
|
64
|
+
- `color`
|
|
65
|
+
- `uiPosition`
|
|
66
|
+
- `requiresAdmin`
|
|
67
|
+
- `devOnly`
|
|
68
|
+
|
|
69
|
+
Containers omit `path`; leaves provide `path`. `uiPosition`, `requiresAdmin`, and `devOnly` inherit from ancestors.
|
|
70
|
+
Development-only features remain defined and enabled with `devOnly: true`; shell consumers hide those entries and route paths outside development mode while preserving the semantic contract.
|
|
71
|
+
|
|
72
|
+
Navigation surfaces may also carry `devOnly` for compatibility with the legacy/domain navigation model. `operations.command-view` is intentionally development-only while its graph visualization modes continue to mature.
|
|
73
|
+
|
|
74
|
+
## Graph IDs and Resource Links
|
|
75
|
+
|
|
76
|
+
Cross-collection links use kind-prefixed graph IDs:
|
|
77
|
+
|
|
78
|
+
- `feature:sales.crm`
|
|
79
|
+
- `integration:instantly`
|
|
80
|
+
- `resource:lead-import`
|
|
81
|
+
- `capability:operations.queue.review`
|
|
82
|
+
|
|
83
|
+
Example resource metadata:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
config: {
|
|
87
|
+
resourceId: 'lead-import',
|
|
88
|
+
name: 'Lead Import',
|
|
89
|
+
type: 'workflow',
|
|
90
|
+
version: '1.0.0',
|
|
91
|
+
status: 'prod',
|
|
92
|
+
links: [{ nodeId: 'feature:sales.lead-gen', kind: 'operates-on' }],
|
|
93
|
+
category: 'production'
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Domain Semantics
|
|
98
|
+
|
|
99
|
+
The model keeps business semantics in named domains:
|
|
100
|
+
|
|
101
|
+
- `sales` -- pipeline stages and stage semantics
|
|
102
|
+
- `prospecting` -- company/contact lifecycle stages
|
|
103
|
+
- `projects` -- project, milestone, and task statuses
|
|
104
|
+
- `identity`, `customers`, `offerings`, `roles`, `goals` -- organizational reality
|
|
105
|
+
- `statuses` and `operations` -- runtime/vibe-layer semantic registries
|
|
106
|
+
|
|
107
|
+
## Authoring and Resolution
|
|
108
|
+
|
|
109
|
+
- `defineOrganizationModel()` is a typed authoring helper.
|
|
110
|
+
- `resolveOrganizationModel(partial)` deep-merges a partial override into `DEFAULT_ORGANIZATION_MODEL` and validates the result.
|
|
111
|
+
- `createFoundationOrganizationModel(partial)` resolves the canonical model and returns template-facing helpers.
|
|
112
|
+
- Plain objects merge recursively.
|
|
113
|
+
- Arrays replace defaults.
|
|
114
|
+
|
|
115
|
+
## Referential Integrity
|
|
116
|
+
|
|
117
|
+
`OrganizationModelSchema` validates:
|
|
118
|
+
|
|
119
|
+
- unique feature IDs
|
|
120
|
+
- ancestor existence for dotted feature IDs
|
|
121
|
+
- container/leaf path rules
|
|
122
|
+
- valid sidebar positions
|
|
123
|
+
- reality-domain cross references such as offering target segments and role reporting lines
|
|
124
|
+
|
|
125
|
+
Resource graph links are validated during resource registry registration because resources live in deployment specs.
|
|
126
|
+
|
|
127
|
+
## Provider Integration
|
|
128
|
+
|
|
129
|
+
`ElevasisFeaturesProvider` uses the organization model to:
|
|
130
|
+
|
|
131
|
+
1. validate each manifest's `featureId`
|
|
132
|
+
2. resolve effective feature access
|
|
133
|
+
3. derive sidebar entries from `features`
|
|
134
|
+
4. expose shell helpers such as `childrenOf`, `ancestorsOf`, and `findByPath`
|
|
135
|
+
5. bridge the operations graph into shared UI
|
|
136
|
+
|
|
137
|
+
## Published Package
|
|
138
|
+
|
|
139
|
+
`@elevasis/core` exposes a curated browser-safe surface:
|
|
140
|
+
|
|
141
|
+
- root barrel
|
|
142
|
+
- `./organization-model`
|
|
143
|
+
|
|
144
|
+
The organization graph builder remains monorepo-internal until there is a concrete external contract need.
|
|
145
|
+
|
|
146
|
+
## Verification
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pnpm --filter @repo/core test -- organization-model
|
|
150
|
+
pnpm --filter @repo/core build:publish
|
|
151
|
+
pnpm -C external/_template/core test
|
|
152
|
+
```
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { OrganizationModelSchema } from './schema'
|
|
2
|
-
export { FeatureSchema } from './domains/features'
|
|
2
|
+
export { FeatureSchema, NodeIdPathSchema, NodeIdStringSchema, UiPositionSchema } from './domains/features'
|
|
3
|
+
export { LinkSchema } from './graph/link'
|
|
3
4
|
export { TechStackEntrySchema } from './domains/shared'
|
|
4
5
|
export {
|
|
5
6
|
PROJECTS_FEATURE_ID,
|
|
@@ -11,10 +12,11 @@ export {
|
|
|
11
12
|
MONITORING_FEATURE_ID,
|
|
12
13
|
SETTINGS_FEATURE_ID,
|
|
13
14
|
SEO_FEATURE_ID,
|
|
14
|
-
SALES_PIPELINE_SURFACE_ID,
|
|
15
|
-
PROSPECTING_LISTS_SURFACE_ID,
|
|
16
|
-
OPERATIONS_ORGANIZATION_GRAPH_SURFACE_ID
|
|
17
|
-
|
|
15
|
+
SALES_PIPELINE_SURFACE_ID,
|
|
16
|
+
PROSPECTING_LISTS_SURFACE_ID,
|
|
17
|
+
OPERATIONS_ORGANIZATION_GRAPH_SURFACE_ID,
|
|
18
|
+
OPERATIONS_COMMAND_VIEW_SURFACE_ID
|
|
19
|
+
} from './contracts'
|
|
18
20
|
export { DEFAULT_ORGANIZATION_MODEL } from './defaults'
|
|
19
21
|
export {
|
|
20
22
|
DEFAULT_ORGANIZATION_MODEL_STATUSES,
|
|
@@ -55,26 +57,25 @@ export type {
|
|
|
55
57
|
OrganizationModelCustomerFirmographics,
|
|
56
58
|
OrganizationModelCustomers,
|
|
57
59
|
OrganizationModelCustomerSegment,
|
|
58
|
-
OrganizationModelFeature,
|
|
59
|
-
OrganizationModelGoals,
|
|
60
|
-
OrganizationModelKeyResult,
|
|
61
|
-
|
|
62
|
-
OrganizationModelObjective,
|
|
60
|
+
OrganizationModelFeature,
|
|
61
|
+
OrganizationModelGoals,
|
|
62
|
+
OrganizationModelKeyResult,
|
|
63
|
+
OrganizationModelObjective,
|
|
63
64
|
OrganizationModelOfferings,
|
|
64
65
|
OrganizationModelOperationEntry,
|
|
65
66
|
OrganizationModelOperationSemanticClass,
|
|
66
67
|
OrganizationModelOperations,
|
|
67
|
-
OrganizationModelPricingModel,
|
|
68
|
-
OrganizationModelProduct,
|
|
69
|
-
|
|
70
|
-
OrganizationModelRole,
|
|
68
|
+
OrganizationModelPricingModel,
|
|
69
|
+
OrganizationModelProduct,
|
|
70
|
+
OrganizationModelRole,
|
|
71
71
|
OrganizationModelTechStackEntry,
|
|
72
72
|
OrganizationModelRoles,
|
|
73
|
-
OrganizationModelStatuses,
|
|
74
|
-
OrganizationModelStatusEntry,
|
|
75
|
-
OrganizationModelStatusSemanticClass,
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
OrganizationModelStatuses,
|
|
74
|
+
OrganizationModelStatusEntry,
|
|
75
|
+
OrganizationModelStatusSemanticClass,
|
|
76
|
+
NodeIdPath,
|
|
77
|
+
NodeIdString
|
|
78
|
+
} from './types'
|
|
78
79
|
|
|
79
80
|
export type {
|
|
80
81
|
FoundationBranding,
|
|
@@ -31,36 +31,11 @@ function deepMerge<T>(base: T, override: DeepPartial<T> | undefined): T {
|
|
|
31
31
|
return result as T
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function
|
|
35
|
-
model
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const validSurfaceIds = new Set(model.navigation.surfaces.map((surface) => surface.id))
|
|
43
|
-
const groups = model.navigation.groups
|
|
44
|
-
.map((group) => ({
|
|
45
|
-
...group,
|
|
46
|
-
surfaceIds: group.surfaceIds.filter((surfaceId) => validSurfaceIds.has(surfaceId))
|
|
47
|
-
}))
|
|
48
|
-
.filter((group) => group.surfaceIds.length > 0)
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
...model,
|
|
52
|
-
navigation: {
|
|
53
|
-
...model.navigation,
|
|
54
|
-
groups
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function defineOrganizationModel<T extends DeepPartial<OrganizationModel>>(model: T): T {
|
|
60
|
-
return model
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function resolveOrganizationModel(override?: DeepPartial<OrganizationModel>): OrganizationModel {
|
|
64
|
-
const merged = normalizeInheritedNavigationGroups(deepMerge(DEFAULT_ORGANIZATION_MODEL, override), override)
|
|
65
|
-
return OrganizationModelSchema.parse(merged)
|
|
66
|
-
}
|
|
34
|
+
export function defineOrganizationModel<T extends DeepPartial<OrganizationModel>>(model: T): T {
|
|
35
|
+
return model
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function resolveOrganizationModel(override?: DeepPartial<OrganizationModel>): OrganizationModel {
|
|
39
|
+
const merged = deepMerge(DEFAULT_ORGANIZATION_MODEL, override)
|
|
40
|
+
return OrganizationModelSchema.parse(merged)
|
|
41
|
+
}
|