@unoverse-platform/studio 0.1.45 → 0.1.46
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/local/mcp.ts +5 -1
- package/local/scaffold.mjs +4 -1
- package/package.json +1 -1
- package/vendor/skills/unoverse-create/SKILL.md +61 -27
- package/vendor/skills/unoverse-create/references/agent-skill.md +8 -5
- package/vendor/skills/unoverse-create/references/block.md +3 -2
- package/vendor/skills/unoverse-create/references/brand.md +10 -9
- package/vendor/skills/unoverse-create/references/component.md +33 -22
- package/vendor/skills/unoverse-create/references/node.md +17 -11
- package/vendor/skills/unoverse-create/references/template.md +13 -8
- package/vendor/skills/unoverse-prompt-component/SKILL.md +22 -17
package/local/mcp.ts
CHANGED
|
@@ -63,7 +63,11 @@ export interface McpDeps {
|
|
|
63
63
|
vocabulary: () => Promise<unknown>;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
const SKILL_DIRS = [
|
|
66
|
+
const SKILL_DIRS = [
|
|
67
|
+
"vendor/skills/unoverse-create",
|
|
68
|
+
"../../.claude/skills/unoverse-create", // running from source: the authoring home
|
|
69
|
+
"../../prompts/skills/unoverse-create",
|
|
70
|
+
];
|
|
67
71
|
|
|
68
72
|
/** The vendored authoring skill, which is how an agent learns the rules before writing. */
|
|
69
73
|
function authoringSkill(appRoot: string): string {
|
package/local/scaffold.mjs
CHANGED
|
@@ -85,7 +85,10 @@ export function createFirstProject(home, name) {
|
|
|
85
85
|
|
|
86
86
|
// The Claude Code authoring skills, so "open the folder and describe what you want"
|
|
87
87
|
// works in every Studio project from its first minute. Vendored into the published
|
|
88
|
-
// app (vendor/skills, written by prepack
|
|
88
|
+
// app (vendor/skills, written by prepack from the monorepo's .claude/skills, the one
|
|
89
|
+
// authoring home, mirrored publicly to github.com/unoverse-platform/skills); read
|
|
90
|
+
// from that home when running as source. `unoverse update` refreshes installed
|
|
91
|
+
// copies from the public mirror.
|
|
89
92
|
//
|
|
90
93
|
// ALL of them. One Studio project is where a person designs a brand AND writes the node
|
|
91
94
|
// behind it, so they need the whole set: naming one skill here shipped the authoring
|
package/package.json
CHANGED
|
@@ -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
|
|
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 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
|
|
9
|
-
|
|
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
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
13
|
+
| `rx/` | **Design** (data, not code): per-project components, templates + styles; each project is `rx/<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 `rx/<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 `rx/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
|
|
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
|
|
41
|
-
|
|
42
|
-
(
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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://
|
|
53
|
-
[
|
|
54
|
-
(https://
|
|
55
|
-
|
|
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://
|
|
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
|
-
|
|
66
|
-
words dominate, disqualify by property
|
|
67
|
-
the summary; read
|
|
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,7 +103,7 @@ 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://
|
|
106
|
+
[UNOVERSE_CONFORMANCE](https://docs.unoverse.ai/unoverse/UNOVERSE_CONFORMANCE.md).
|
|
76
107
|
|
|
77
108
|
| Artifact | To see it live |
|
|
78
109
|
|---|---|
|
|
@@ -81,9 +112,12 @@ messages; justify any warning), and the conformance checklist is
|
|
|
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
|
-
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
>
|
|
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 rx/ 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
@@ -16,8 +16,8 @@ client. They share one discipline and one failure mode.
|
|
|
16
16
|
`rx/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 (`
|
|
20
|
-
|
|
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**
|
|
@@ -87,8 +87,9 @@ Its theme is then served at `unoverse://theme/<neworg>/light`, and a template po
|
|
|
87
87
|
|
|
88
88
|
## 4. Anatomy of an org pack (`rx/<org>/styles/`)
|
|
89
89
|
|
|
90
|
-
Read
|
|
91
|
-
|
|
90
|
+
Read an installed org pack as the worked example (Studio MCP `listOrgs` →
|
|
91
|
+
`listDefinitions`; in the platform monorepo, `rx/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
|
|---|---|---|
|
|
@@ -137,8 +138,8 @@ 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
|
|
141
|
-
|
|
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
|
|
|
@@ -149,13 +150,13 @@ place. Work through them in order.
|
|
|
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://
|
|
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://
|
|
158
|
+
[UNOVERSE_AUTHORING](https://docs.unoverse.ai/unoverse/UNOVERSE_AUTHORING.md).
|
|
158
159
|
Which tier owns what:
|
|
159
|
-
[UNOVERSE_LAYERS](https://
|
|
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
1
|
# Playbook: Components & Atoms (rx data)
|
|
2
2
|
|
|
3
|
-
**Read first:**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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: `rx/sab/components/product-card`.)
|
|
8
13
|
|
|
9
14
|
## The anatomy
|
|
10
15
|
|
|
11
16
|
```
|
|
12
|
-
|
|
17
|
+
rx/<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
|
-
(
|
|
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`.
|
|
105
|
-
`
|
|
106
|
-
|
|
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;
|
|
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** (
|
|
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 `rx/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**
|
|
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,12 +167,13 @@ 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
|
|
170
|
+
continuously-enriched pages.
|
|
161
171
|
7. **Tokens only** (LAW 1) + closed style keys + real space-scale steps
|
|
162
|
-
(
|
|
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
|
-
|
|
165
|
-
[UNOVERSE_AUTHORING](https://
|
|
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
179
|
a hook is data like everything else in `rx/`. It's for DATA, not styling or logic.
|
|
@@ -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
|
|
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
|
|
@@ -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 (
|
|
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
|
|
273
|
+
1. Study the closest installed exemplar (Studio MCP `listDefinitions` → `readDefinition`;
|
|
274
|
+
monorepo: `rx/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.
|
|
6
|
-
non-negotiable rules, every file with an example, and
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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`
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# Playbook: Templates (MCP apps)
|
|
2
2
|
|
|
3
|
-
**Read first:**
|
|
4
|
-
|
|
5
|
-
(the
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
**Read first:** [design/templates](https://docs.unoverse.ai/design/templates.md)
|
|
4
|
+
(layouts + manifest-as-envelope) +
|
|
5
|
+
[design/state](https://docs.unoverse.ai/design/state.md) (the state model). Deep law:
|
|
6
|
+
[UNOVERSE_STATE_MODEL](https://docs.unoverse.ai/unoverse/UNOVERSE_STATE_MODEL.md) §5
|
|
7
|
+
(the six rules), [UNOVERSE_LAYERS](https://docs.unoverse.ai/unoverse/UNOVERSE_LAYERS.md).
|
|
8
|
+
Everything in the component playbook (closed vocabulary, tokens, scale steps) applies
|
|
9
|
+
here too. **Study an exemplar before writing**: read the closest installed template
|
|
10
|
+
through Studio's MCP (`listDefinitions` kind `template` → `readDefinition`); the
|
|
11
|
+
canonical manifest tree is inlined below. (Monorepo: `rx/sab/templates/sab-chat-layout`.)
|
|
8
12
|
|
|
9
13
|
## THE model: the component drives; the template reacts by name
|
|
10
14
|
|
|
@@ -23,7 +27,7 @@ template knows it = a base substate or template state.**
|
|
|
23
27
|
|
|
24
28
|
## The manifest states tree: one declaration, everything follows
|
|
25
29
|
|
|
26
|
-
The canonical shape (
|
|
30
|
+
The canonical shape (a chat layout: a base arrangement plus a reaction ladder):
|
|
27
31
|
|
|
28
32
|
```yaml
|
|
29
33
|
states:
|
|
@@ -64,7 +68,7 @@ states:
|
|
|
64
68
|
## The anatomy: manifest-only (NO `<name>.yaml` envelope)
|
|
65
69
|
|
|
66
70
|
```
|
|
67
|
-
|
|
71
|
+
rx/<project>/templates/<name>/
|
|
68
72
|
manifest.yaml # THE ENVELOPE: the whole app contract, incl. the states tree
|
|
69
73
|
layouts/ # the ARRANGEMENTS: one per state that owns one (same-name default)
|
|
70
74
|
main.yaml # the base: the core alone
|
|
@@ -84,7 +88,8 @@ ComponentSlot selecting the matching view.
|
|
|
84
88
|
|
|
85
89
|
Required: `name` · `description` (what it IS, ≤120) · `whenToUse` (utterance-shaped: the
|
|
86
90
|
USER's words; findIntent ranks on it, the OPENING words carry the ranking; disqualify by
|
|
87
|
-
property, never by naming siblings; full contract:
|
|
91
|
+
property, never by naming siblings; full contract:
|
|
92
|
+
[design/templates](https://docs.unoverse.ai/design/templates.md) §Naming) ·
|
|
88
93
|
`category` · `defaultState: "template"` (the app-level LOAD state, a protocol field,
|
|
89
94
|
nothing to do with component views) · `inputSchema` · `layout` (the BASE layout name) ·
|
|
90
95
|
`states` (the tree above) · `binding` (`workflow` + `trigger`: the app OWNS its
|
|
@@ -19,19 +19,23 @@ one you wrote.
|
|
|
19
19
|
|
|
20
20
|
## Step 1 (do this first, and again at the end): compile the schema
|
|
21
21
|
|
|
22
|
+
In the **platform monorepo**, run the bundled compiler from the repo root (it resolves
|
|
23
|
+
`@unoverse-platform/base`):
|
|
24
|
+
|
|
22
25
|
```bash
|
|
23
26
|
cp .claude/skills/unoverse-prompt-component/verify.ts ./verify.tmp.ts
|
|
24
27
|
npx tsx verify.tmp.ts <org>/<component> # e.g. better-alpha/deal-page
|
|
25
28
|
rm -f verify.tmp.ts
|
|
26
29
|
```
|
|
27
30
|
|
|
28
|
-
It
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Then, always: `node -e "import('./packages/base/src/lint/rx/index.mjs').then(m=>{const r=m.lintDefinitions();console.log(r.problems.length);r.problems.forEach(p=>console.log(p.level,p.file,p.msg))})"`
|
|
31
|
+
It prints every part the agent can fill, its description, and its limits. Read that
|
|
32
|
+
output, not the YAML. Then run the rx lint
|
|
33
|
+
(`node -e "import('./packages/base/src/lint/rx/index.mjs').then(m=>{const r=m.lintDefinitions();console.log(r.problems.length);r.problems.forEach(p=>console.log(p.level,p.file,p.msg))})"`).
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
In a **Studio developer workspace**, the same linter runs as Studio's publish lint (or
|
|
36
|
+
the authoring MCP's `lintProject` tool); it catches two of the five traps below by
|
|
37
|
+
name. Read what it says rather than silencing it, and always do the final proof in
|
|
38
|
+
Step 6: one real fill.
|
|
35
39
|
|
|
36
40
|
---
|
|
37
41
|
|
|
@@ -73,8 +77,9 @@ are briefed or neither is.
|
|
|
73
77
|
|
|
74
78
|
## Step 3: put the brief on the node that BINDS the field
|
|
75
79
|
|
|
76
|
-
This is the rule from the rulebook
|
|
77
|
-
|
|
80
|
+
This is the rule from the rulebook
|
|
81
|
+
([design/CLAUDE.md](https://docs.unoverse.ai/design/CLAUDE.md) §3) and it is where
|
|
82
|
+
authored briefs go to die.
|
|
78
83
|
|
|
79
84
|
```yaml
|
|
80
85
|
# COMPILES
|
|
@@ -114,7 +119,7 @@ silence it. Move them to the `Each`.
|
|
|
114
119
|
|
|
115
120
|
### Trap 3: a briefed atom drags a collection into the schema
|
|
116
121
|
|
|
117
|
-
Briefs are collected from the **expanded** tree (
|
|
122
|
+
Briefs are collected from the **expanded** tree (the platform's brief collector).
|
|
118
123
|
An `Each` is recorded whenever its template has briefed fields, whether or not the `Ref`
|
|
119
124
|
itself carries a brief.
|
|
120
125
|
|
|
@@ -135,8 +140,8 @@ each other.
|
|
|
135
140
|
### Trap 5: counts that are layout, not preference
|
|
136
141
|
|
|
137
142
|
If a strip renders `columns: "4"`, then `minItems: 4, maxItems: 4`. Note that `minItems`
|
|
138
|
-
is **guidance only**: the compiler appends it to the description and never gates
|
|
139
|
-
|
|
143
|
+
is **guidance only**: the schema compiler appends it to the description and never gates
|
|
144
|
+
on it. `maxItems` and `maxLength` are hard. A count that is
|
|
140
145
|
genuinely structural cannot be enforced by the schema alone, so say so in the description.
|
|
141
146
|
|
|
142
147
|
---
|
|
@@ -159,18 +164,18 @@ abbreviate and never substitute characters. Say so in the page-level brief.
|
|
|
159
164
|
## Step 5: the workflow door
|
|
160
165
|
|
|
161
166
|
If the component is filled by an agent inside a workflow rather than in chat, read
|
|
162
|
-
`
|
|
167
|
+
`UNOVERSE_MCP_TEMPLATE_PROTOCOL.md` §The workflow door (platform spec; in the monorepo
|
|
168
|
+
at `docs/unoverse/`). The load-bearing law:
|
|
163
169
|
**a blank part is a valid final state.** A part the source does not cover stays blank, the
|
|
164
170
|
`visibleWhen` guards render around it, and the page is complete. Gaps are orientation on
|
|
165
|
-
the first read, never standing debt.
|
|
166
|
-
|
|
167
|
-
every turn to `maxTurns` and returns an empty text output.
|
|
171
|
+
the first read, never standing debt. The platform's brief service implements this; a
|
|
172
|
+
component that fights it burns every turn to `maxTurns` and returns an empty text output.
|
|
168
173
|
|
|
169
174
|
### The agent node's system prompt stays ROLE-ONLY
|
|
170
175
|
|
|
171
176
|
The service port already delivers everything else when the agent connects: the fill
|
|
172
|
-
protocol (
|
|
173
|
-
are finished, stop when done
|
|
177
|
+
protocol (the brief service's `getSchema` instructions: survey once, batch fills, blanks
|
|
178
|
+
are finished, stop when done; folded into the agent's system prompt by the service router),
|
|
174
179
|
the content rules (each part's compiled brief), and the grounding law (injected into the
|
|
175
180
|
schema description). A workflow agent's system prompt therefore carries ONE line of role
|
|
176
181
|
("You fill the page from this run's documents; the service's instructions and each part's
|