Skip to main content
Three enforcement layers catch mistakes for you; one checklist covers what only you can judge. Validation lives in Studio, because Studio is the only thing that publishes: the lint rules below run automatically when you publish, and nothing with errors ships. Errors block; warnings flag judgment calls (e.g. an untyped global slot); hints suggest niceties (a missing states fixture). Each message cites the doc that owns the rule.

Layer 1: The JSON Schema (as you type)

rx/_schema/unoverse.schema.json validates every definition in your editor (wiring in 01). Structural, zero false positives. It catches:
  • missing envelope fields (unoverse, kind, name, root; components also need category: discovery meta lives in the manifest)
  • an unknown primitive type (the closed set is encoded)
  • broken primitives: Switch without cases, Each without template + a list (literal items or bind.items), Ref without ref, ComponentSlot without select
  • illegal conditions: only eq / ne / in / truthy exist; and/or/arithmetic are rejected by design (derive in the node, 03)
It validates two shapes: envelope files (with the unoverse field) and bare node partials (layouts/, states/, components/, atoms). One-off sweep of everything from the CLI, if you want it (needs ajv once: npm i -D ajv at the repo root; Studio runs the same schema as you type):

Layer 2: The lint rules (Studio, at publish)

The same rules the platform’s guard tests enforce in CI, run by Studio before anything publishes: The platform’s own CI additionally runs the theme-contract and discoverability-meta guards, and the SDK build enforces the closed set at the renderer level: you can’t drift past them even if a rule were missed at publish.

Layer 3: Your judgment (the conformance checklist)

What no linter can decide: audit every artifact against this before calling it done: Structure
  • Structure is earned: flat if it can be; components//states//layouts/ only when the shape demands them
  • Few shallow discriminants, not boolean soup; same-shape states collapsed into one data-driven state
  • No self-guarding states; mutually exclusive views in ONE Switch
Data
  • Every bind has a prop/state key with a default
  • Derived values computed in the node, sent as plain fields: no logic simulated in the definition
State (04)
  • Reaction contract respected: a component writes only its own slice; template surfaces react via select.where; setTemplateValue only for the template’s own chrome
  • Locked state respected: lifecycle from derived flags, voice via callState, host chrome via props
Templates (05)
  • No component-type rules in slots; reaction surfaces select by where; components own their size and faces
  • Manifest binds the workflow; whenToUse outcome-first, disqualifies by property
Style (06)
  • Semantic tokens only; no invented component-named tokens

Ship

Publishing happens in Studio and nowhere else: assets never deploy through the core platform:
  1. Preview, mock: prop defaults + the state picker; exercise every state fixture.
  2. Preview, live (07): stream real data through it, watch the stream log stay clean. The preview runs the production path, so it is the release test.
  3. Publish: Studio lints (Layer 2 blocks on any error) and publishes to your universe over the API, authenticated by your sign-in to that universe (publishing is a specific permission on your account). The item is live in every canvas immediately: no build, no restart, no deploy.

Next: 09, Troubleshooting.