A SIMAI Framework release is one ui build commit. Its id is
ui-<first 12 hex of that commit>, and its contract is the release lock v2
ui/contracts/releases/ui-<12>.lock.json (schema
ui/contracts/schema/framework-release-lock.v2.schema.json).
Core, rules, components, utilities and Smart components are all published in
ui/distr (core, rule, component, utility, smart). While consumers
still read Smart from ui-smart, the same Smart bytes are also laid into
ui-smart/smart (the mirror), and the lock records it.
scripts/publish-framework-release.sh is the route. It never pushes.
- Build output verified by
scripts/verify-product-reproducibility.cjs(two byte-identical waves);wave-1/outputholdscore/core,core/rule,component/component,utility/utilityandsmart/smart. - Clean checkouts of
ui,ui-smart(the mirror) and theui-sourcerevision that was built. - A changelog entry for
ui/CHANGELOG.md, inserted under[Unreleased]in the section named by--changelog-section.
scripts/publish-framework-release.sh \
--output /absolute/repro/wave-1/output \
--ui ../ui --mirror ../ui-smart --source ../ui-source \
--subject "what the release changes" \
--changelog-entry /absolute/entry.md --changelog-section "<heading under [Unreleased]>"--builder-revision names the ui-builder commit that built the output when it
is not HEAD. --trailer appends a closing paragraph (for example a
Co-Authored-By: line) to every commit message. --no-mirror publishes
without ui-smart once no consumer reads it.
- Lay the output into
ui/distr/{core,rule,component,utility,smart}andui-smart/smart(rsync --delete; nothing else in either repository is touched). - Commit the build. The
uicommit (Build: <subject>) is the release.ui-smartgets a build commit only when its Smart bytes moved. - Write the receipt (
scripts/write-release-receipt.py). Each tree is named by the build commit that last wrote it as of the release:distris always the release itself;distr/smartis the release, or an earlier build commit if this release did not change a Smart byte. The mirror is named the same way inui-smart. - Bind the owner manifests (
bind-generated-runtime-contracts.py --phase owners, with--smart-rootequal to--ui-root). The Smart owner manifest isui/contracts/owners/smart-component.manifest.json(ownersimai/ui, assetssimai/ui@<rev>:distr/smart/...); on the first v2 release it is seeded from the mirror's. The mirror's own manifest is rebound to the mirror commit and committed inui-smart. The ui Smart owner manifest lists exactly the Smart components the release ships: a folder indistr/smartwith itscl-<folder>rule of type smart indistr/rule/rule.json. A shipped component with no entry gets one, built from its bytes, its rule (tags, declared assets,requiresfrom the rule's relations) and its sourcesrc/smart/<name>/smart.manifest.json(title, lifecycle), with the readiness of a component outside the curated plan:discoverable,safe_to_suggest: false,curated_for: []. The binder refuses a shipped component the source does not describe. An entry whose folder or rule is gone is dropped. Existing entries keep every curated decision;inventory.expectedis recounted. The binder prints what it added and removed; promoting a new entry into the curated plan stays a deliberate edit by the owner. (ui-9fb0297bb5c2 addedsf-page-headerbefore this, and its entry was written by hand.) Components a package provides (ui/distr/core/contracts/packages/*.package.json, source ui-sourcesrc/core/contracts/packages/; SIMAI Framework Pro is the first) leave both Smart owner manifests here, in the release that stops shipping their folders: the binder drops their entries and lowersinventory.expected, and refuses if a provided folder is still indistr/smartor the mirror. - Write the release lock (
--phase reference):ui-<12>.lock.json, and, while mirrored, the v1 aliasui-<12>-smart-<12>.lock.jsonthat v1 readers resolve (ui-admin's pin finds a release by the commits it pins). - Run
ui/scripts/release-gate.sh. It builds and checks the registry, runs the registry suite andcheck-release-locks.py(each tree named by its writer, mirror byte-identical, alias present and in agreement). When it is red the route stops and the bind stays uncommitted inuifor inspection. - Changelog and the bind commit
Bind the registry to the release ui-<12>.
{
"schema_version": 2,
"compatibility_id": "ui-<R12>",
"runtime_sources": {
"core": { "owner": "simai/ui", "revision": "<R>", "commit": "<R>", "runtime_path": "distr", ... },
"smart": { "owner": "simai/ui", "revision": "<R>", "commit": "<writer of distr/smart>", "runtime_path": "distr/smart", ... }
},
"mirror": { "owner": "simai/ui-smart", "commit": "<M>", "runtime_path": "smart", "runtime_tree": "<same as smart>", "mirrors": "smart", ... },
"pair": "ui-<R12>-smart-<M12>"
}revision is the release, the same for both trees. commit, tree,
runtime_tree and archive_sha256 describe the commit that wrote each tree, as
in v1. mirror and pair are present only while ui-smart is mirrored.
A product's own Smart components are built over the ui-source revision a free
release was built from, and pinned by a package receipt that names that
release: docs/developer/product-packages.md. Release the free Framework
first. (SIMAI Framework Pro, released into ui-pro, is superseded:
docs/developer/pro-release.md.)
Stop laying distr/smart: publish with the v1 binder layout (--smart-root
pointing at ui-smart). Already written v2 locks stay valid history.