@unoverse-platform/studio 0.1.25 → 0.1.27
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/package.json +1 -1
- package/screens/AtomsView.tsx +40 -2
- package/screens/ComponentsView.tsx +112 -21
- package/screens/NativeAppHost.tsx +8 -4
- package/screens/SourceView.tsx +1 -1
- package/screens/TemplatesView.tsx +52 -22
- package/screens/states.ts +27 -4
- package/src/PublishDialog.tsx +1 -1
- package/vendor/sdk/appEngine.tsx +44 -3
- package/vendor/sdk/lib/UnoverseComponent.tsx +1 -1
- package/vendor/sdk/lib/core/actions.ts +20 -6
- package/vendor/sdk/lib/core/analytics.ts +89 -0
- package/vendor/sdk/lib/core/client.ts +1 -1
- package/vendor/sdk/lib/core/index.ts +30 -0
- package/vendor/sdk/lib/core/store.ts +65 -38
- package/vendor/sdk/lib/core/templates.ts +252 -0
- package/vendor/sdk/lib/core/types.ts +40 -7
- package/vendor/sdk/lib/index.ts +9 -0
- package/vendor/sdk/lib/orb/ThinkingOrb.tsx +127 -0
- package/vendor/sdk/lib/orb/engine/braid.ts +50 -0
- package/vendor/sdk/lib/orb/engine/core.ts +157 -0
- package/vendor/sdk/lib/orb/engine/lattice.ts +205 -0
- package/vendor/sdk/lib/orb/engine/morph.ts +138 -0
- package/vendor/sdk/lib/orb/engine/orbits.ts +80 -0
- package/vendor/sdk/lib/orb/engine/profiles.ts +162 -0
- package/vendor/sdk/lib/orb/engine/registry.ts +24 -0
- package/vendor/sdk/lib/orb/engine/ribbon.ts +93 -0
- package/vendor/sdk/lib/orb/engine/types.ts +14 -0
- package/vendor/sdk/lib/orb/engine/web.ts +98 -0
- package/vendor/sdk/lib/orb/index.ts +20 -0
- package/vendor/sdk/lib/orb/presets.ts +104 -0
- package/vendor/sdk/lib/orb/theme.ts +83 -0
- package/vendor/sdk/lib/orb/types.ts +73 -0
- package/vendor/sdk/lib/primitives.tsx +114 -5
- package/vendor/sdk/lib/render.tsx +25 -5
- package/vendor/sdk/lib/style.ts +6 -2
- package/vendor/sdk/lib/template.tsx +77 -218
- package/vendor/sdk/main.tsx +7 -0
- package/vendor/skills/unoverse-create/SKILL.md +18 -18
- package/vendor/skills/unoverse-create/references/agent-skill.md +12 -12
- package/vendor/skills/unoverse-create/references/block.md +5 -5
- package/vendor/skills/unoverse-create/references/component.md +150 -75
- package/vendor/skills/unoverse-create/references/node.md +1 -1
- package/vendor/skills/unoverse-create/references/template.md +142 -82
- package/vendor/skills/unoverse-create/references/workflow.md +15 -15
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: unoverse-create
|
|
3
|
-
description: Create or edit Unoverse platform artifacts
|
|
3
|
+
description: Create or edit Unoverse platform artifacts: UI components (including briefed/AI-filled components), templates, atoms and styles (rx/), 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, agent skill, prompt block, custom node, or workflow in an Unoverse starter repo.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Creating Unoverse Artifacts
|
|
7
7
|
|
|
8
|
-
You are helping a developer build in the **Unoverse developer carve-out
|
|
8
|
+
You are helping a developer build in the **Unoverse developer carve-out**, the three
|
|
9
9
|
editable folders mounted into the running platform:
|
|
10
10
|
|
|
11
11
|
| Folder | What lives there |
|
|
@@ -14,14 +14,14 @@ editable folders mounted into the running platform:
|
|
|
14
14
|
| `apps/unoverse/prompts/` | **Behavior**: agent skills (`skills/`) + prompt blocks (`blocks/`) |
|
|
15
15
|
| `apps/unoverse/nodes/` | **Logic**: custom workflow nodes, authored as YAML |
|
|
16
16
|
|
|
17
|
-
## Step 1
|
|
17
|
+
## Step 1: Identify the artifact, read its playbook
|
|
18
18
|
|
|
19
19
|
Decide what the user is creating, then **read the matching reference file before writing
|
|
20
20
|
anything**:
|
|
21
21
|
|
|
22
22
|
| The user wants… | Read |
|
|
23
23
|
|---|---|
|
|
24
|
-
| A UI component or shared atom
|
|
24
|
+
| A UI component or shared atom, incl. a page/card the AI fills with content (briefed) | `references/component.md` (§Briefs for AI-filled) |
|
|
25
25
|
| A template / app layout (chat surface, wizard shell) | `references/template.md` |
|
|
26
26
|
| A style / theme / token change | `references/component.md` §Tokens |
|
|
27
27
|
| An agent skill (behavior guide the AI follows) | `references/agent-skill.md` |
|
|
@@ -30,11 +30,11 @@ anything**:
|
|
|
30
30
|
| A workflow (wire nodes into an agent/pipeline on the Canvas) | `references/workflow.md` |
|
|
31
31
|
|
|
32
32
|
Workflows are the one artifact that is NOT files: they are built live on the running
|
|
33
|
-
platform through the `canvas` MCP (registered by this repo's `.mcp.json`)
|
|
34
|
-
|
|
33
|
+
platform through the `canvas` MCP (registered by this repo's `.mcp.json`).
|
|
34
|
+
The playbook covers the bind/build/test contract.
|
|
35
35
|
|
|
36
36
|
If it's ambiguous (e.g. "add a card that shows weather"), it's usually a **component**
|
|
37
|
-
(the UI) plus possibly a **node** (the data source)
|
|
37
|
+
(the UI) plus possibly a **node** (the data source). Confirm the scope with the user.
|
|
38
38
|
|
|
39
39
|
**New to design artifacts?** The guided learning journey at `docs/design/` (README →
|
|
40
40
|
quick-start → … → troubleshooting) teaches components, templates, the state model
|
|
@@ -42,32 +42,32 @@ quick-start → … → troubleshooting) teaches components, templates, the stat
|
|
|
42
42
|
`docs/design/CLAUDE.md` is the condensed agent rulebook. The playbooks here assume that
|
|
43
43
|
material.
|
|
44
44
|
|
|
45
|
-
## Step 2
|
|
45
|
+
## Step 2: Hard rules (apply to every artifact)
|
|
46
46
|
|
|
47
47
|
1. **Only edit the three carve-out folders.** Never touch, vendor, or work around the
|
|
48
|
-
SDK, engine, or server
|
|
49
|
-
SDK change, the answer is almost always "express it as data"
|
|
48
|
+
SDK, engine, or server. They are not yours to change. If a task seems to require an
|
|
49
|
+
SDK change, the answer is almost always "express it as data". Re-read the playbook.
|
|
50
50
|
2. **Docs are the source of truth.** The playbooks name the design journey and the deep
|
|
51
51
|
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),
|
|
52
52
|
[UNOVERSE_CONFORMANCE](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_CONFORMANCE.md)). They live in the docs repo
|
|
53
53
|
(https://github.com/unoverse-platform/docs), not in your repo. When the user's request goes beyond the playbook, read the named section rather
|
|
54
54
|
than improvising conventions.
|
|
55
|
-
3. **UI is data.** No pixels, hex colors, or CSS anywhere
|
|
56
|
-
in definitions
|
|
55
|
+
3. **UI is data.** No pixels, hex colors, or CSS anywhere: token names only. No logic
|
|
56
|
+
in definitions: anything computed (totals, formatting, chosen colors) is computed in
|
|
57
57
|
the node and sent as a plain field.
|
|
58
58
|
4. **Audit before you're done.** Every component/template must pass the conformance
|
|
59
59
|
checklist ([UNOVERSE_AUTHORING](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_AUTHORING.md) §9). Walk it item by item.
|
|
60
|
-
5. **Discovery meta is ranked, not read.** Any spatially-discoverable artifact
|
|
61
|
-
component or template with a manifest, an agent skill, a node
|
|
60
|
+
5. **Discovery meta is ranked, not read.** Any spatially-discoverable artifact (a
|
|
61
|
+
component or template with a manifest, an agent skill, a node) is selected by
|
|
62
62
|
embedding `title`/`name` + `whenToUse`/`description` + `category` against the user's
|
|
63
63
|
own words (or the planner's task). The strict contract is
|
|
64
64
|
`docs/nodes/node-discoverability.md`: outcome-first, utterance-shaped, opening
|
|
65
65
|
words dominate, disqualify by property (never name a sibling). Each playbook carries
|
|
66
66
|
the summary; read doc 14 before writing any meta.
|
|
67
67
|
|
|
68
|
-
## Step 3
|
|
68
|
+
## Step 3: Deploy loop (after the artifact is written)
|
|
69
69
|
|
|
70
|
-
Components and templates have NO scaffold command
|
|
70
|
+
Components and templates have NO scaffold command. Author them from the references
|
|
71
71
|
(mirror the closest existing artifact). Validation lives in Studio: its publish step
|
|
72
72
|
lints every definition before anything leaves the machine (0 errors required, doc-cited
|
|
73
73
|
messages; justify any warning), and the conformance checklist is
|
|
@@ -75,7 +75,7 @@ messages; justify any warning), and the conformance checklist is
|
|
|
75
75
|
|
|
76
76
|
| Artifact | To see it live |
|
|
77
77
|
|---|---|
|
|
78
|
-
| Component / atom / template / style | Preview in Studio (`unoverse studio`), then publish from Studio
|
|
78
|
+
| Component / atom / template / style | Preview in Studio (`unoverse studio`), then publish from Studio: the publish lint gates it |
|
|
79
79
|
| Existing component restyle/edit only | takes effect live (the SDK reads `rx/` directly) |
|
|
80
80
|
| Agent skill / prompt block | publish from Studio, same gate |
|
|
81
81
|
| Node | Studio's Nodes screen: Load sample → Run. A node is YAML; running it is the only proof (no build) |
|
|
@@ -85,4 +85,4 @@ on :4108 (`unoverse studio` from the project folder).
|
|
|
85
85
|
|
|
86
86
|
> In the platform monorepo (not the starter), the node docs live at
|
|
87
87
|
> `packages/docs/nodes/`, the design journey at `packages/docs/design/`, and the dev loop
|
|
88
|
-
> is just `npm run dev
|
|
88
|
+
> is just `npm run dev`. Component nodes synthesize from rx/ definitions at boot.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Playbook
|
|
1
|
+
# Playbook: Agent Skills (prompts/skills)
|
|
2
2
|
|
|
3
3
|
An agent skill is a markdown behavior guide the platform's AI agents discover and follow
|
|
4
4
|
at runtime (e.g. how to handle complaints, how to walk a user through a process). It is
|
|
@@ -8,7 +8,7 @@ at runtime (e.g. how to handle complaints, how to walk a user through a process)
|
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
apps/unoverse/prompts/skills/<skill-name>/
|
|
11
|
-
SKILL.md # required
|
|
11
|
+
SKILL.md # required: frontmatter + instructions
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The runtime scans this folder and serves skills to agents; agents match on the
|
|
@@ -19,9 +19,9 @@ The runtime scans this folder and serves skills to agents; agents match on the
|
|
|
19
19
|
```markdown
|
|
20
20
|
---
|
|
21
21
|
name: skill-name # required: lowercase, numbers, hyphens; max 64 chars
|
|
22
|
-
title: Skill Name # human display name
|
|
23
|
-
description: What this skill IS
|
|
24
|
-
whenToUse: The SELECTION text
|
|
22
|
+
title: Skill Name # human display name, shown as the item's title in listings
|
|
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.
|
|
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)
|
|
@@ -36,24 +36,24 @@ nodeTypes: [] # optional: workflow node types the skill relies
|
|
|
36
36
|
[The situations that should activate this behavior]
|
|
37
37
|
|
|
38
38
|
## Approach / Instructions
|
|
39
|
-
[Clear, step-by-step guidance. Concrete do/don't phrasing beats abstractions
|
|
40
|
-
|
|
39
|
+
[Clear, step-by-step guidance. Concrete do/don't phrasing beats abstractions.
|
|
40
|
+
Include example lines the agent can use verbatim where tone matters.]
|
|
41
41
|
|
|
42
42
|
## Examples
|
|
43
43
|
[Concrete before/after or dialogue examples]
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
Stick to the keys above. The markdown body must exist
|
|
46
|
+
Stick to the keys above. The markdown body must exist: a skill with frontmatter only
|
|
47
47
|
is invalid.
|
|
48
48
|
|
|
49
49
|
## Writing rules
|
|
50
50
|
|
|
51
51
|
1. **`whenToUse` does the routing; `description` says what it is.** Never blend them
|
|
52
52
|
into one field. `whenToUse` is embedded and ranked against user intent (spatial
|
|
53
|
-
discovery / findIntent)
|
|
54
|
-
("Handle an unhappy customer
|
|
53
|
+
discovery / findIntent). Write it **outcome-first in the user's vocabulary**
|
|
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
|
|
56
|
+
`docs/nodes/node-discoverability.md`. They apply to skills verbatim.
|
|
57
57
|
2. **Study an existing skill** in `prompts/skills/` and match its voice and structure.
|
|
58
58
|
3. Keep instructions outcome-focused and scannable: short sections, bulleted do/don't
|
|
59
59
|
lists, explicit escalation/stop conditions.
|
|
@@ -61,5 +61,5 @@ is invalid.
|
|
|
61
61
|
|
|
62
62
|
## Ship
|
|
63
63
|
|
|
64
|
-
`docker compose restart unoverse
|
|
64
|
+
`docker compose restart unoverse`. The runtime rescans skills at boot. Verify the
|
|
65
65
|
skill appears via the platform's skill listing (`unoverse check` for overall health).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Playbook
|
|
1
|
+
# Playbook: Prompt Blocks (prompts/blocks)
|
|
2
2
|
|
|
3
|
-
A prompt block is a **reusable prompt fragment
|
|
3
|
+
A prompt block is a **reusable prompt fragment**: a named piece of agent prompt text
|
|
4
4
|
(formatting rules, a reasoning pattern, a policy) that workflows compose into agent
|
|
5
5
|
system prompts. Smaller than a skill: a skill is a discoverable behavior guide; a block
|
|
6
6
|
is an ingredient.
|
|
@@ -23,7 +23,7 @@ description: What this block contributes to a prompt
|
|
|
23
23
|
tags: [topic, topic2]
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
[The prompt text itself
|
|
26
|
+
[The prompt text itself, written as direct instructions to the agent,
|
|
27
27
|
present tense, no meta-commentary about being a block.]
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -31,7 +31,7 @@ present tense, no meta-commentary about being a block.]
|
|
|
31
31
|
|
|
32
32
|
1. **Study an existing block** (e.g. `blocks/formatting/markdown-guidelines.md`) and
|
|
33
33
|
match its shape: tight sections, imperative bullets.
|
|
34
|
-
2. A block must stand alone
|
|
34
|
+
2. A block must stand alone: no references to other blocks, no assumptions about what
|
|
35
35
|
else is in the prompt.
|
|
36
36
|
3. Keep it generic and reusable. Anything workflow-specific belongs in that workflow's
|
|
37
37
|
own prompt, not in a shared block.
|
|
@@ -40,4 +40,4 @@ present tense, no meta-commentary about being a block.]
|
|
|
40
40
|
|
|
41
41
|
## Ship
|
|
42
42
|
|
|
43
|
-
`docker compose restart unoverse
|
|
43
|
+
`docker compose restart unoverse`. The runtime rescans blocks at boot.
|
|
@@ -1,116 +1,186 @@
|
|
|
1
|
-
# Playbook
|
|
1
|
+
# Playbook: Components & Atoms (rx data)
|
|
2
2
|
|
|
3
3
|
**Read first:** `docs/design/components.md` (the microapp anatomy) + `docs/design/state.md`
|
|
4
|
-
(the
|
|
5
|
-
`UNOVERSE_STATE_MODEL.md` §
|
|
6
|
-
|
|
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.
|
|
7
8
|
|
|
8
9
|
## The anatomy
|
|
9
10
|
|
|
10
11
|
```
|
|
11
|
-
apps/unoverse/rx
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
states/ # the component's PRIVATE steps (wizard questions) — listed in stateOrder
|
|
19
|
-
components/ # component-local shared partials (earned: 2+ states share a shape)
|
|
12
|
+
apps/unoverse/rx/<project>/components/<name>/
|
|
13
|
+
<name>.yaml # ROOT: envelope (name/category/nodeSize/outputs/props) + the STATE TREE + root
|
|
14
|
+
manifest.yaml # RENDER CONTRACT + discovery meta (title / description ≤120 /
|
|
15
|
+
# whenToUse utterance-shaped). Presence = spatially discoverable;
|
|
16
|
+
# the envelope NEVER duplicates the meta.
|
|
17
|
+
layouts/ # the DRAWINGS: one file per state that owns an arrangement
|
|
18
|
+
components/ # component-local shared partials (earned: 2+ layouts share a shape)
|
|
20
19
|
```
|
|
21
20
|
|
|
22
|
-
A **flat component** (simple card/chart) is just `<name>.
|
|
23
|
-
no folders. Structure is EARNED; start flat.
|
|
21
|
+
A **flat component** (simple card/chart) is just `<name>.yaml` + `root`: one drawing, no
|
|
22
|
+
manifest, no folders. Structure is EARNED; start flat.
|
|
24
23
|
|
|
25
|
-
## The
|
|
24
|
+
## The state model (v2): data + a state tree
|
|
25
|
+
|
|
26
|
+
A component is **data plus a state tree**; each state owns the layout(s) that draw it.
|
|
27
|
+
The root's `state:` block IS the design, readable top to bottom. The canonical shape
|
|
28
|
+
(`rx/sab/components/product-card/product-card.yaml`):
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
state:
|
|
32
|
+
view: # the PUBLIC axis
|
|
33
|
+
initial: products
|
|
34
|
+
states:
|
|
35
|
+
products: {} # no layout key: same-name default, {} is complete
|
|
36
|
+
detail: # the full page
|
|
37
|
+
layout: product # declared ONLY because the filename differs
|
|
38
|
+
on: step # a PRIVATE axis: the steps inside detail
|
|
39
|
+
initial: detail
|
|
40
|
+
states:
|
|
41
|
+
detail: { layout: product-detail }
|
|
42
|
+
apply: { layout: product-apply }
|
|
43
|
+
root:
|
|
44
|
+
type: Box
|
|
45
|
+
children:
|
|
46
|
+
- type: Switch
|
|
47
|
+
on: view # the root switches on `view`, ONE case active
|
|
48
|
+
cases:
|
|
49
|
+
products: { $include: layouts/products }
|
|
50
|
+
detail: { $include: layouts/product }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The rules, in the order they bite:
|
|
54
|
+
|
|
55
|
+
- **The axis is `view`.** The component's public state lives in its slice under `view`.
|
|
56
|
+
The root is a `Switch on view`; buttons transition with
|
|
57
|
+
`setValue { values: [{ key: view, value: detail }] }`. That write is the ENTIRE
|
|
58
|
+
transition machinery: no guards, no events, no per-component machines.
|
|
59
|
+
- **Public states are the top level of the `view` axis** and they are the template's
|
|
60
|
+
WHOLE vocabulary: everything templates can react to, nothing more. Steps nest
|
|
61
|
+
privately (`on: <key>` + `initial` + `states`), invisible outside the component.
|
|
62
|
+
The placement rule: **if the template must rearrange to show it, make it public;
|
|
63
|
+
if the template wouldn't move, nest it privately.** Privacy is the default;
|
|
64
|
+
promotion is deliberate.
|
|
65
|
+
- **Each state owns its layout(s).** `layout:` is OPTIONAL: a state with no declaration
|
|
66
|
+
draws the layout of its own name, and `{}` is a complete state. Write `layout:` only
|
|
67
|
+
when the filename differs. A layout is never addressable on its own; only its owning
|
|
68
|
+
state selects it. Name states for what they MEAN (`detail`), never for their drawing.
|
|
69
|
+
- **Shell vs steps.** A state's own layout is its SHELL, always on while the state is
|
|
70
|
+
active. Its nested substates are the STEPS, the only choices inside it, addressed by
|
|
71
|
+
state names (layout filenames are plumbing).
|
|
72
|
+
- **Variants**: one meaning, several arrangements:
|
|
73
|
+
`focused: { layouts: { horizontal: focused-h, vertical: focused-v } }`. Variants bind
|
|
74
|
+
the SAME fields; anything that changes meaning or bound data is a second state
|
|
75
|
+
(lint-enforced). The component picks its variant by the space it finds itself in
|
|
76
|
+
(container queries); templates influence it only by controlling the container.
|
|
77
|
+
- **Arrival: nobody sends a state.** The host template places the spawned actor: it
|
|
78
|
+
honors the component's declared `initial` when it has a matching state; otherwise it
|
|
79
|
+
scans its own states in declared order and takes the first name on the component's
|
|
80
|
+
public menu; no overlap and the component wakes in its own initial chain and renders
|
|
81
|
+
inline. The server compiles the tree to the public menu + initials it serves
|
|
82
|
+
(`publicStates`, `initialView`). Post-spawn, the template NEVER writes a component's
|
|
83
|
+
state.
|
|
84
|
+
- **Unmatched view = inline. Always.** An ignored public state does not stay put, it
|
|
85
|
+
FALLS INLINE in the flow. A step that must never move the page belongs in the nest,
|
|
86
|
+
not on the public menu.
|
|
87
|
+
- Legacy: v1's `defaultState` field and layout-name-sync are superseded; the SDK reads
|
|
88
|
+
`defaultState` only as a migration alias for `view`. New authoring writes `view`.
|
|
89
|
+
|
|
90
|
+
## The rules (Studio's publish lint enforces all of these; 0 errors required)
|
|
26
91
|
|
|
27
92
|
1. **Three homes for everything it shows** (AUTHORING §3):
|
|
28
93
|
- static content → **hardcoded literals** in the layout (`value`, literal `items: []` on
|
|
29
|
-
Each, `src`)
|
|
94
|
+
Each, `src`), never props, never `state`;
|
|
30
95
|
- internal view-state → the **`state` block, SCALARS ONLY** (`step`, `phase`,
|
|
31
|
-
`progressPct`)
|
|
96
|
+
`progressPct`): an array/object/URL in `state` is slop (workflow data → props;
|
|
32
97
|
static → hardcode);
|
|
33
98
|
- workflow-fed data → **`props` with `input: true`** (the `default` is the preview mock).
|
|
34
99
|
Usually empty.
|
|
35
|
-
- **Prop names = the data contract
|
|
100
|
+
- **Prop names = the data contract. Never invent them.** Source data hydrates props
|
|
36
101
|
BY NAME (as-is, no mapping); a name the source doesn't carry silently renders the
|
|
37
102
|
preview default (tell: title streams, image stays mock). Content-attached cards use
|
|
38
103
|
the writer vocabulary: `title` `tagline` `description` `bodyCopy` `introParagraph`
|
|
39
104
|
`primaryImage` `images` `link` `callToAction`. Guard:
|
|
40
105
|
`server/src/runtime/content-card-hydration.test.ts`; law:
|
|
41
106
|
`UNOVERSE_MCP_TEMPLATE_PROTOCOL.md` §Content-attached cards.
|
|
42
|
-
2. **Root = `Switch on
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
**Lifetime (OPTIONAL manifest flag)** — `"lifetime": "conversation"` marks a durable,
|
|
107
|
+
2. **Root = `Switch on view`**, cases per PUBLIC state, each `$include` of the state's
|
|
108
|
+
layout (the tree above). Keep an `inline` state for any component that lives in a
|
|
109
|
+
conversation flow: the flow is its placeholder there. One EXCEPTION is the
|
|
110
|
+
**single-state (permanent) component**: exactly ONE public state, NO inline. It
|
|
111
|
+
arrives in its surface, cannot leave it (no ✕, no other state), and is enriched in
|
|
112
|
+
place via data merges to one stable instance, e.g. a continuously-updated page
|
|
113
|
+
living in a template's focus state. Permanence is by construction, never by a
|
|
114
|
+
pinning rule.
|
|
115
|
+
**Lifetime (OPTIONAL manifest flag).** `"lifetime": "conversation"` marks a durable,
|
|
52
116
|
conversation-scoped surface (a cart, an itinerary, a composed page): the platform keys
|
|
53
|
-
its instance by the CONVERSATION (every re-call hydrates the SAME slice
|
|
117
|
+
its instance by the CONVERSATION (every re-call hydrates the SAME slice: merge, not
|
|
54
118
|
re-place) and the new-turn reset skips it, so it stays on screen until it is replaced,
|
|
55
119
|
closes itself, or a NEW TEMPLATE loads (the template swap is the hard refresh boundary).
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
120
|
+
Durable slices also opt out of `cancelBelow` retraction when a higher template state
|
|
121
|
+
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).
|
|
123
|
+
3. **State is local; the `view` is the interface** (STATE_MODEL §5). Private axes
|
|
124
|
+
(`step`, `phase`, …) are the component's own business; the template never sees them.
|
|
125
|
+
The only thing that crosses is the public `view`. The component writes ONLY its own
|
|
126
|
+
slice (`setValue`): expand = `setValue { view: "detail" }`; its expanded layout
|
|
127
|
+
carries its own ✕ back down. When a hosting template has a state of that name, the
|
|
128
|
+
instance **lifts out of the flow into that state's slot**. The SDK renders each
|
|
129
|
+
instance in exactly ONE placeholder, so you never hide a flow copy yourself (no
|
|
130
|
+
`hideBelow`, no overlay hack). ❌ NEVER `setTemplateValue` to open a surface
|
|
131
|
+
(the deprecated bridge, linted). ❌ Never guard a layout against its own state:
|
|
132
|
+
the tree owns "which state is active"; a layout re-checking its discriminant is
|
|
133
|
+
the double-guard smell the model removes.
|
|
134
|
+
4. **A new layout file is earned only by a different arrangement** (or a different set
|
|
135
|
+
of bound fields). Same shape, different values = ONE state, data-driven: seven wizard
|
|
136
|
+
questions sharing one shape are one `question` state whose data changes, never seven
|
|
137
|
+
files (LAYERS §4, §7).
|
|
138
|
+
5. **Atoms** (`rx/atoms/`, via `Ref`) are for shapes shared across components, authoring-time
|
|
69
139
|
only (the server expands them before serving; no Studio view). `Ref props` remaps FIELDS;
|
|
70
140
|
**`Ref with` passes LITERALS**: `{ "type": "Ref", "ref": "button", "with": { "label":
|
|
71
|
-
"Learn more", "icon": "arrowRight" }, "action": { … } }
|
|
141
|
+
"Learn more", "icon": "arrowRight" }, "action": { … } }`. A bind whose field is a `with`
|
|
72
142
|
key becomes a hardcoded attribute, a truthy `with` key drops a matching `visibleWhen`
|
|
73
143
|
guard (unprovided ⇒ that piece stays hidden), `{{key}}` style bindings take the value.
|
|
74
144
|
⚠ Icon: literal = `icon: "phone"`; bound = `bind: { name: field }`.
|
|
75
|
-
6. **Briefs
|
|
76
|
-
sits ON the node that renders what it describes, next to its `bind
|
|
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`
|
|
146
|
+
sits ON the node that renders what it describes, next to its `bind`, never in the
|
|
77
147
|
manifest, never a separate file:
|
|
78
148
|
- bound element → `"brief": { "description": "…", "maxLength": 60 }`
|
|
79
149
|
- Each → `"brief": { "description": "…", "minItems": 3, "maxItems": 5 }` (its template's
|
|
80
|
-
binds define the item shape
|
|
81
|
-
-
|
|
150
|
+
binds define the item shape; brief those fields too)
|
|
151
|
+
- layout/partial ROOT (no bind) → a plain-string brief = composition context
|
|
82
152
|
(ordering, refinement rules like "update, never discard").
|
|
83
153
|
The platform compiles briefs into the component's MCP tool schema (keys pass through
|
|
84
|
-
verbatim
|
|
85
|
-
spatial search results
|
|
154
|
+
verbatim; the brief uses JSON Schema's OWN vocabulary) and injects the grounding law (fill ONLY from
|
|
155
|
+
spatial search results, never invent). The server REFEREES (invalid/empty args →
|
|
86
156
|
instructive result, no render) and MIRRORS (success result returns the rendered page
|
|
87
|
-
for surgical refinement). **The schema IS the instruction channel
|
|
88
|
-
|
|
89
|
-
closed + linted. Pairs with the single-
|
|
157
|
+
for surgical refinement). **The schema IS the instruction channel.**
|
|
158
|
+
Write briefs, never side-channel prompts/skills for filling a component. Shape is
|
|
159
|
+
closed + linted. Pairs with the single-state permanent pattern (rule 2) for
|
|
90
160
|
continuously-enriched pages; exemplar: `rx/components/perfectday`.
|
|
91
161
|
7. **Tokens only** (LAW 1) + closed style keys + real space-scale steps
|
|
92
|
-
(`docs/design/06`)
|
|
93
|
-
8. **Lifecycle handlers
|
|
162
|
+
(`docs/design/06`): an invented step is silently broken CSS.
|
|
163
|
+
8. **Lifecycle handlers: a component's own server-side code** (deep law:
|
|
94
164
|
[UNOVERSE_AUTHORING](https://github.com/unoverse-platform/docs/blob/main/unoverse/UNOVERSE_AUTHORING.md) §3c). When a component needs live data from an
|
|
95
165
|
API or a computation (not from an AI = brief, not from the workflow = `input` props), it
|
|
96
166
|
may bring a small server-side script that runs when the component renders. It's for DATA,
|
|
97
167
|
not styling or logic-in-the-def.
|
|
98
168
|
- **Opt in via the manifest** (authorizes execution; auditable): `"lifecycle":
|
|
99
|
-
["onStart"]` in
|
|
100
|
-
- **The code is a sibling file named for the lifecycle
|
|
101
|
-
beside
|
|
169
|
+
["onStart"]` in the manifest. A handler file with no opt-in is INERT.
|
|
170
|
+
- **The code is a sibling file named for the lifecycle**: `onStart` → `onstart.js`,
|
|
171
|
+
beside the root file. Its **default export** is the handler.
|
|
102
172
|
- **`onStart`** is the only lifecycle today; it runs SERVER-SIDE when the instance is
|
|
103
173
|
created (the MCP render path), and whatever it returns is **merged into the instance
|
|
104
|
-
by prop name** (unknown keys inert
|
|
174
|
+
by prop name** (unknown keys inert, same contract as workflow-fed props). Return only
|
|
105
175
|
the fields you're filling; **never overwrite a curated prop** you didn't intend to.
|
|
106
176
|
- **`ctx` is a bounded sandbox**: `ctx.props` (the instance's current data),
|
|
107
177
|
`ctx.instanceId`, `ctx.env(name)` (server config). The **API key comes from
|
|
108
|
-
`ctx.env`, NEVER hardcoded
|
|
178
|
+
`ctx.env`, NEVER hardcoded**: `rx/` is a shared design folder; a secret in it leaks.
|
|
109
179
|
- Self-contained (the external call lives in the handler); errors never break the render.
|
|
110
|
-
Exemplar: `rx/
|
|
180
|
+
Exemplar: `rx/yasisland/components/restaurantcard/onstart.js` (Google Maps details).
|
|
111
181
|
|
|
112
182
|
```js
|
|
113
|
-
// onstart.js
|
|
183
|
+
// onstart.js: manifest opts in with "lifecycle": ["onStart"]
|
|
114
184
|
export default async function onStart(ctx) {
|
|
115
185
|
const key = ctx.env("SEARCHAPI_KEY"); // secret from server config
|
|
116
186
|
const res = await fetch(`https://…&q=${encodeURIComponent(ctx.props.title)}&api_key=${key}`);
|
|
@@ -119,34 +189,39 @@ no folders. Structure is EARNED; start flat.
|
|
|
119
189
|
}
|
|
120
190
|
```
|
|
121
191
|
|
|
122
|
-
## Discovery meta
|
|
192
|
+
## Discovery meta: write it to RANK (docs/nodes/node-discoverability.md)
|
|
123
193
|
|
|
124
194
|
A component with a manifest is an **MCP app**: spatial embeds `` `title. whenToUse||description
|
|
125
195
|
[category]` `` and ranks it against the USER'S OWN WORDS (`findIntent`). The meta is the
|
|
126
|
-
selection text, not documentation
|
|
196
|
+
selection text, not documentation. Weak meta makes a working component invisible. The
|
|
127
197
|
strict contract (doc 14, applies verbatim):
|
|
128
198
|
|
|
129
|
-
- **`title`** = the thing itself, human display name
|
|
130
|
-
- **`description`** = what it IS, ONE line ≤120 chars
|
|
199
|
+
- **`title`** = the thing itself, human display name, no mechanism, no org prefix.
|
|
200
|
+
- **`description`** = what it IS, ONE line ≤120 chars, the listing subtitle; never
|
|
131
201
|
"use when…" inside it.
|
|
132
202
|
- **`whenToUse`** = the selection text, **utterance-shaped**: the words a user would
|
|
133
|
-
actually say, outcome first
|
|
203
|
+
actually say, outcome first; the OPENING words dominate the embedding. Never
|
|
134
204
|
selector-shaped dev framing ("Pick when the user asks…"), never layout/mechanism first
|
|
135
|
-
("A single-
|
|
205
|
+
("A single-state page that…" ranks near rendering concepts and loses every intent query).
|
|
136
206
|
- **Disqualify by PROPERTY, never by naming a sibling** ("Not for answering a single
|
|
137
|
-
question, not for one item on its own")
|
|
207
|
+
question, not for one item on its own"). Naming rivals dates and tangles; the ranking
|
|
138
208
|
surfaces the alternative. A fallback/home surface never enumerates siblings' jobs
|
|
139
209
|
(the generalist trap).
|
|
140
210
|
- **`category`** = the domain of the JOB (Travel, Payments…), not the implementation.
|
|
141
211
|
- Meta embeds **AS-IS** (no LLM rewrite); editing `whenToUse` re-embeds on the next train.
|
|
142
212
|
|
|
143
213
|
Self-test before shipping: write the sentence the user would actually say to want this
|
|
144
|
-
component
|
|
214
|
+
component. Its key nouns/verbs must appear in `whenToUse`'s first sentence.
|
|
145
215
|
|
|
146
216
|
## Workflow
|
|
147
217
|
|
|
148
|
-
1. Study
|
|
149
|
-
2.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
218
|
+
1. Study `rx/sab/components/product-card`; copy its folder shape.
|
|
219
|
+
2. Declare the state tree first: public states on top, steps nested, each state owning
|
|
220
|
+
its layout (same-name default, `layout:` only when the filename differs).
|
|
221
|
+
3. Write the envelope + manifest + layouts; root = `Switch on view`; every shown thing
|
|
222
|
+
in its ONE home; transitions are `setValue` writes to `view` (or a private axis).
|
|
223
|
+
4. Publish from Studio; its lint must report 0 errors (the rules are v2-aware and cite
|
|
224
|
+
the doc for every rule).
|
|
225
|
+
5. Restart unoverse → Studio: mock (prop defaults + the state switcher reads the tree:
|
|
226
|
+
public states on top, steps beneath by state name), then live. Debug: stream log →
|
|
227
|
+
state inspector → definition; never guess.
|
|
@@ -161,7 +161,7 @@ the manifest describes the real service rather than something merely coherent.
|
|
|
161
161
|
returns: "return response.items.map(helpers.row)"
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
A helper sees ONLY its arguments and its sibling helpers
|
|
164
|
+
A helper sees ONLY its arguments and its sibling helpers, never `config`, `credentials`
|
|
165
165
|
or the caller's scope. Same sandbox, no extra authority.
|
|
166
166
|
|
|
167
167
|
## Do not
|