@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 CHANGED
@@ -63,7 +63,11 @@ export interface McpDeps {
63
63
  vocabulary: () => Promise<unknown>;
64
64
  }
65
65
 
66
- const SKILL_DIRS = ["vendor/skills/unoverse-create", "../../prompts/skills/unoverse-create"];
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 {
@@ -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); read from the repo when running as source.
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@unoverse-platform/studio",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "type": "module",
5
5
  "description": "Unoverse Studio: the local authoring app. Reads your own rx/ folder, no backend.",
6
6
  "scripts": {
@@ -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 (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 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
+ | `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 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,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://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
  |---|---|
@@ -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
- 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 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
- 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.
@@ -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 (`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**
@@ -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 `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, `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 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
 
@@ -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://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
1
  # Playbook: Components & Atoms (rx 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: `rx/sab/components/product-card`.)
8
13
 
9
14
  ## The anatomy
10
15
 
11
16
  ```
12
- apps/unoverse/rx/<project>/components/<name>/
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
- (`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 `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** (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,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; 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
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 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
@@ -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: `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. `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`
@@ -1,10 +1,14 @@
1
1
  # Playbook: Templates (MCP apps)
2
2
 
3
- **Read first:** `docs/design/templates.md` (layouts + manifest-as-envelope) +
4
- `docs/design/state.md` (the state model). Deep law: `UNOVERSE_STATE_MODEL.md` §5
5
- (the six rules), `UNOVERSE_LAYERS.md`. Everything in the component playbook (closed
6
- vocabulary, tokens, scale steps) applies here too. **Study the exemplar before
7
- writing**: `rx/sab/templates/sab-chat-layout` is the canonical State Model v2 template.
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 (`rx/sab/templates/sab-chat-layout/manifest.yaml`):
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
- apps/unoverse/rx/<project>/templates/<name>/
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: docs/design/05 §Naming) ·
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 must run from the repo root (it resolves `@unoverse-platform/base`). It prints every
29
- part the agent can fill, its description, and its limits. Read that output, not the YAML.
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
- The linter catches two of the five traps below by name. Read what it says rather than
34
- silencing it.
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 (docs/design/CLAUDE.md §3) and it is where authored
77
- briefs go to die.
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 (`packages/base/src/markdown/collect.ts`).
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 on it
139
- (`server/src/mcp/briefSchema.ts`). `maxItems` and `maxLength` are hard. A count that is
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
- `docs/unoverse/UNOVERSE_MCP_TEMPLATE_PROTOCOL.md` §The workflow door. The load-bearing law:
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. This is implemented in
166
- `server/src/runtime/components/executor/briefService.ts`; a component that fights it burns
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 (`briefService.ts` `getSchema.instructions` — survey once, batch fills, blanks
173
- are finished, stop when done — folded into the agent's system prompt by MCPServiceRouter),
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