@unoverse-platform/studio 0.1.45 → 0.1.47

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.
@@ -247,7 +247,7 @@ function useTextSelection(enabled: boolean) {
247
247
  * (childList change) OR streaming text (a child resizing) — but only while the user is
248
248
  * already near the bottom, so scrolling up to read history is never yanked back down.
249
249
  * NOT chat-specific: any `overflow:auto` Box can opt in. The SDK owns behaviour, never
250
- * styles — the scroll container itself is defined in `rx/` (the conversation state box).
250
+ * styles — the scroll container itself is defined in `design/` (the conversation state box).
251
251
  */
252
252
  function useAutoScrollToEnd(enabled: boolean) {
253
253
  const ref = useRef<HTMLDivElement | null>(null);
@@ -281,11 +281,11 @@ function useAutoScrollToEnd(enabled: boolean) {
281
281
  /**
282
282
  * Generic "sticky bar that reveals only once you've scrolled past the element before it"
283
283
  * (opt-in via `node.revealOnStick`). Pins the bar at the top (its own `position: sticky`
284
- * + `top` in `rx/`) and, using an IntersectionObserver on the bar's PREVIOUS sibling as
284
+ * + `top` in `design/`) and, using an IntersectionObserver on the bar's PREVIOUS sibling as
285
285
  * the sentinel (e.g. a hero image), keeps it hidden while that sibling is on screen and
286
286
  * fades it in once the sibling scrolls away. A negative margin removes the bar's flow
287
287
  * footprint so it OVERLAYS the content (no gap at the top). NOT card-specific: any sticky
288
- * bar can opt in. The SDK owns behaviour, never styles — the bar's look lives in `rx/`.
288
+ * bar can opt in. The SDK owns behaviour, never styles — the bar's look lives in `design/`.
289
289
  */
290
290
  function useRevealWhenStuck(enabled: boolean) {
291
291
  const ref = useRef<HTMLDivElement | null>(null);
@@ -415,7 +415,7 @@ export function IconView({ name, style, theme }: { name?: string; style?: Record
415
415
 
416
416
  /**
417
417
  * `Skeleton` — built-in loading placeholder. ALL dimensions are DATA, fetched
418
- * from the theme (theme.skeleton ← rx/styles/semantic/skeleton.json). The SDK
418
+ * from the theme (theme.skeleton ← design/styles/semantic/skeleton.json). The SDK
419
419
  * authors nothing here but structure (flex/full). `bars` are 'width height' pairs.
420
420
  */
421
421
  export function SkeletonView({ variant = "text", theme }: { variant?: string; theme: ResolvedTheme }): ReactNode {
@@ -680,9 +680,9 @@ export function InputView({ node, data, theme, onAction }: { node: UnoverseNode;
680
680
  * markdown-to-jsx overrides for the `Markdown` primitive.
681
681
  *
682
682
  * ⛔ AUTHORS ZERO STYLE VALUES. Every element style is read verbatim from the
683
- * SERVED prose recipe (`theme.prose` ← rx/styles/semantic/prose.json). The SDK
683
+ * SERVED prose recipe (`theme.prose` ← design/styles/semantic/prose.json). The SDK
684
684
  * only maps element → recipe key and supplies BEHAVIOUR (target=_blank, image
685
- * error-fallback). No hex, no px/rem, no weights here — they live in rx/.
685
+ * error-fallback). No hex, no px/rem, no weights here — they live in design/.
686
686
  *
687
687
  * Built once per theme (stable component identities so streaming re-renders don't
688
688
  * remount images and drop their broken-state).
@@ -734,7 +734,7 @@ function buildMarkdownOverrides(theme: ResolvedTheme, inheritLinkColor = false)
734
734
  // INLINE MARKS and BLOCKS. Authored copy uses all of these (a bolded access code, a
735
735
  // rule between sections, a quoted term), and an element absent from this map renders
736
736
  // at the browser's default — the one place a definition's look escapes the token
737
- // layer. Each maps to a served recipe key; the VALUES live in rx/styles/semantic/prose.
737
+ // layer. Each maps to a served recipe key; the VALUES live in design/styles/semantic/prose.
738
738
  strong: { props: { style: s("strong") } },
739
739
  em: { props: { style: s("emphasis") } },
740
740
  del: { props: { style: s("strikethrough") } },
@@ -12,7 +12,7 @@
12
12
  * `Button` (and its styling logic) kept leaking back into the dispatcher.
13
13
  *
14
14
  * THE FRAMEWORK IN ONE SENTENCE: this SDK is a FIXED, GENERIC interpreter. ALL
15
- * UX is built in DATA (rx/ definitions + the served theme) — an author creates
15
+ * UX is built in DATA (design/ definitions + the served theme) — an author creates
16
16
  * ANY interface WITHOUT editing this SDK. (See FRAMEWORK.md.)
17
17
  *
18
18
  * WHERE THINGS LIVE:
@@ -30,7 +30,7 @@
30
30
  *
31
31
  * Need something the vocab can't express? Prefer extending the generic interpreter
32
32
  * ONCE (a new style-vocab key in ./style, a new model projection) over a primitive.
33
- * You almost never need to touch this file — build it in rx/ first.
33
+ * You almost never need to touch this file — build it in design/ first.
34
34
  * ════════════════════════════════════════════════════════════════════════════
35
35
  */
36
36
  import type { ReactNode } from "react";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The style + animation INTERPRETER — neutral vocab → CSS, resolving token NAMES
3
3
  * against a theme FETCHED from the server. ⛔ OWNS ZERO STYLE VALUES (no hex, no
4
- * px/rem/em, no recipes); every value lives in rx/styles and is served. See
4
+ * px/rem/em, no recipes); every value lives in design/styles and is served. See
5
5
  * FRAMEWORK.md and render.tsx's header. Enforced by test/golden-rule.test.mjs.
6
6
  */
7
7
  import type { CSSProperties } from "react";
@@ -227,10 +227,10 @@ export function cssDecls(css: CSSProperties, important = false): string {
227
227
  }
228
228
 
229
229
  /**
230
- * Serialize the SERVED keyframes (theme.keyframes ← rx/styles/semantic/keyframes.json)
230
+ * Serialize the SERVED keyframes (theme.keyframes ← design/styles/semantic/keyframes.json)
231
231
  * into a CSS string, injected ONCE per render root (<style>). This authors zero values —
232
232
  * it only structures DATA into `@keyframes uno-<name>{ <stop>{ <prop>:<val> } }`. Adding a
233
- * new animation = adding a keyframe to rx/ + a `style.animation` ref in a definition. No
233
+ * new animation = adding a keyframe to design/ + a `style.animation` ref in a definition. No
234
234
  * SDK edit, no new primitive. Keyframe decls use real CSS prop names, KEBAB-CASE
235
235
  * (`box-shadow`, never `boxShadow`) — that is the canonical authored form, lint-enforced.
236
236
  * Property keys are still kebab-normalized here, exactly as Studio's gallery serializer
@@ -179,7 +179,7 @@ export function StreamedUnoverseTemplate({ client, store, uri, onAction, theme:
179
179
  * THE LATCH, PUBLISHED FOR CHROME (docs/MCP_COMPLETE_GUIDE.md §The Component Latch).
180
180
  *
181
181
  * `resolveLatch` derives WHICH instance the conversation addresses; the composer's pill
182
- * is ordinary rx chrome, so it needs those facts as template state to bind against. Same
182
+ * is ordinary design chrome, so it needs those facts as template state to bind against. Same
183
183
  * shape as the voice service (§4 "services feed the state"): a producer writes derived
184
184
  * values into the normal bucket, and `core` learns no UI concept — nothing here knows a
185
185
  * pill exists.
@@ -328,7 +328,7 @@ export function StreamedUnoverseTemplate({ client, store, uri, onAction, theme:
328
328
 
329
329
  // Timeline — GENERIC iterator. Walks the timeline and renders the `user` / `assistant`
330
330
  // DATA sub-tree per turn. Zero chat UX in the SDK: bubbles, alignment, structure all live
331
- // in those sub-trees (rx/templates/chatlayout/*.json). The assistant sub-tree's ComponentSlot
331
+ // in those sub-trees (design/templates/chatlayout/*.json). The assistant sub-tree's ComponentSlot
332
332
  // is scoped to THAT turn's components.
333
333
  const resolveTimeline = (node: UnoverseNode, key?: React.Key): ReactNode => {
334
334
  return (
@@ -2,8 +2,8 @@
2
2
  * useUnoverseTheme — fetch the RESOLVED theme from the MCP server by name.
3
3
  *
4
4
  * The SDK owns ZERO token values. The server owns the visual (apps/unoverse/
5
- * rx/styles, resolved live) and serves it at `unoverse://theme/{name}`; this hook
6
- * fetches it (cached in the client). A brand change in rx/styles is refresh-only
5
+ * design/styles, resolved live) and serves it at `unoverse://theme/{name}`; this hook
6
+ * fetches it (cached in the client). A brand change in design/styles is refresh-only
7
7
  * — there is no baked theme in this bundle to rebuild (UNOVERSE_SPEC §2d-1).
8
8
  */
9
9
  import { useEffect, useState } from "react";
@@ -1,18 +1,35 @@
1
1
  ---
2
2
  name: unoverse-create
3
- description: Create or edit Unoverse platform artifacts: UI components (including briefed/AI-filled components), templates, atoms and styles (rx/), brand and org packs (themes, tokens, rebranding, cloning a universe for a new client), agent skills and prompt blocks (prompts/), custom workflow nodes (nodes/), and workflows built live on the Canvas via the builder MCP. Use when the user wants to create, add, build, or modify a component, template, theme, brand, org pack, agent skill, prompt block, custom node, or workflow in an Unoverse starter repo.
3
+ description: "Create or edit Unoverse platform artifacts: UI components (including briefed/AI-filled components), templates, atoms and styles (design/), brand and org packs (themes, tokens, rebranding, cloning a universe for a new client), agent skills and prompt blocks (prompts/), custom workflow nodes (nodes/), and workflows built live on the Canvas via the builder MCP. Use whenever the user wants to create, add, build, or modify a component, template, card, page, theme, brand, org pack, agent skill, prompt block, custom node, or workflow in an Unoverse project, even if they don't say 'Unoverse' (they'll describe UI like 'a card with a button and an image' or 'a page the AI fills in')."
4
4
  ---
5
5
 
6
6
  # Creating Unoverse Artifacts
7
7
 
8
- You are helping a developer build in the **Unoverse developer carve-out**, the three
9
- editable folders mounted into the running platform:
8
+ You are helping a developer build in an Unoverse project: the three editable folders
9
+ the platform reads.
10
10
 
11
11
  | Folder | What lives there |
12
12
  |---|---|
13
- | `apps/unoverse/rx/` | **Design** (data, not code): components, atoms, org-scoped templates + styles |
14
- | `apps/unoverse/prompts/` | **Behavior**: agent skills (`skills/`) + prompt blocks (`blocks/`) |
15
- | `apps/unoverse/nodes/` | **Logic**: custom workflow nodes, authored as YAML |
13
+ | `design/` | **Design** (data, not code): per-project components, templates + styles; each project is `design/<project>/` |
14
+ | `prompts/` | **Behavior**: agent skills (`skills/`) + prompt blocks (`blocks/`) |
15
+ | `nodes/` | **Logic**: custom workflow nodes, authored as YAML |
16
+
17
+ Paths in this skill are relative to the project root. In a Studio developer workspace
18
+ that is the workspace folder itself; in the platform monorepo it is `apps/unoverse/`.
19
+
20
+ ## Step 0: Anchor in the project before writing
21
+
22
+ 1. **Pick the right `design/<project>/`.** Every design artifact belongs to a project (an
23
+ "org"). If the workspace has more than one, or the only one looks like a placeholder,
24
+ ask which project the artifact belongs to. Never invent a new project folder to hold
25
+ one artifact.
26
+ 2. **The design system is installed, not on disk.** A developer workspace holds only the
27
+ developer's own projects; the marketplace design system (shared atoms, components,
28
+ the token foundation) resolves from the installed platform. See it through Studio's
29
+ authoring MCP: `listDefinitions` (kind `atom` / `component`, org `marketplace`),
30
+ `readDefinition` for a full definition, `getVocabulary` for the closed node/style
31
+ vocabulary. Compose those atoms; never hand-roll a shape the design system already
32
+ ships. (In the platform monorepo the same material is on disk at `design/marketplace/`.)
16
33
 
17
34
  ## Step 1: Identify the artifact, read its playbook
18
35
 
@@ -31,40 +48,54 @@ anything**:
31
48
  | A workflow (wire nodes into an agent/pipeline on the Canvas) | `references/workflow.md` |
32
49
 
33
50
  Workflows are the one artifact that is NOT files: they are built live on the running
34
- platform through the `canvas` MCP (registered by this repo's `.mcp.json`).
51
+ platform through the `canvas` MCP (registered by the project's `.mcp.json`).
35
52
  The playbook covers the bind/build/test contract.
36
53
 
37
54
  If it's ambiguous (e.g. "add a card that shows weather"), it's usually a **component**
38
55
  (the UI) plus possibly a **node** (the data source). Confirm the scope with the user.
39
56
 
40
- **New to design artifacts?** The guided learning journey at `docs/design/` (README →
41
- quick-start → … → troubleshooting) teaches components, templates, the state model
42
- (incl. locked voice/native state), tokens, Studio, and validation in order;
43
- `docs/design/CLAUDE.md` is the condensed agent rulebook. The playbooks here assume that
44
- material.
57
+ **New to design artifacts?** The guided learning journey at
58
+ [docs.unoverse.ai/design](https://docs.unoverse.ai/design/overview.md)
59
+ ([quick-start](https://docs.unoverse.ai/design/quick-start.md) →
60
+ [components](https://docs.unoverse.ai/design/components.md) →
61
+ [state](https://docs.unoverse.ai/design/state.md) →
62
+ [templates](https://docs.unoverse.ai/design/templates.md) →
63
+ [styles-and-tokens](https://docs.unoverse.ai/design/styles-and-tokens.md) →
64
+ [studio](https://docs.unoverse.ai/design/studio.md) →
65
+ [validate-and-ship](https://docs.unoverse.ai/design/validate-and-ship.md) →
66
+ [troubleshooting](https://docs.unoverse.ai/design/troubleshooting.md)) teaches
67
+ components, templates, the state model (incl. locked voice/native state), tokens,
68
+ Studio, and validation in order;
69
+ [design/CLAUDE.md](https://docs.unoverse.ai/design/CLAUDE.md) is the condensed agent
70
+ rulebook. The playbooks here assume that material. Append `.md` to any docs URL to
71
+ fetch it as raw markdown.
45
72
 
46
73
  ## Step 2: Hard rules (apply to every artifact)
47
74
 
48
- 1. **Only edit the three carve-out folders.** Never touch, vendor, or work around the
75
+ 1. **Only edit the three project folders.** Never touch, vendor, or work around the
49
76
  SDK, engine, or server. They are not yours to change. If a task seems to require an
50
77
  SDK change, the answer is almost always "express it as data". Re-read the playbook.
51
78
  2. **Docs are the source of truth.** The playbooks name the design journey and the deep
52
- references ([UNOVERSE_AUTHORING](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_AUTHORING.md), [UNOVERSE_STATE_MODEL](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_STATE_MODEL.md), [UNOVERSE_LAYERS](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_LAYERS.md),
53
- [UNOVERSE_CONFORMANCE](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_CONFORMANCE.md)). They live in the docs repo
54
- (https://github.com/unoverse-platform/docs), not in your repo. When the user's request goes beyond the playbook, read the named section rather
55
- than improvising conventions.
79
+ references ([UNOVERSE_AUTHORING](https://docs.unoverse.ai/unoverse/UNOVERSE_AUTHORING.md),
80
+ [UNOVERSE_STATE_MODEL](https://docs.unoverse.ai/unoverse/UNOVERSE_STATE_MODEL.md),
81
+ [UNOVERSE_LAYERS](https://docs.unoverse.ai/unoverse/UNOVERSE_LAYERS.md),
82
+ [UNOVERSE_CONFORMANCE](https://docs.unoverse.ai/unoverse/UNOVERSE_CONFORMANCE.md)),
83
+ all served at docs.unoverse.ai. When the user's request goes beyond the playbook,
84
+ read the named section rather than improvising conventions.
56
85
  3. **UI is data.** No pixels, hex colors, or CSS anywhere: token names only. No logic
57
86
  in definitions: anything computed (totals, formatting, chosen colors) is computed in
58
87
  the node and sent as a plain field.
59
88
  4. **Audit before you're done.** Every component/template must pass the conformance
60
- checklist ([UNOVERSE_AUTHORING](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_AUTHORING.md) §9). Walk it item by item.
89
+ checklist ([UNOVERSE_AUTHORING](https://docs.unoverse.ai/unoverse/UNOVERSE_AUTHORING.md) §9).
90
+ Walk it item by item.
61
91
  5. **Discovery meta is ranked, not read.** Any spatially-discoverable artifact (a
62
92
  component or template with a manifest, an agent skill, a node) is selected by
63
93
  embedding `title`/`name` + `whenToUse`/`description` + `category` against the user's
64
94
  own words (or the planner's task). The strict contract is
65
- `docs/nodes/node-discoverability.md`: outcome-first, utterance-shaped, opening
66
- words dominate, disqualify by property (never name a sibling). Each playbook carries
67
- the summary; read doc 14 before writing any meta.
95
+ [node-discoverability](https://docs.unoverse.ai/nodes/node-discoverability.md):
96
+ outcome-first, utterance-shaped, opening words dominate, disqualify by property
97
+ (never name a sibling). Each playbook carries the summary; read that page before
98
+ writing any meta.
68
99
 
69
100
  ## Step 3: Deploy loop (after the artifact is written)
70
101
 
@@ -72,18 +103,21 @@ Components and templates have NO scaffold command. Author them from the referenc
72
103
  (mirror the closest existing artifact). Validation lives in Studio: its publish step
73
104
  lints every definition before anything leaves the machine (0 errors required, doc-cited
74
105
  messages; justify any warning), and the conformance checklist is
75
- [UNOVERSE_CONFORMANCE](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_CONFORMANCE.md).
106
+ [UNOVERSE_CONFORMANCE](https://docs.unoverse.ai/unoverse/UNOVERSE_CONFORMANCE.md).
76
107
 
77
108
  | Artifact | To see it live |
78
109
  |---|---|
79
110
  | Component / atom / template / style | Preview in Studio (`unoverse studio`), then publish from Studio: the publish lint gates it |
80
- | Existing component restyle/edit only | takes effect live (the SDK reads `rx/` directly) |
111
+ | Existing component restyle/edit only | takes effect live (the SDK reads `design/` directly) |
81
112
  | Agent skill / prompt block | publish from Studio, same gate |
82
113
  | Node | Studio's Nodes screen: Load sample → Run. A node is YAML; running it is the only proof (no build) |
83
114
 
84
- Verify the platform with `unoverse check` (services, schema, environment). Studio opens
85
- on :4108 (`unoverse studio` from the project folder).
115
+ **Close the loop with the developer.** After creating something visual, tell them how
116
+ to see it: open Studio (`unoverse studio`, :4108), find the artifact, preview it. The
117
+ deliverable is the rendered thing, never the YAML alone.
86
118
 
87
- > In the platform monorepo (not the starter), the node docs live at
88
- > `packages/docs/nodes/`, the design journey at `packages/docs/design/`, and the dev loop
89
- > is just `npm run dev`. Component nodes synthesize from rx/ definitions at boot.
119
+ Verify the platform with `unoverse check` (services, schema, environment).
120
+
121
+ > In the platform monorepo (not a developer workspace), the project root is
122
+ > `apps/unoverse/`, the docs source is `packages/docs/`, and the dev loop is
123
+ > `npm run dev`. Component nodes synthesize from design/ definitions at boot.
@@ -7,7 +7,7 @@ at runtime (e.g. how to handle complaints, how to walk a user through a process)
7
7
  ## Where files go
8
8
 
9
9
  ```
10
- apps/unoverse/prompts/skills/<skill-name>/
10
+ prompts/skills/<skill-name>/
11
11
  SKILL.md # required: frontmatter + instructions
12
12
  ```
13
13
 
@@ -21,7 +21,7 @@ The runtime scans this folder and serves skills to agents; agents match on the
21
21
  name: skill-name # required: lowercase, numbers, hyphens; max 64 chars
22
22
  title: Skill Name # human display name, shown as the item's title in listings
23
23
  description: What this skill IS: ONE short line (≤ 120 chars), the listing subtitle
24
- whenToUse: The SELECTION text: outcome-first, in the user's vocabulary; ranked against intent for spatial discovery. See docs/nodes/node-discoverability.md. The node rules apply verbatim.
24
+ whenToUse: The SELECTION text: outcome-first, in the user's vocabulary; ranked against intent for spatial discovery. See https://docs.unoverse.ai/nodes/node-discoverability.md. The node rules apply verbatim.
25
25
  version: 1.0.0 # optional, semver
26
26
  category: ai # optional: ai | integration | workflow | utility
27
27
  triggers: # optional, max 20 keywords/phrases (2–50 chars each)
@@ -53,7 +53,8 @@ is invalid.
53
53
  discovery / findIntent). Write it **outcome-first in the user's vocabulary**
54
54
  ("Handle an unhappy customer: a complaint, frustration, or a request to
55
55
  escalate…"), never mechanism-first. The full rules are
56
- `docs/nodes/node-discoverability.md`. They apply to skills verbatim.
56
+ [node-discoverability](https://docs.unoverse.ai/nodes/node-discoverability.md).
57
+ They apply to skills verbatim.
57
58
  2. **Study an existing skill** in `prompts/skills/` and match its voice and structure.
58
59
  3. Keep instructions outcome-focused and scannable: short sections, bulleted do/don't
59
60
  lists, explicit escalation/stop conditions.
@@ -61,5 +62,7 @@ is invalid.
61
62
 
62
63
  ## Ship
63
64
 
64
- `docker compose restart unoverse`. The runtime rescans skills at boot. Verify the
65
- skill appears via the platform's skill listing (`unoverse check` for overall health).
65
+ Publish from Studio (the publish lint gates it). Operators running the platform
66
+ directly can instead `docker compose restart unoverse`: the runtime rescans skills at
67
+ boot. Verify the skill appears via the platform's skill listing (`unoverse check` for
68
+ overall health).
@@ -8,7 +8,7 @@ is an ingredient.
8
8
  ## Where files go
9
9
 
10
10
  ```
11
- apps/unoverse/prompts/blocks/<category>/<block-name>.md
11
+ prompts/blocks/<category>/<block-name>.md
12
12
  ```
13
13
 
14
14
  The immediate subdirectory is the block's category (existing: `core/`, `formatting/`).
@@ -40,4 +40,5 @@ present tense, no meta-commentary about being a block.]
40
40
 
41
41
  ## Ship
42
42
 
43
- `docker compose restart unoverse`. The runtime rescans blocks at boot.
43
+ Publish from Studio (the publish lint gates it). Operators running the platform
44
+ directly can instead `docker compose restart unoverse`: the runtime rescans blocks at boot.
@@ -13,11 +13,11 @@ client. They share one discipline and one failure mode.
13
13
 
14
14
  ## 1. How overriding actually works: name a token to override, omit it to inherit
15
15
 
16
- `rx/marketplace/styles` is the **foundation**: the shared token contract every org
16
+ `design/marketplace/styles` is the **foundation**: the shared token contract every org
17
17
  inherits. Your org pack is read **on top** of it.
18
18
 
19
- The server builds a theme in three passes (`packages/base/src/definitions/theme.ts`,
20
- `buildTheme`):
19
+ The server builds a theme in three passes (`buildTheme`, in the platform's theme
20
+ resolver):
21
21
 
22
22
  1. the foundation's `base/` then `semantic/`, into a flat registry keyed by dotted path
23
23
  2. **your org's `base/` then `semantic/`, over the top**
@@ -45,8 +45,8 @@ re-copying anything.
45
45
 
46
46
  | Pack | `styles/base/` holds |
47
47
  |---|---|
48
- | Foundation (`rx/marketplace/`) | `border` `color` `motion` `radius` `shadow` `spacing` `typography` |
49
- | A brand (`rx/bpp/`) | `color` `typography` **only** |
48
+ | Foundation (`design/marketplace/`) | `border` `color` `motion` `radius` `shadow` `spacing` `typography` |
49
+ | A brand (`design/bpp/`) | `color` `typography` **only** |
50
50
 
51
51
  BPP names two files and inherits the other five. That ratio is the health check: a pack
52
52
  redefining `spacing` or `radius` is not branding, it is forking. The neutral core is
@@ -57,7 +57,7 @@ redefining `spacing` or `radius` is not branding, it is forking. The neutral cor
57
57
  An org is a folder. There is no registry to edit and nothing to declare.
58
58
 
59
59
  ```
60
- rx/<neworg>/
60
+ design/<neworg>/
61
61
  styles/
62
62
  base/color.yaml brand INGREDIENTS (color.brand.*)
63
63
  base/typography.yaml font families only
@@ -85,10 +85,11 @@ you genuinely differ.
85
85
 
86
86
  Its theme is then served at `unoverse://theme/<neworg>/light`, and a template points at it.
87
87
 
88
- ## 4. Anatomy of an org pack (`rx/<org>/styles/`)
88
+ ## 4. Anatomy of an org pack (`design/<org>/styles/`)
89
89
 
90
- Read `rx/bpp/styles/` as the worked example. Every file there carries a `$description`
91
- explaining its own job, and those descriptions are the spec.
90
+ Read an installed org pack as the worked example (Studio MCP `listOrgs` →
91
+ `listDefinitions`; in the platform monorepo, `design/bpp/styles/`). Every file in a healthy
92
+ pack carries a `$description` explaining its own job, and those descriptions are the spec.
92
93
 
93
94
  | File | Holds | Rule |
94
95
  |---|---|---|
@@ -127,7 +128,7 @@ naming your situation is the intended answer.
127
128
  Copying an org pack is mechanical, and every gotcha is a name that exists in more than one
128
129
  place. Work through them in order.
129
130
 
130
- 1. **Copy the folder.** `rx/<source>/` → `rx/<neworg>/`, keeping `styles/`, `components/`,
131
+ 1. **Copy the folder.** `design/<source>/` → `design/<neworg>/`, keeping `styles/`, `components/`,
131
132
  `templates/`.
132
133
  2. **Rename the templates.** Folder AND the id inside the manifest. `bpp-chat-layout`
133
134
  becomes `lyceum-chat-layout` in both places. Template ids are org-qualified and they
@@ -137,25 +138,25 @@ place. Work through them in order.
137
138
  is addressed `unoverse://components/<org>/<name>`. The one forbidden case is a name
138
139
  that shadows a design-system (marketplace) component; the publish lint catches it.
139
140
  Update template manifest `name`s only where the id itself is org-branded.
140
- 4. **Register the client surface** if the demo client serves it
141
- (`packages/demo/src/demo/clients.js`).
141
+ 4. **Register the client surface** if a demo client serves it (platform monorepo only:
142
+ `packages/demo/src/demo/clients.js`).
142
143
  5. **Point the app at the new theme.** Templates reference `unoverse://theme/<org>/<name>`.
143
144
  A copied template still names the old org until you change it.
144
145
 
145
146
  ## 7. Done means all four
146
147
 
147
- - `grep -ri "<oldorg>" rx/<neworg>/` returns **nothing**. This is the check that catches
148
+ - `grep -ri "<oldorg>" design/<neworg>/` returns **nothing**. This is the check that catches
148
149
  the id someone missed at midnight, and it is the one people skip.
149
150
  - The new org appears in **Studio's universe dropdown** and its theme renders.
150
151
  - **Studio's publish lint: 0 errors.** Justify any warning out loud.
151
152
  - The conformance checklist passes for every copied component
152
- ([UNOVERSE_CONFORMANCE](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_CONFORMANCE.md)).
153
+ ([UNOVERSE_CONFORMANCE](https://docs.unoverse.ai/unoverse/UNOVERSE_CONFORMANCE.md)).
153
154
 
154
155
  ## Deeper references
155
156
 
156
157
  Token structure and the resolve order:
157
- [UNOVERSE_AUTHORING](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_AUTHORING.md).
158
+ [UNOVERSE_AUTHORING](https://docs.unoverse.ai/unoverse/UNOVERSE_AUTHORING.md).
158
159
  Which tier owns what:
159
- [UNOVERSE_LAYERS](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_LAYERS.md).
160
+ [UNOVERSE_LAYERS](https://docs.unoverse.ai/unoverse/UNOVERSE_LAYERS.md).
160
161
  Styles are W3C design tokens, two-tier base/semantic, resolved live by the server and
161
162
  served at `unoverse://theme/{org}/{name}`. The SDK fetches them and bundles none.
@@ -1,15 +1,20 @@
1
- # Playbook: Components & Atoms (rx data)
1
+ # Playbook: Components & Atoms (design data)
2
2
 
3
- **Read first:** `docs/design/components.md` (the microapp anatomy) + `docs/design/state.md`
4
- (the state model). Deep law: [UNOVERSE_AUTHORING](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_AUTHORING.md) §3 (three homes),
5
- `UNOVERSE_STATE_MODEL.md` §5 (the six rules), `UNOVERSE_LAYERS.md` (states own layouts).
6
- **Study the exemplar before writing**: `rx/sab/components/product-card` is the canonical
7
- State Model v2 component; mirror it exactly.
3
+ **Read first:** [design/components](https://docs.unoverse.ai/design/components.md) (the
4
+ microapp anatomy) + [design/state](https://docs.unoverse.ai/design/state.md) (the state
5
+ model). Deep law: [UNOVERSE_AUTHORING](https://docs.unoverse.ai/unoverse/UNOVERSE_AUTHORING.md) §3 (three homes),
6
+ [UNOVERSE_STATE_MODEL](https://docs.unoverse.ai/unoverse/UNOVERSE_STATE_MODEL.md) §5 (the six rules),
7
+ [UNOVERSE_LAYERS](https://docs.unoverse.ai/unoverse/UNOVERSE_LAYERS.md) (states own layouts).
8
+ **Study an exemplar before writing.** The canonical State Model v2 state tree is inlined
9
+ below; for a full worked component, read an installed one through Studio's MCP
10
+ (`listDefinitions` kind `component`, then `readDefinition`), and prefer a rich
11
+ multi-state one over a starter stub. (In the platform monorepo the canonical exemplar
12
+ is on disk: `design/sab/components/product-card`.)
8
13
 
9
14
  ## The anatomy
10
15
 
11
16
  ```
12
- apps/unoverse/rx/<project>/components/<name>/
17
+ design/<project>/components/<name>/
13
18
  <name>.yaml # ROOT: envelope (name/category/nodeSize/outputs/props) + the STATE TREE + root
14
19
  manifest.yaml # RENDER CONTRACT + discovery meta (title / description ≤120 /
15
20
  # whenToUse utterance-shaped). Presence = spatially discoverable;
@@ -25,7 +30,7 @@ manifest, no folders. Structure is EARNED; start flat.
25
30
 
26
31
  A component is **data plus a state tree**; each state owns the layout(s) that draw it.
27
32
  The root's `state:` block IS the design, readable top to bottom. The canonical shape
28
- (`rx/sab/components/product-card/product-card.yaml`):
33
+ (a product-card component: a grid state and a detail page with nested steps):
29
34
 
30
35
  ```yaml
31
36
  state:
@@ -101,9 +106,9 @@ The rules, in the order they bite:
101
106
  BY NAME (as-is, no mapping); a name the source doesn't carry silently renders the
102
107
  preview default (tell: title streams, image stays mock). Content-attached cards use
103
108
  the writer vocabulary: `title` `tagline` `description` `bodyCopy` `introParagraph`
104
- `primaryImage` `images` `link` `callToAction`. Guard:
105
- `server/src/runtime/content-card-hydration.test.ts`; law:
106
- `UNOVERSE_MCP_TEMPLATE_PROTOCOL.md` §Content-attached cards.
109
+ `primaryImage` `images` `link` `callToAction`. Law:
110
+ `UNOVERSE_MCP_TEMPLATE_PROTOCOL.md` §Content-attached cards (platform spec; a
111
+ guard test in the platform server enforces it).
107
112
  2. **Root = `Switch on view`**, cases per PUBLIC state, each `$include` of the state's
108
113
  layout (the tree above). Keep an `inline` state for any component that lives in a
109
114
  conversation flow: the flow is its placeholder there. One EXCEPTION is the
@@ -119,7 +124,8 @@ The rules, in the order they bite:
119
124
  closes itself, or a NEW TEMPLATE loads (the template swap is the hard refresh boundary).
120
125
  Durable slices also opt out of `cancelBelow` retraction when a higher template state
121
126
  takes over. Default `"turn"`: the instance returns to inline / retires on the next
122
- user turn. Values are a closed set: `turn | conversation` (lint-checked; docs/design/04).
127
+ user turn. Values are a closed set: `turn | conversation` (lint-checked;
128
+ [design/components](https://docs.unoverse.ai/design/components.md)).
123
129
  3. **State is local; the `view` is the interface** (STATE_MODEL §5). Private axes
124
130
  (`step`, `phase`, …) are the component's own business; the template never sees them.
125
131
  The only thing that crosses is the public `view`. The component writes ONLY its own
@@ -135,14 +141,18 @@ The rules, in the order they bite:
135
141
  of bound fields). Same shape, different values = ONE state, data-driven: seven wizard
136
142
  questions sharing one shape are one `question` state whose data changes, never seven
137
143
  files (LAYERS §4, §7).
138
- 5. **Atoms** (`rx/marketplace/atoms/`, via `Ref`) are for shapes shared across components, authoring-time
144
+ 5. **Atoms** (the installed design system's shared shapes, used via `Ref`; list them with
145
+ Studio's MCP `listDefinitions` kind `atom`, or on disk at `design/marketplace/atoms/` in the
146
+ platform monorepo) are for shapes shared across components, authoring-time
139
147
  only (the server expands them before serving; no Studio view). `Ref props` remaps FIELDS;
140
148
  **`Ref with` passes LITERALS**: `{ "type": "Ref", "ref": "button", "with": { "label":
141
149
  "Learn more", "icon": "arrowRight" }, "action": { … } }`. A bind whose field is a `with`
142
150
  key becomes a hardcoded attribute, a truthy `with` key drops a matching `visibleWhen`
143
151
  guard (unprovided ⇒ that piece stays hidden), `{{key}}` style bindings take the value.
144
152
  ⚠ Icon: literal = `icon: "phone"`; bound = `bind: { name: field }`.
145
- 6. **Briefs: components an AI fills** (docs/design/03 §Briefed components; deep law: [UNOVERSE_AUTHORING](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_AUTHORING.md) §3b). A `brief`
153
+ 6. **Briefs: components an AI fills**
154
+ ([design/components](https://docs.unoverse.ai/design/components.md) §Briefed components;
155
+ deep law: [UNOVERSE_AUTHORING](https://docs.unoverse.ai/unoverse/UNOVERSE_AUTHORING.md) §3b). A `brief`
146
156
  sits ON the node that renders what it describes, next to its `bind`, never in the
147
157
  manifest, never a separate file:
148
158
  - bound element → `"brief": { "description": "…", "maxLength": 60 }`
@@ -157,15 +167,16 @@ The rules, in the order they bite:
157
167
  for surgical refinement). **The schema IS the instruction channel.**
158
168
  Write briefs, never side-channel prompts/skills for filling a component. Shape is
159
169
  closed + linted. Pairs with the single-state permanent pattern (rule 2) for
160
- continuously-enriched pages; exemplar: `rx/yasisland/components/perfect-day`.
170
+ continuously-enriched pages.
161
171
  7. **Tokens only** (LAW 1) + closed style keys + real space-scale steps
162
- (`docs/design/06`): an invented step is silently broken CSS.
172
+ ([design/styles-and-tokens](https://docs.unoverse.ai/design/styles-and-tokens.md)):
173
+ an invented step is silently broken CSS.
163
174
  8. **Lifecycle hooks: the component fetches its own data** (full doc:
164
- `docs/design/lifecycle-hooks.md`; deep law:
165
- [UNOVERSE_AUTHORING](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_AUTHORING.md) §3c). When a component needs data the
175
+ [design/lifecycle-hooks](https://docs.unoverse.ai/design/lifecycle-hooks.md); deep law:
176
+ [UNOVERSE_AUTHORING](https://docs.unoverse.ai/unoverse/UNOVERSE_AUTHORING.md) §3c). When a component needs data the
166
177
  workflow did not send (not from an AI = brief, not from the workflow = `input` props),
167
178
  it declares CALLS that the platform runs at a platform-owned fire point. **No code**:
168
- a hook is data like everything else in `rx/`. It's for DATA, not styling or logic.
179
+ a hook is data like everything else in `design/`. It's for DATA, not styling or logic.
169
180
  - **Two phases**: `onStart` (the instance is created: data every copy needs) and
170
181
  `onEnterView` (the instance entered a named STATE: data only that state needs). A
171
182
  detail view's content is `onEnterView`, NEVER `onStart` — hydrating at creation
@@ -183,7 +194,7 @@ The rules, in the order they bite:
183
194
  never travels through a model. Use it for "fill this card with its own record".
184
195
 
185
196
  ```yaml
186
- # manifest.yaml — the shipped rx/sab/components/product-card
197
+ # manifest.yaml — the shipped product-card exemplar
187
198
  lifecycle:
188
199
  - phase: onEnterView
189
200
  layouts: [ detail ] # STATE names; the grid state never wakes it
@@ -199,7 +210,7 @@ The rules, in the order they bite:
199
210
  - **`allowedHosts` in the manifest is NOT optional**: a hook that makes requests
200
211
  without it is a lint ERROR (deny by default). Credentials are named by type in the
201
212
  manifest and resolved server-side from encrypted storage; **a key never sits in
202
- `rx/`** (a shared design folder) and never reaches the client.
213
+ `design/`** (a shared design folder) and never reaches the client.
203
214
  - A phase fires once per instance; enrichment streams in (no blocked render); errors
204
215
  fail quietly, leaving whatever the card already had.
205
216
 
@@ -232,14 +243,13 @@ The rules, in the order they bite:
232
243
  LEGACY form. It still runs for handlers that predate declared calls, but it is **never
233
244
  the one to copy**: a script fetches any host with a key from the environment, and the
234
245
  calls runtime settles both questions.
235
- Exemplar: `rx/yasisland/components/restaurant-card/` (maps lookup, declared calls).
236
246
 
237
- ## Discovery meta: write it to RANK (docs/nodes/node-discoverability.md)
247
+ ## Discovery meta: write it to RANK ([node-discoverability](https://docs.unoverse.ai/nodes/node-discoverability.md))
238
248
 
239
249
  A component with a manifest is an **MCP app**: spatial embeds `` `title. whenToUse||description
240
250
  [category]` `` and ranks it against the USER'S OWN WORDS (`findIntent`). The meta is the
241
251
  selection text, not documentation. Weak meta makes a working component invisible. The
242
- strict contract (doc 14, applies verbatim):
252
+ strict contract (that page, applied verbatim):
243
253
 
244
254
  - **`title`** = the thing itself, human display name, no mechanism, no org prefix.
245
255
  - **`description`** = what it IS, ONE line ≤120 chars, the listing subtitle; never
@@ -260,7 +270,8 @@ component. Its key nouns/verbs must appear in `whenToUse`'s first sentence.
260
270
 
261
271
  ## Workflow
262
272
 
263
- 1. Study `rx/sab/components/product-card`; copy its folder shape.
273
+ 1. Study the closest installed exemplar (Studio MCP `listDefinitions` → `readDefinition`;
274
+ monorepo: `design/sab/components/product-card`); copy its folder shape.
264
275
  2. Declare the state tree first: public states on top, steps nested, each state owning
265
276
  its layout (same-name default, `layout:` only when the filename differs).
266
277
  3. Write the envelope + manifest + layouts; root = `Switch on view`; every shown thing
@@ -1,13 +1,19 @@
1
1
  # Playbook: custom workflow nodes
2
2
 
3
- **Read first, in order:**
4
-
5
- 1. `docs/nodes/CLAUDE.md`: the authoritative agent guide. The folder, the eight
6
- non-negotiable rules, every file with an example, and the lint-error-to-fix table.
7
- 2. `docs/nodes/manifest-nodes.md`: the format in full, including the run context.
8
- 3. `docs/nodes/node-discoverability.md`: **read before writing `whenToUse`**.
9
- 4. As needed: `credentials.md`, `who-can-run-it.md`, `config-schema.md`, `service-connectors.md`,
10
- `mcp-services.md`, `signal-routing.md`, `testing-nodes.md`.
3
+ **Read first, in order** (all served at docs.unoverse.ai; append `.md` for raw markdown):
4
+
5
+ 1. [nodes/CLAUDE.md](https://docs.unoverse.ai/nodes/CLAUDE.md): the authoritative agent
6
+ guide. The folder, the eight non-negotiable rules, every file with an example, and
7
+ the lint-error-to-fix table.
8
+ 2. [nodes/manifest-nodes](https://docs.unoverse.ai/nodes/manifest-nodes.md): the format in full, including the run context.
9
+ 3. [nodes/node-discoverability](https://docs.unoverse.ai/nodes/node-discoverability.md): **read before writing `whenToUse`**.
10
+ 4. As needed: [credentials](https://docs.unoverse.ai/nodes/credentials.md),
11
+ [who-can-run-it](https://docs.unoverse.ai/nodes/who-can-run-it.md),
12
+ [config-schema](https://docs.unoverse.ai/nodes/config-schema.md),
13
+ [service-connectors](https://docs.unoverse.ai/nodes/service-connectors.md),
14
+ [mcp-services](https://docs.unoverse.ai/nodes/mcp-services.md),
15
+ [signal-routing](https://docs.unoverse.ai/nodes/signal-routing.md),
16
+ [testing-nodes](https://docs.unoverse.ai/nodes/testing-nodes.md).
11
17
 
12
18
  This playbook adds the workflow around those docs. It never overrides them.
13
19
 
@@ -20,7 +26,7 @@ is a folder of YAML files that the platform interprets.
20
26
  missing a capability. Say so and stop, rather than reaching for code.
21
27
 
22
28
  ```
23
- apps/unoverse/nodes/<package>/
29
+ nodes/<package>/
24
30
  package.yaml # name, category, allowedHosts, requires
25
31
  credentials/
26
32
  <name>Credential.yaml # the SHAPE of a credential, never a value
@@ -79,7 +85,7 @@ package only for a new integration.
79
85
  ```
80
86
 
81
87
  `required: false` is right for almost every node: the trigger already decided who was let
82
- in. See `docs/nodes/who-can-run-it.md`.
88
+ in. See [who-can-run-it](https://docs.unoverse.ai/nodes/who-can-run-it.md).
83
89
  3. **Write `interface.yaml`.** Name outputs for what they carry, because those names become
84
90
  what someone types in a template.
85
91
  4. **Write `config.yaml`.** Every value the workflow supplies needs `ui:field: template`. A
@@ -107,7 +113,7 @@ package only for a new integration.
107
113
  5. **Write `api/run.yaml`.** A list, each entry named. Include `error` wherever the service
108
114
  can answer 200 with a failure body. Where one call is really many, name the capability on
109
115
  the call rather than trying to express a loop: `paginate`, `chunk`, `poll` or `state`.
110
- See `docs/nodes/calls-that-loop.md`, and declare each one in `requires`. A call
116
+ See [calls-that-loop](https://docs.unoverse.ai/nodes/calls-that-loop.md), and declare each one in `requires`. A call
111
117
  authenticates with `credential: { scheme, token }`, which is OUTBOUND: how the node proves
112
118
  itself to the vendor. It is not `node.yaml`'s `auth`, which is who may run the node.
113
119
  6. **Write `api/events.yaml`.** One row per output connector, in the order `interface.yaml`