@elevasis/sdk 1.46.0 → 1.48.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 +816 -107
- package/dist/index.d.ts +166 -2
- package/dist/index.js +23 -7
- package/dist/node/index.d.ts +2 -2
- package/dist/test-utils/index.d.ts +166 -2
- package/dist/test-utils/index.js +48 -9
- package/dist/worker/index.d.ts +213 -10
- package/dist/worker/index.js +14 -2
- package/package.json +2 -2
- package/reference/_navigation.md +5 -4
- package/reference/_reference-manifest.json +11 -1
- package/reference/packages/core/src/content/README.md +15 -10
- package/reference/packages/core/src/organization-model/README.md +9 -6
- package/reference/rules/content.md +27 -0
- package/reference/rules/organization-model.md +9 -3
- package/reference/rules/organization-os.md +2 -2
- package/reference/rules/vibe-intents.md +18 -15
- package/reference/rules/vibe.md +12 -12
- package/reference/scaffold/recipes/add-a-feature.md +1 -1
- package/reference/scaffold/recipes/customize-organization-model.md +2 -2
- package/reference/scaffold/recipes/extend-content.md +167 -68
- package/reference/scaffold/reference/glossary.md +2 -2
- package/reference/scaffold/reference/system-interface-capabilities.md +26 -6
- package/reference/sdk/cli-management.mdx +175 -29
- package/reference/sdk/platform-tools/adapters-platform.mdx +5 -5
- package/reference/sdk/platform-tools/index.mdx +0 -2
- package/reference/sdk/resources/patterns.mdx +14 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Build and Extend Content
|
|
3
|
-
description: Map the content platform primitives available to SDK projects: pipeline and step catalogs, shared review/pipeline/distribution pages, data hooks, the content workflow adapter,
|
|
3
|
+
description: Map the content platform primitives available to SDK projects: pipeline and step catalogs, shared review/pipeline/distribution pages, data hooks, the content workflow adapter, and org-model extension boundaries.
|
|
4
4
|
---
|
|
5
5
|
<!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
|
|
6
6
|
<!-- Regenerate: pnpm scaffold:sync -->
|
|
@@ -15,16 +15,16 @@ Good trigger phrases:
|
|
|
15
15
|
- "Add a content pipeline for our podcast."
|
|
16
16
|
- "Change the review screen for clip proposals."
|
|
17
17
|
- "Write a workflow that produces the next step of a content item."
|
|
18
|
-
- "Store our style guide so producers read the same rules."
|
|
19
18
|
- "Track where each piece got published."
|
|
20
19
|
|
|
21
20
|
Content is a layered platform surface, not one component:
|
|
22
21
|
|
|
23
22
|
- **Organization OS:** pipelines, steps, statuses, pillars, and platforms are **catalogs** in the content System's ontology, not hardcoded enums. `content.config.defaultPipelineId` names the pipeline the UI opens by default.
|
|
24
|
-
- **Shared UI:** overview, items list, item review, pipelines, pipeline workspace,
|
|
25
|
-
- **Headless hooks:** item, attempt, distribution,
|
|
26
|
-
- **Workflow adapter:** `content` from `@elevasis/sdk/worker` lets workflows create items, record attempts, and open distributions through platform tools.
|
|
27
|
-
- **
|
|
23
|
+
- **Shared UI:** overview, items list, item review, pipelines, pipeline workspace, and distribution pages live in `@elevasis/ui/features/content`.
|
|
24
|
+
- **Headless hooks:** item, attempt, distribution, and pipeline-summary hooks live under `@elevasis/ui/hooks`.
|
|
25
|
+
- **Workflow adapter:** `content` from `@elevasis/sdk/worker` lets workflows create items, record and patch attempts, manage source assets, and open distributions through platform tools.
|
|
26
|
+
- **Operator surface:** `elevasis-sdk content:*` is the read and review surface for humans and agents outside a session -- list items, inspect one, see what is waiting on a `queued` review gate, read a pipeline's step contract, and clear a gate. See `apps/docs/content/docs/sdk/sdk/cli-management.mdx` for the full command reference.
|
|
27
|
+
- **Producer instructions live in the prompt.** Content has no governing-document tier. Voice, constraints, and style belong in the generating workflow's own prompt, where they behave identically and are versioned with the code that reads them. The removed tier had no producer and no consumer — nothing called `getActive` and nothing wrote back measured performance.
|
|
28
28
|
- **System API Interface:** `content` uses the flat `system.apiInterface` marker. Readiness is derived from scoped resources, ontology bindings, and required catalogs — `content:object/item` plus the pipeline, step, and status catalogs.
|
|
29
29
|
|
|
30
30
|
## The one thing to understand first: a pipeline is data
|
|
@@ -43,29 +43,31 @@ A pipeline's step catalog is named `content:catalog/{pipelineId}-steps` and refe
|
|
|
43
43
|
|
|
44
44
|
## Decision Table
|
|
45
45
|
|
|
46
|
-
| User wants | Start here
|
|
47
|
-
| ------------------------------------------------- |
|
|
48
|
-
| Add a pipeline, step, status, pillar, or platform | `core/config/organization-model.ts` — the content System's `ontology`
|
|
49
|
-
| Produce the next step of an item from a workflow | `content` from `@elevasis/sdk/worker`
|
|
50
|
-
| Change the review screen | `ContentItemReviewPage` slots, or compose `ContentReviewCard` yourself
|
|
51
|
-
| Add content nav or a content route | `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle`
|
|
52
|
-
|
|
|
53
|
-
| Track where a piece was published | `createDistribution` / `updateDistribution`
|
|
54
|
-
|
|
|
46
|
+
| User wants | Start here | Notes |
|
|
47
|
+
| ------------------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| Add a pipeline, step, status, pillar, or platform | `core/config/organization-model.ts` — the content System's `ontology` | Catalog edit. Add the step catalog **and** point the pipeline record's `stepCatalog` at it. |
|
|
49
|
+
| Produce the next step of an item from a workflow | `content` from `@elevasis/sdk/worker` | One workflow per step. Claim the step with `stepKey`; return the next step key. |
|
|
50
|
+
| Change the review screen | `ContentItemReviewPage` slots, or compose `ContentReviewCard` yourself | Prefer slots. The card, action bar, alternates panel, and processing strip are all exported individually. |
|
|
51
|
+
| Add content nav or a content route | `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle` | Same manifest/sidebar composition pattern as CRM and lead-gen. |
|
|
52
|
+
| Give producers a style guide or voice constraints | The generating workflow's own prompt | Content has no rules-document tier. Put the instructions where the model reads them. |
|
|
53
|
+
| Track where a piece was published | `createDistribution` / `updateDistribution` | One distribution row per platform per item. |
|
|
54
|
+
| Pick source material for a new item | `createSourceAsset` / `getSourceAsset` / `listSourceAssets` / `updateSourceAsset` | Raw material a producer works from -- an uploaded episode, a transcript, a reference file. |
|
|
55
|
+
| Operate the pipeline from outside a browser | `elevasis-sdk content:*` | Read-only plus one write (`content:review`). Not how a workflow produces content -- see the boundary below. |
|
|
56
|
+
| Add a new persisted content column or table | Platform/API migration work, not just scaffold work | DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts move together. |
|
|
55
57
|
|
|
56
58
|
## Published Content Surfaces
|
|
57
59
|
|
|
58
|
-
| Surface
|
|
59
|
-
|
|
|
60
|
-
| `contentManifest`, `CONTENT_ITEMS`, `ContentSidebar`, `
|
|
61
|
-
| `ContentOverviewPage`, `ContentItemsPage`, `ContentItemReviewPage`, `ContentPipelinesPage`, `
|
|
62
|
-
| `
|
|
63
|
-
| `ContentReviewCard`, `ReviewActionBar`, `PayloadBody`, `AlternatesPanel`, `ProcessingStateStrip`
|
|
64
|
-
| `useContentConfig`, `resolveContentStepResource`
|
|
65
|
-
| `useContentItems`, `useContentItem`, `useContentItemAttempts`, `useUpdateContentItem`, `useReviewContentItem`
|
|
66
|
-
| `useContentDistributions`, `useContentDistribution`, `useUpdateContentDistribution`
|
|
67
|
-
| `
|
|
68
|
-
| `content
|
|
60
|
+
| Surface | Import from | Use for |
|
|
61
|
+
| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------ |
|
|
62
|
+
| `contentManifest`, `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle`, `MyReviewQueuePanel` | `@elevasis/ui/features/content` | Feature registration and sidebar composition |
|
|
63
|
+
| `ContentOverviewPage`, `ContentItemsPage`, `ContentItemReviewPage`, `ContentPipelinesPage`, `ContentPipelineBoardPage` | `@elevasis/ui/features/content` | Shared pages you can route to or wrap |
|
|
64
|
+
| `ContentDistributionsPage`, `ContentDistributionDetailPage` | `@elevasis/ui/features/content` | Distribution surfaces |
|
|
65
|
+
| `ContentReviewCard`, `ReviewActionBar`, `PayloadBody`, `AlternatesPanel`, `ProcessingStateStrip` | `@elevasis/ui/features/content` | Review UI primitives for a custom review screen |
|
|
66
|
+
| `useContentConfig`, `resolveContentStepResource` | `@elevasis/ui/features/content` | Read the resolved pipeline/step catalogs from OM |
|
|
67
|
+
| `useContentItems`, `useContentItem`, `useContentItemAttempts`, `useUpdateContentItem`, `useReviewContentItem` | `@elevasis/ui/hooks` | Item data access and review mutations |
|
|
68
|
+
| `useContentDistributions`, `useContentDistribution`, `useUpdateContentDistribution` | `@elevasis/ui/hooks` | Distribution data access |
|
|
69
|
+
| `useContentPipelineSummary` | `@elevasis/ui/hooks` | Pipeline roll-ups |
|
|
70
|
+
| `content` | `@elevasis/sdk/worker` | Workflow-side content adapter |
|
|
69
71
|
|
|
70
72
|
Read the generated contracts before changing typed boundaries:
|
|
71
73
|
|
|
@@ -104,7 +106,11 @@ Then set `content.config.defaultPipelineId` if this should be the pipeline the U
|
|
|
104
106
|
|
|
105
107
|
## 2. Produce a Step in a Workflow
|
|
106
108
|
|
|
107
|
-
One workflow per step. The workflow records an **attempt** against the item, tagged with the step it satisfies
|
|
109
|
+
One workflow per step. The workflow records an **attempt** against the item, tagged with the step it satisfies.
|
|
110
|
+
|
|
111
|
+
**Attempt lifecycle: write at start, patch at end.** Call `createAttempt` with `status: 'running'` and the `stepKey` _before_ doing the step's work, then call `updateAttempt` on that same row with the terminal status and the final payload once the work finishes. This is **one row per step execution, patched in place** -- never a start row plus a separate finish row. `attempt_number` counts per item, not per step, and there is a live unique constraint on the pair, so two rows for one step execution collide with genuine retries of that step and cannot be told apart from them.
|
|
112
|
+
|
|
113
|
+
A crashed step correctly leaves its row at `status: 'running'` forever. There is deliberately no reaper that flips a stale `running` row to `error` -- a stuck item is information an operator needs to see (`content:get` or `content:queue` surface it as-is), not a signal to paper over. If a nudge is wanted later it is a separate SLA concern, not something this write path should hide.
|
|
108
114
|
|
|
109
115
|
<!-- doc-snippet:skip: illustrative excerpt -- `resourceDescriptors` names the project's own resource-descriptors export -->
|
|
110
116
|
|
|
@@ -141,16 +147,29 @@ export const clipSelectionWorkflow: WorkflowDefinition = {
|
|
|
141
147
|
const input = rawInput as z.infer<typeof inputSchema>
|
|
142
148
|
const item = await content.getItem({ contentItemId: input.contentItemId })
|
|
143
149
|
|
|
150
|
+
// Write-at-start: create the row before doing any work, so a crash
|
|
151
|
+
// mid-step still leaves a `running` row an operator can find.
|
|
144
152
|
const attempt = await content.createAttempt({
|
|
145
153
|
contentItemId: item.id,
|
|
146
154
|
// Tags the attempt with the step it satisfies. Omit it and the
|
|
147
155
|
// attempt is stored with a null step -- the review UI's Step column
|
|
148
156
|
// renders "--" and nothing tells you which step produced the row.
|
|
149
157
|
stepKey: 'clip-selection',
|
|
150
|
-
|
|
158
|
+
status: 'running',
|
|
159
|
+
payload: {},
|
|
151
160
|
sourceExecutionId: context.executionId
|
|
152
161
|
})
|
|
153
162
|
|
|
163
|
+
const clips = await selectClips(item)
|
|
164
|
+
|
|
165
|
+
// Patch-at-end: the same row, moved to its terminal status with the
|
|
166
|
+
// real payload. Never a second createAttempt call for this step.
|
|
167
|
+
await content.updateAttempt({
|
|
168
|
+
attemptId: attempt.id,
|
|
169
|
+
status: 'success',
|
|
170
|
+
payload: { clips }
|
|
171
|
+
})
|
|
172
|
+
|
|
154
173
|
return { attemptId: attempt.id, nextStepKey: 'publish' }
|
|
155
174
|
}
|
|
156
175
|
}
|
|
@@ -159,39 +178,24 @@ export const clipSelectionWorkflow: WorkflowDefinition = {
|
|
|
159
178
|
}
|
|
160
179
|
```
|
|
161
180
|
|
|
162
|
-
`ContentToolMap` is the full surface: `createItem`, `getItem`, `listItems`, `updateItem`, `createAttempt`, `listAttempts`, `createDistribution`, `updateDistribution`. `organizationId` is injected server-side — never pass it from workflow code.
|
|
181
|
+
`ContentToolMap` is the full surface, 13 methods: `createItem`, `getItem`, `listItems`, `updateItem`, `createAttempt`, `listAttempts`, `updateAttempt`, `createSourceAsset`, `getSourceAsset`, `listSourceAssets`, `updateSourceAsset`, `createDistribution`, `updateDistribution`. `organizationId` is injected server-side — never pass it from workflow code.
|
|
163
182
|
|
|
164
|
-
|
|
183
|
+
**`reviewItem` is deliberately not on this list.** Clearing a `queued` review gate is operator work, done through `elevasis-sdk content:review` (see `apps/docs/content/docs/sdk/sdk/cli-management.mdx`), not something a producer workflow can call. A producer approving its own output collapses the gate the review step exists to provide.
|
|
165
184
|
|
|
166
|
-
|
|
185
|
+
### Source Assets
|
|
167
186
|
|
|
168
|
-
|
|
169
|
-
import { artifacts } from '@elevasis/sdk/worker'
|
|
170
|
-
|
|
171
|
-
// `kind` MUST be `content:`-prefixed. The artifacts_manage_content RLS policy
|
|
172
|
-
// grants org-owned writes only for `kind LIKE 'content:%'` -- a non-prefixed
|
|
173
|
-
// kind is rejected server-side with no compile-time warning.
|
|
174
|
-
//
|
|
175
|
-
// `pipelineId` MUST be supplied. getActive filters on
|
|
176
|
-
// (organizationId, pipelineId, kind); an artifact written without one can
|
|
177
|
-
// never be read back by the lookup every consumer uses.
|
|
178
|
-
await artifacts.createArtifact({
|
|
179
|
-
ownerKind: 'organization',
|
|
180
|
-
ownerId: null,
|
|
181
|
-
kind: 'content:style-guide',
|
|
182
|
-
pipelineId: 'podcast-to-clips',
|
|
183
|
-
content: { tone: 'direct', bannedPhrases: ['synergy'] }
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
const { artifact } = await artifacts.getActive({
|
|
187
|
-
pipelineId: 'podcast-to-clips',
|
|
188
|
-
kind: 'content:style-guide'
|
|
189
|
-
})
|
|
190
|
-
```
|
|
187
|
+
`createSourceAsset`, `getSourceAsset`, `listSourceAssets`, and `updateSourceAsset` manage the raw material a producer works from -- an uploaded podcast episode, a transcript, a reference file -- independent of any content item. A producer lists or fetches source assets to pick material, then references the chosen one (`sourceAssetId`) when it calls `createItem`. Source assets have their own `kind`-keyed payload envelope, declared per-kind in the content System's OM catalog, the same pattern `createItem`'s payload uses.
|
|
191
188
|
|
|
192
|
-
|
|
189
|
+
## 3. Give Producers Their Instructions
|
|
193
190
|
|
|
194
|
-
|
|
191
|
+
There is no rules-document tier and no settings page. Voice, banned phrases, structural constraints, and the craft checklist go in the **producing workflow's own prompt**.
|
|
192
|
+
|
|
193
|
+
That is not a downgrade from a stored document — it is where the instructions were always read. The tier that used to sit here stored versioned prose that no step ever loaded and no measurement ever rewrote, so its contents reached the model only by being copied into a prompt anyway. Putting them in the prompt directly means they version with the code that reads them and cannot silently disagree with it.
|
|
194
|
+
|
|
195
|
+
Two consequences worth knowing:
|
|
196
|
+
|
|
197
|
+
- **Per-idea constraints belong with the idea, not in a separate document.** When the substance of a piece and the constraints on that substance come apart, a model will invent the substance to satisfy the constraints. Keep an entry and its rules as one object in whatever the workflow reads.
|
|
198
|
+
- **The `artifacts` platform tool still exists** and is a generic, kind-keyed document store. It is no longer part of content's story, and `artifacts_manage_content` is gone, so a `content:`-prefixed org-owned write is no longer granted by `content.manage`. Reach for it as a general capability, not as content's rules tier.
|
|
195
199
|
|
|
196
200
|
## 4. Customize the Review Screen
|
|
197
201
|
|
|
@@ -213,14 +217,106 @@ function ReviewRoute() {
|
|
|
213
217
|
|
|
214
218
|
When the layout itself has to change, compose the primitives — `ContentReviewCard`, `ReviewActionBar`, `PayloadBody`, `AlternatesPanel`, `ProcessingStateStrip` — with `useContentItem`, `useContentItemAttempts`, and `useReviewContentItem`. Read the pipeline's steps through `useContentConfig` rather than hardcoding them, or a new step silently fails to render.
|
|
215
219
|
|
|
216
|
-
## 5.
|
|
220
|
+
## 5. Own the Workspace
|
|
221
|
+
|
|
222
|
+
This section is a contract, not a nav convention. One line divides the content System's UI:
|
|
223
|
+
|
|
224
|
+
**You own the surfaces where content is operated on. The platform owns the surfaces where it is
|
|
225
|
+
observed.**
|
|
226
|
+
|
|
227
|
+
Operating means creating a piece and clearing its review gates. Both are yours, and they are usually
|
|
228
|
+
one screen. Observing means the board, the items list, the attempt history, the distributions, and
|
|
229
|
+
the cross-pipeline review queue — those are shared, they display state, and they do not write.
|
|
217
230
|
|
|
218
|
-
|
|
231
|
+
The sidebar is shaped to match: the **`Workspace`** section on top holds your screens; the
|
|
232
|
+
`Content Pipeline` section below it is the shared observation nav.
|
|
233
|
+
|
|
234
|
+
### Why the split is drawn here
|
|
235
|
+
|
|
236
|
+
There are many ways to make content and many ways to review it, and both vary by source, by
|
|
237
|
+
platform, and by pipeline. A shared screen cannot hold that variation — every attempt produces a
|
|
238
|
+
page that fits nobody. What the System actually shares is the data model, the gate mechanics, and
|
|
239
|
+
the cross-pipeline view.
|
|
240
|
+
|
|
241
|
+
Nothing is given up by moving the review screen to your side. The rule that a producer cannot
|
|
242
|
+
approve its own output is enforced in two places, neither of them the UI: `reviewItem` is
|
|
243
|
+
deliberately absent from the content tool map, so no workflow can call it, and the HTTP route
|
|
244
|
+
carries `requireAccess(AccessKeys.contentManage)`. A React page of yours calling
|
|
245
|
+
`useReviewContentItem` is a person in a browser holding a JWT — exactly the operator the gate exists
|
|
246
|
+
for.
|
|
247
|
+
|
|
248
|
+
### Declare your Workspace pages
|
|
249
|
+
|
|
250
|
+
Pass `workspaceItems`:
|
|
219
251
|
|
|
220
252
|
```tsx
|
|
221
|
-
import { contentManifest,
|
|
253
|
+
import { contentManifest, ContentSidebar } from '@elevasis/ui/features/content'
|
|
222
254
|
import type { SystemModule } from '@elevasis/ui/provider'
|
|
223
255
|
import type { NavItem } from '@elevasis/ui/layout'
|
|
256
|
+
import { IconPencil } from '@tabler/icons-react'
|
|
257
|
+
|
|
258
|
+
const workspaceItems: NavItem[] = [{ label: 'Draft a Post', to: '/content/write', icon: IconPencil, exact: false }]
|
|
259
|
+
|
|
260
|
+
export const customContentManifest: SystemModule = {
|
|
261
|
+
...contentManifest,
|
|
262
|
+
sidebar: () => <ContentSidebar workspaceItems={workspaceItems} />
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Add as many as you like — a drafting screen, a brief builder, a batch generator, a review console.
|
|
267
|
+
Until you declare one, the section renders a "No workspace pages declared yet" hint rather than an
|
|
268
|
+
empty gap.
|
|
269
|
+
|
|
270
|
+
Everything you need to build a combined create-and-review screen is already exported:
|
|
271
|
+
`useReviewContentItem`, `ReviewActionBar`, `ContentReviewCard`, `PayloadBody`, `AlternatesPanel`,
|
|
272
|
+
`ProcessingStateStrip`, `useContentItem`, `useContentItemAttempts`, `useContentConfig`, and
|
|
273
|
+
`resolveContentStepResource`.
|
|
274
|
+
|
|
275
|
+
**`Workspace` is a fixed platform label and is not vocabulary-driven.** Rename the page, not the
|
|
276
|
+
section. (The section was called `Create` before 2026-08-15. `createItems` still works as a
|
|
277
|
+
deprecated alias for one minor; move to `workspaceItems`.)
|
|
278
|
+
|
|
279
|
+
### Point the shared pages at your Workspace
|
|
280
|
+
|
|
281
|
+
A display-only surface with nothing to click is a dead end, so declare where a reviewer should go.
|
|
282
|
+
Set `workspaceRoute` on the pipeline's catalog entry:
|
|
283
|
+
|
|
284
|
+
<!-- doc-snippet:skip: illustrative excerpt, not a standalone compilable file -->
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
'linkedin-post': {
|
|
288
|
+
label: 'LinkedIn Post',
|
|
289
|
+
stepCatalog: 'content:catalog/linkedin-post-steps',
|
|
290
|
+
workspaceRoute: '/content/write'
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The board, the items list, and the review queue all resolve it: a card or row sitting at an open
|
|
295
|
+
gate opens your screen instead of the shared item page. A step may override it when a pipeline's
|
|
296
|
+
gates are genuinely different screens — a video chain reviewing text candidates, then rendered
|
|
297
|
+
video, then copy plus a schedule, is three screens, not one:
|
|
298
|
+
|
|
299
|
+
<!-- doc-snippet:skip: illustrative excerpt, not a standalone compilable file -->
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
'render-video': { actor: 'workflow', reviewMode: 'queued', workspaceRoute: '/content/review-render' }
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Resolution is override-then-fallback — the step's route when it declares one, the pipeline's
|
|
306
|
+
otherwise. Use `resolveContentWorkspaceRoute` rather than reading either field directly.
|
|
307
|
+
|
|
308
|
+
**Declaring it is expected, and omitting it is supported.** A pipeline with no `workspaceRoute`
|
|
309
|
+
keeps the shared review page's action bar, so you can clear gates before you have built anything.
|
|
310
|
+
Once you declare a route, that page defers to it and links out instead. The route is an
|
|
311
|
+
app-relative path (`/content/write`); an absolute URL is rejected, because a pipeline entry should
|
|
312
|
+
not be able to send a reviewer off-origin.
|
|
313
|
+
|
|
314
|
+
### Adding to the pipeline nav (rare)
|
|
315
|
+
|
|
316
|
+
`items` replaces the pipeline list wholesale, so you must restate the shared entries:
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
import { CONTENT_ITEMS, ContentSidebar, ContentSidebarMiddle } from '@elevasis/ui/features/content'
|
|
224
320
|
import { IconChartBar } from '@tabler/icons-react'
|
|
225
321
|
|
|
226
322
|
const customItems: NavItem[] = [
|
|
@@ -228,17 +324,20 @@ const customItems: NavItem[] = [
|
|
|
228
324
|
{ label: 'Performance', to: '/content/performance', icon: IconChartBar, exact: false }
|
|
229
325
|
]
|
|
230
326
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
</ContentSidebar>
|
|
237
|
-
)
|
|
238
|
-
}
|
|
327
|
+
sidebar: () => (
|
|
328
|
+
<ContentSidebar>
|
|
329
|
+
<ContentSidebarMiddle workspaceItems={workspaceItems} items={customItems} />
|
|
330
|
+
</ContentSidebar>
|
|
331
|
+
)
|
|
239
332
|
```
|
|
240
333
|
|
|
241
|
-
|
|
334
|
+
**Prefer `workspaceItems` whenever the page is one of yours.** `CONTENT_ITEMS` is a static snapshot
|
|
335
|
+
built from the default vocabulary, so spreading it pins the shared labels at their defaults — a
|
|
336
|
+
tenant that later renames its pipelines to "Campaigns" through `system.config.vocabulary` keeps
|
|
337
|
+
rendering "Pipelines" here, and nothing fails to tell you.
|
|
338
|
+
|
|
339
|
+
Replace `contentManifest` with `customContentManifest` in the local `SYSTEM_MANIFESTS` array and add
|
|
340
|
+
the matching route under `ui/src/routes/content/`.
|
|
242
341
|
|
|
243
342
|
## 6. Mark the System API-Ready
|
|
244
343
|
|
|
@@ -42,9 +42,9 @@ description: Terminology disambiguation for Organization OS concepts used in the
|
|
|
42
42
|
|
|
43
43
|
**MembershipFeatureConfig** -- retired per-member feature override config. The migration is complete: access is resolved through the unified Access Model using Organization Model System lifecycle, role permissions, diagnostic allowlists, membership scope, and platform-admin bypass.
|
|
44
44
|
|
|
45
|
-
**OrganizationModel** -- top-level semantic contract for an organization. Current primary fields include `version`, `snapshotHash`, `domainMetadata`, `branding`, `navigation`, `ontology`, `systems`, `resources`, `topology`, `identity`, `clients`, `customers`, `offerings`, `roles`, `goals`,
|
|
45
|
+
**OrganizationModel** -- top-level semantic contract for an organization. Current primary fields include `version`, `snapshotHash`, `domainMetadata`, `branding`, `navigation`, `ontology`, `systems`, `resources`, `topology`, `identity`, `clients`, `customers`, `offerings`, `roles`, `goals`, and `knowledge`. `sales`, `prospecting`, `projects`, `statuses`, and `policies` were removed from the top-level contract in the Phase 4 schema cut; do not author against them. `actions` and `entities` remain current schema fields but are compatibility-oriented -- new authoring prefers System-owned ontology/config/resource contracts.
|
|
46
46
|
|
|
47
|
-
**OrganizationModelSystemEntry** -- System node in `OrganizationModel.systems`. Primary authoring fields include `id`, `label`, `description`, `parentSystemId`, `systems`, `lifecycle`, `ui`, `requiresAdmin`, `devOnly`, `responsibleRoleId`, `governedByKnowledge`, `drivesGoals`, `actions`, `
|
|
47
|
+
**OrganizationModelSystemEntry** -- System node in `OrganizationModel.systems`. Primary authoring fields include `id`, `label`, `description`, `parentSystemId`, `systems`, `lifecycle`, `ui`, `requiresAdmin`, `devOnly`, `responsibleRoleId`, `governedByKnowledge`, `drivesGoals`, `actions`, `ontology`, `config`, and `order`. `subsystems` is a retained compatibility input for older projects and should not be used for new recursive Systems. `content` is **retired** — the System schema is strict and rejects it. Author schemas and catalogs in `ontology`, and system-local settings in `config`.
|
|
48
48
|
|
|
49
49
|
**Provider / ElevasisSystemsProvider** -- runtime that registers System modules, resolves System lifecycle against the org model, projects sidebar navigation, and exposes shell helpers through `useElevasisSystems()`.
|
|
50
50
|
|
|
@@ -8,18 +8,24 @@
|
|
|
8
8
|
|
|
9
9
|
This catalog is generated from `SYSTEM_INTERFACE_PROFILES` and the derived-readiness checks in `packages/core/src/organization-model/readiness/` (built-in lead-gen/CRM profiles are registered from `packages/core/src/business/acquisition/ontology-validation.ts`).
|
|
10
10
|
|
|
11
|
-
System Interface profiles are a closed platform adoption handshake.
|
|
11
|
+
System Interface profiles are a closed platform adoption handshake. This table is the complete set of profiles a System may declare; a System that adopts none of them omits `apiInterface` entirely and extends behavior through workflows/operations plus ontology, resources, and topology instead.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
|
13
|
+
The catalog is closed because a `readinessProfile` names a capability the platform API gates a route on -- only the platform can mint one. `SystemInterfaceReadinessProfileSchema` accepts any non-empty string, but that is a property of the schema layer, not permission: an uncataloged id names no route, so the declaring System computes as **ready** and every call still fails, which is worse than the `missing-interface` a plain omission reports. Needing an uncataloged capability is a platform request (a route plus a catalog entry), not something to route around by inventing an id.
|
|
14
|
+
|
|
15
|
+
Two validation modes appear below. A **built-in** profile has a validator registered in platform code, so the adopting System needs no `readinessContract`. A **contract-validated** profile is platform-served but its structural requirements differ per adopter, so the adopting System MUST declare a `readinessContract`. `registerBuiltInReadinessProfile` is platform-code-only and is not reachable from a project: it mutates an in-process registry, while the readiness assertion that gates the API runs in the API process.
|
|
16
|
+
|
|
17
|
+
| Profile | Required System path | Interface key | Validation | `readinessContract` |
|
|
18
|
+
| --- | --- | --- | --- | --- |
|
|
19
|
+
| `sales.lead-gen.api` | `sales.lead-gen` | `api` | built-in | not required |
|
|
20
|
+
| `sales.crm.api` | `sales.crm` | `api` | built-in | not required |
|
|
21
|
+
| `sales.lead-gen.crm-handoff` | `sales.lead-gen` | `crm-handoff` | built-in | not required |
|
|
22
|
+
| `content.api` | `content` | `api` | contract | **required** |
|
|
18
23
|
|
|
19
24
|
## `sales.lead-gen.api`
|
|
20
25
|
|
|
21
26
|
- Required System path: `sales.lead-gen`
|
|
22
27
|
- Interface key: `api`
|
|
28
|
+
- Validation: **built-in** -- a platform-registered validator covers this profile; no `readinessContract` is required.
|
|
23
29
|
- Derived-readiness requirements:
|
|
24
30
|
- System Interface marker must be active and scope at least one active `sales.lead-gen` Resource.
|
|
25
31
|
- Object types: `sales.lead-gen:object/list`, `sales.lead-gen:object/company`, `sales.lead-gen:object/contact`.
|
|
@@ -33,6 +39,7 @@ System Interface profiles are a closed platform adoption handshake. Tenant custo
|
|
|
33
39
|
|
|
34
40
|
- Required System path: `sales.crm`
|
|
35
41
|
- Interface key: `api`
|
|
42
|
+
- Validation: **built-in** -- a platform-registered validator covers this profile; no `readinessContract` is required.
|
|
36
43
|
- Derived-readiness requirements:
|
|
37
44
|
- System Interface marker must be active and scope at least one active `sales.crm` Resource.
|
|
38
45
|
- Catalog types with entries: `sales.crm:catalog/crm.pipeline`.
|
|
@@ -42,6 +49,7 @@ System Interface profiles are a closed platform adoption handshake. Tenant custo
|
|
|
42
49
|
|
|
43
50
|
- Required System path: `sales.lead-gen`
|
|
44
51
|
- Interface key: `crm-handoff`
|
|
52
|
+
- Validation: **built-in** -- a platform-registered validator covers this profile; no `readinessContract` is required.
|
|
45
53
|
- Derived-readiness requirements:
|
|
46
54
|
- Derived handoff readiness is evaluated for `sales.lead-gen/crm-handoff`; it is not authored as a separate custom tenant API surface.
|
|
47
55
|
- Lead-gen API readiness requirements must pass for `sales.lead-gen/api`.
|
|
@@ -49,3 +57,15 @@ System Interface profiles are a closed platform adoption handshake. Tenant custo
|
|
|
49
57
|
- Derived scoped resources are active `sales.lead-gen` Resources whose `ontology.usesCatalogs` includes `sales.crm:catalog/crm.pipeline`.
|
|
50
58
|
- Provider readiness must pass for `sales.crm/api`.
|
|
51
59
|
- Topology must include a scoped `systemInterfaceGrant` relationship from consumer `sales.lead-gen/crm-handoff` to provider `sales.crm/api`.
|
|
60
|
+
|
|
61
|
+
## `content.api`
|
|
62
|
+
|
|
63
|
+
- Required System path: `content`
|
|
64
|
+
- Interface key: `api`
|
|
65
|
+
- Validation: **contract-validated** -- the adopting System MUST declare a `readinessContract`.
|
|
66
|
+
- Derived-readiness requirements:
|
|
67
|
+
- Gates every `/api/content/*` and `/api/external/content/*` route, via `CONTENT_API_INTERFACE` in `apps/api/src/business/content/organization-model.ts`.
|
|
68
|
+
- A `readinessContract` is REQUIRED. Unlike the `sales.*` profiles, no built-in validator is registered for this id, so readiness is checked structurally against the contract you declare; omitting it produces `missing-readiness-contract` at deploy preflight.
|
|
69
|
+
- `readinessContract.requiredObjects` must name the content item object owned by `content`, with a scoped `ontology.reads` binding on at least one scoped resource.
|
|
70
|
+
- `readinessContract.requiredCatalogs` must name your pipeline and status catalogs plus the step catalog your pipeline declares, each owned by `content`, non-empty, and carrying a scoped `ontology.usesCatalogs` binding. The step catalog id is project-specific by design.
|
|
71
|
+
- `resourceIds` must scope at least one active Resource; an empty array fails at runtime with `missing-scoped-resources` regardless of what deploy reported.
|