Status: draft standard simai.component-set 1.3.0
(contracts/standards/simai.component-set/1.3.0/standard.json; 1.0.0, 1.1.0,
1.1.1 and 1.2.0 are kept under contracts/standards/history; 1.1.1 changed no
requirement of 1.1.0; 1.2.0 adds pair ranges; 1.3.0 adds the exports list). The Loader
follows it since core.runtime 1.25.0 and the builder has a generic set
release route; the package references do not follow it yet. See "What is not
built yet" below.
Every Smart component outside the free SIMAI Framework lives in a component set (owner decision of 2026-11-03). A set is one repository that holds:
- the set's Smart components;
- its Docara documentation site;
- its demonstrations.
The set id is the repository name without ui-:
| Set | Repository | Licence | Families | State |
|---|---|---|---|---|
| admin | simai/ui-admin | simai-pro | admin | planned |
| view | simai/ui-view | simai-pro | view | pending the owner |
| chat | simai/ui-chat | simai-pro | chat | planned |
| simai/ui-mail | simai-pro | planned | ||
| editor | simai/ui-editor | simai-pro | editor | planned |
| chart | simai/ui-chart | simai-pro | chart | planned |
| effects | simai/ui-effects | framework (free) | effect | planned, first set |
view is a possible set of data views (table, kanban, calendar, gantt, list).
It is waiting for the owner's decision. Until then the registry holds the
family view for it rather than for admin.
A set describes itself in set.json at the root of its package folder, in the
format simai.framework.component-set (schema_version: 1). The schema is
contracts/schema/component-set.v1.schema.json, and the standard pins a frozen
copy of it.
{
"schema": "simai.framework.component-set",
"schema_version": 1,
"standard": {"id": "simai.component-set", "version": "1.0.0"},
"id": "effects",
"title": "SIMAI Effects",
"repository": "simai/ui-effects",
"licence": {"class": "framework", "spdx": "Apache-2.0"},
"framework": {"core_runtime": ">=1.25.0", "source_revision": "<40 hex>"},
"requires_sets": [],
"families": ["effect"],
"docs": "https://effects.simai.io/",
"components": [
{"element": "sf-effect", "rule": "cl-effect", "folder": "effect", "requires": ["cl-menu-context"]}
]
}The fields:
-
id: the repository name withoutui-. The same id appears in:- the lock;
- the rule file's
registerPackage; - the release tag;
- the npm name
@simai-studio/<id>; - the page declaration;
- the registry.
-
licence: one of two classes, each with exactly one SPDX expression.simai-pro:PolyForm-Small-Business-1.0.0 OR PolyForm-Noncommercial-1.0.0 OR LicenseRef-SIMAI-Commercial.framework:Apache-2.0.
-
framework: the one ui-source commit the set is built and tested against (the same commit asframework.lock.json), and the lowestcore.runtimeversion it needs. A release addspair(ui-<12 hex>). -
requires_sets: the other sets the set's components use, each as{id, range}.- There are no cycles, and the graph is at most two levels deep.
- The Loader never loads a required set on its own: the page declares it.
-
families: the name families the set owns. They must match the registry. -
components: every element the set holds, with:- its rule (
cl-<folder>); - its folder;
- the rules it needs;
- its dated former tags.
This list is the only description of the set's components. SIMAI Framework keeps no per-set reference, so a new component in a set needs no Framework release.
- its rule (
-
release: added by the release route. It holds<set>-<12 hex>, the npm version and the source commit.
A set that gives a scripted API -- what its bundle registers with
SF.RuleLoader.registerPackage('<set>', {exports}) and a page reads as
SF.package('<set>') -- lists every export in set.json under exports
(passport form 1.1.0, optional; owner decision D17, 2026-11-11). An entry has
the shape of SIMAI Framework's own core/contracts/public-js-api.json:
| Field | Value |
|---|---|
name |
the export's key: SF.package('<set>').<name> |
status |
public (a promise, renamed only through the set's journal) or internal |
layer |
set (the set's own function, on the set's Functions page) or component (a component's API, on the page of the element named in component) |
audience |
public (listed for authors) or internal (not listed); an internal status is never public |
kind |
object, function, class or value |
author |
the one line the site shows: one sentence, present tense, no history |
doc |
optional: the slug of the page that describes it |
summary, members, deprecated |
as in public-js-api |
The set's browser probe holds the list against the page:
import {checkSetExports} from '<ui-source>/scripts/set-exports-kit.mjs';
const problems = await checkSetExports(page, JSON.parse(readFileSync('set.json', 'utf8')));It reports an unlisted export, a listed name not exported, a wrong kind, a
missing member, an internal name listed for authors and a component export that
names no element of the set. tests/browser/component-sets.browser.mjs runs
the kit over the fixture set demo.
- Every element starts with
sf-and every rule withcl-. A set has no prefix of its own. - A name is
sf-<family>[-<block>][-<variant>](decisions/2026-11-02-smart-component-naming.mdin ui-control). Its family is the first word aftersf-. - The shared registry
contracts/registry/names.json(formatsimai.framework.name-registry) gives each family one owner: SIMAI Framework or one set. It also lists the names known in each family. - The registry holds names only. It has no files, versions or rules.
- CI reads the registry from ui-source
main. This is the one documented exception to "main is not a contract". - To take a family, change the registry before the set's first release that uses it. A new name inside a family the set already owns needs no registry change first.
- A name that breaks the rule today goes in
exceptions, with its reason and date. The gate refuses any break that is not listed. - Dated former tags (the tag alias table) are exempt until their
remove_after.
tests/component-set.contract.test.mjs checks that:
- no family has two owners;
- every registered name starts with a family its owner holds, or is a listed exception, and no exception is stale;
- the Framework's own
src/smart/*/smart.manifest.jsonelements are exactly the Framework's registered names; - every tag alias points at a registered name, and no former tag is a current name;
- the fixture passport (
tests/fixtures/component-set/set.json) takes the pinned form and agrees with the registry.
Open exceptions on 2026-11-03: sf-editor-inline and sf-editor-overlay.
They are SIMAI Framework components in the family editor, which belongs to
the set editor. One of two changes will close them:
- they move to the set;
- they are renamed through the change lifecycle.
A set does not ship what the Framework offers or should offer.
- The reusable part of a complex component is raised into the Framework as a Smart component of its own, and the set composes it.
- Basic parts are ordered from the Framework. Examples: a scale, a colour scheme, a token, a scrim, lazy loading of a part.
- Every component that enters a set, new or moved, is first reviewed against this rule. The review is recorded with the component.
- When ui-pro moves, the first candidates are
message-log,confirm-changeandadmin-menu.
These are the gates the Pro package passes today:
- the unit suite over the overlay of the pinned Framework;
- manifest probes;
- browser probes in a real browser;
- the first paint, watched frame by frame;
- linkage: passports whose version moves with the content hash, and the release inventory compared to the previous one;
- two byte-identical builds with a receipt;
- the passport checked against the registry at
main.
Each release is tagged <set>-<12 hex>, the first twelve hex digits of the
set commit. The tag is on the set's own repository and carries these assets:
package-<set>.tarand its.sha256;package-receipt.json;- the built
set.json.
The same bytes go to npm as @simai-studio/<set>.
Each release adds one row to the one compatibility index, with:
- the set;
- the release;
- the version;
- the Framework it was built over (
pair,source_revision) and the lowestcore.runtimeit needs, so it runs over every pair at or above it; - the sets it requires;
- the receipt digest.
No release is moved or rewritten.
When a product moves to a newer SIMAI Framework pair and the set's own source
did not change, the set gets no new version (owner decision 2026-11-07). The
release route runs ui-builder scripts/pair-receipt.py run over the existing
release:
- the released output, read from the Build commit, is the release archive;
- the pair's
core.runtimemeets the set's minimum, its shared runtime and free rules agree with the set, and the registry agrees with the passport; - the release source, repinned to the pair's ui-source in a local commit that
is never pushed, rebuilds to the released bytes apart from the passport's
framework.source_revision(otherwise the set needs a new release); - the set's own gates pass over the pair against the released bytes.
The result, a pair receipt (simai.framework.set-pair-receipt v1), is added to
the existing release as pair-receipt-<pair>.json, to the set repository as
contracts/pair-receipts/<release>/<pair>.json, and to the release's row of
the index as an entry of pairs. The release lock, the package receipt, the
passport and npm do not change. A consumer that pins exact pairs accepts the
set over a pair named either by its package receipt or by one of its pair
receipts.
A pair receipt rebuilds and gates the set over each pair. A pair range does not: it is written once per release, from the released bytes, and lists what those bytes take from the free pair at runtime (owner decision 2026-11-09, R1):
core_runtime: the passport's minimum;runtime: the shared runtime ABI and, per shared module, the names the bundles import throughSF.runtimeModule;relations: every relation of the set's rule file to a rule that is not the set's own, resolved on the pair to a free rule or through a live forward (smart-rule-forwards.v1.json);elements: the set's own tags, which no free rule may take;free_elements: the free tags the set's scripts render;core_folder: the set's package core, which the pair must not ship indistr/smart;variables: the--sf-*properties read bare (no fallback), not set by the set, declared by the pair's sheets;js_api: thewindow.SFnames called thatpublic-js-api.jsonlists as public.
The file, simai.framework.set-pair-range v1 with interface_sha256 (the
sha256 of the canonical JSON of requires) and not_covered, is written by
ui-builder scripts/set-pair-range.py write; the release route writes it with
every release over a free pair, as the asset pair-range.json, as
contracts/pair-ranges/<release>.json in the set repository and as range on
the release's index row. set-pair-range.py check --range <file> --ui <ui> --pair <ui-12> answers from the pair's distr alone. A consumer that pins
exact pairs may accept the set over a pair where every requirement holds,
after the exact pair and the pair receipts. What a range does not cover --
behaviour behind an unchanged interface, Framework code a bundle inlined, the
set's gates -- is left to the consumer's own browser probes at each repin and
to a full pair receipt once per release train.
- Format:
simai.framework.set-index,schema_version: 1(contracts/schema/set-index.v1.schema.json, pinned by the standard). A fixture istests/fixtures/component-set/index.json. - Where:
index.jsonof the npm package@simai-studio/sets. The Loader readshttps://cdn.jsdelivr.net/npm/@simai-studio/sets@1/index.json: the format's major is pinned, the rows grow. - Writer: the set release route,
ui-builder/scripts/set-index.py add, from the release's builtset.jsonand receipt. Nobody edits it by hand. - Reader: the Loader picks the newest release of a set whose
framework.core_runtimethis Core meets (core/js/sets.js,selectSetRelease). It does not readpairs. - A row's facts never change. Since 1.1.0 a row may carry
pairs, each{pair, source_revision, receipt_sha256, received_on}: the further pairs its unchanged release was re-receipted over, in the order received, written byset-index.py pair. Since 1.2.0 a row may carryrange,{sha256, interface_sha256}: the digests of its pair range file and of the interface it names, set once byset-index.py range. The Loader reads neither.
{
"schema": "simai.framework.set-index",
"schema_version": 1,
"sets": [{"id": "effects", "title": "SIMAI Effects", "repository": "simai/ui-effects",
"npm": "@simai-studio/effects", "licence": {"class": "framework", "spdx": "Apache-2.0"},
"docs": "https://effects.simai.io/"}],
"releases": [{"set": "effects", "release": "effects-<12 hex>", "version": "1.0.0",
"source_revision": "<40 hex>",
"framework": {"core_runtime": ">=1.25.0", "source_revision": "<40 hex>", "pair": "ui-<12 hex>"},
"requires_sets": [], "receipt_sha256": "<64 hex>", "published_on": "2026-11-03"}]
}A page loads SIMAI Framework and names its sets on the same line
(core.runtime 1.25.0):
<script src="https://cdn.jsdelivr.net/npm/@simai-studio/ui@<v>/distr/core/js/core.min.js" data-sf-sets="effects"></script>The Loader then:
- finds each set's base through the compatibility index:
@simai-studio/<set>@<version>/dist/smartof the newest release this Core meets; - fetches the set's
rule.jsbefore the first scan; - loads only the components on the page.
Other forms of the same declaration:
| Written | Means |
|---|---|
data-sf-sets="effects admin@1.2.0" |
effects by the index; admin at npm 1.2.0, no index read |
SF_BOOT_CONFIG.packages.effects = true (or {index: true}) |
by the index |
SF_BOOT_CONFIG.packages.effects = '1.0.0' |
that npm version |
SF_BOOT_CONFIG.packages.effects = '<base>' |
that base, for example a gh/ commit or a local copy |
SF_BOOT_CONFIG.packages.effects = {base, rule} |
the component-package form |
<script src="<base>/rule.js"> after core.js |
the base is the rule file's directory |
SF_BOOT_CONFIG wins over data-sf-sets. SF_BOOT_CONFIG.setIndex and
SF_BOOT_CONFIG.setNpm replace the index address and the npm root, for a
mirror or a local stand.
What a set adds at runtime, through its rule.js and its bundles:
SF.RuleLoader.registerPackage(<set>, {rules, api, aliases, base}): the rules;api, the rule whose bundle carries the set's scripted API;aliases, the set's own dated former tags ({from, to, since, remove_after}), merged intoSF.tagsso SIMAI Framework keeps no row for them.SF.RuleLoader.registerPackage(<set>, {exports}): the scripted API, from the bundle. A page reads it withawait SF.package('<set>'), which asks for theapielement if it has not loaded and refuses a set the page has not declared, naming the line to add.SF.RuleLoader.packageOf(<set>):{name, base, declared, registered, version, release, api, exported}, frozen. A bundle finds files published with it (the effects catalogue) relative tobase.
In debug (?sf-debug, data-sf-debug) the Loader also reads
<base>/../set.json and names each required set the page has not declared.
It never loads a required set on the page's behalf. It reads the passport only
of a package the page names as a set (data-sf-sets, true, a version, a
base string, {index: true} or an object with a version); a component
package declared as {base} or {base, rule}, such as the Pro package, has no
passport and nothing is requested for it (core.runtime 1.26.1).
Each set has one site on its own subdomain. It holds the documentation and the demos together and is built with the one shared Docara set template. It has five required sections:
- Overview: what the set does, its licence and its Framework compatibility;
- Connection: the one line;
- Components: one page per component, with live examples;
- Demos: ready screens;
- Changes.
simai.component-set supersedes simai.component-package for components
outside the Framework. It refers to 1.1.1 by digest and does not extend it.
An extends would inherit two requirements that sets replace:
own-element-prefix: sets usesf-;delivered-in-owners-distribution: sets have one release route.
The standard-contract envelope cannot override an inherited requirement.
Every other component-package rule still holds for a set's package folder:
- layout and lock;
- plain-data rules;
- manifests and ports;
- reproducible build and receipt;
- trust;
- the lifecycle of public names.
pro and larena stay on the package standard until pro becomes a
transitional bundle of the paid sets.
- References:
src/core/contracts/packages/*.package.jsonand the tag alias rows generated from them leave as dated aliases. - Compatibility index:
@simai-studio/setsis not published until the first set release; no gate checksrequires_setsacross sets yet. - Docara: the shared set template.
- ui-doc: the guide section on component sets.