Summary#
A kit (like the UI kit) is a library of components, vocabularies, and a theme that is versioned
independently of the platform. You pin the major version your app builds against by adding an @major
suffix to its using:
using Ui@2;@2 is a stability floor: your app resolves to some 2.x, and never silently jumps to a breaking 3.x.
The version applies only to kits — the forkable, independently-shipped libraries. Ordinary platform
capabilities (Storage.Blob, Memory, Content.Markdown, Observability, …) are
version-neutral, so pinning one is rejected.
Signature#
using Ui@2; // any 2.x — a major floor
using Ui@2.3; // any 2.x ≥ 2.3 — a minor floor
using Ui@2.4.1; // exactly 2.4.1 — an exact pinThe suffix is @major[.minor[.patch]]. A bare @ with no number is an error.
Description#
Kits and platform capabilities differ in one decisive way — can you resolve an old or new version independently?
- A kit is composition (components, variant recipes, vocabulary members, a theme) shipped on its own cadence. An older or newer major exists as its own set of source, so pinning a version is meaningful.
- A platform capability is provided by the platform you run. There is no separate "version 2" of
Storage.Blobto resolve to — it is whatever the running platform provides. The platform version is its version, so pinning would be a fiction.
Because of that, a version pin is legal only on a kit. Pinning a platform capability is a compile error that names the fix:
using Storage.Blob@2;
// error VERSION_ON_PLATFORM_CAPABILITY: 'using Storage.Blob@2;' pins a version on a platform capability,
// which is version-neutral (it rides the platform version). Drop the '@2' — a version pin is only meaningful
// on a kit (e.g. `using Ui@2;`).If your app needs a newer platform (for a capability feature that only a newer platform provides), that is an
app-level requirement, not a per-capability pin. Requiring a minimum platform is expressed at the app level,
not by writing @version on a capability.
Which exact version did I get? — osyrin.lock#
Your using Ui@2; declares intent (a stability floor). The exact resolved version is recorded in
osyrin.lock so a build is reproducible across machines and matches what the server compiles — the same split
as package.json vs package-lock.json.
Run osyrin lock to resolve your pins and write the file:
$ osyrin lock
✓ Wrote osyrin.lock (2 pins)
Ui 2.0.0 (bundled)
Storage.Blob platform capabilityEach kit entry records the resolved version, a hash of the resolved source (the integrity check — an edited or
stale local kit copy is caught when you osy compile), the minPlatform the kit needs, the source it came
from, and the constraint you declared. A platform capability is recorded without a version (it rides the
platform). The happy path is offline: a default pin resolves to the kit bundled with your platform — no
network. Pinning a version the bundled kit can't satisfy is a clear error, not a silent mismatch:
$ osyrin lock # with `using Ui@3;` but only 2.x bundled
ERROR KIT_VERSION_UNAVAILABLE 'using Ui@3;' requests a version the bundled kit (2.0.0) does not satisfy…Updating & reconciling forks#
Stay on your major but pick up the newest compatible kit with osyrin update — it re-resolves within the
declared major (never crossing to the next) and rewrites osyrin.lock:
$ osyrin update ui
Ui 2.0.0 → 2.1.0
✓ Updated 1 kit in osyrin.lockAfter an update, osy diff shows how your forks in ui/lib/ differ from the kit's new source, so you can
reconcile them (shadcn-style):
$ osy diff ui/button
≠ Button (your fork vs kit default)
+ // my customizationA fork that matches the kit exactly is flagged as safe to drop.
Each vendored file records the kit version it was taken from, so diff can tell you the other thing a text
comparison cannot — that the kit itself has moved on since you forked:
$ osy diff ui/button
! Button — forked from Osyrin.Ui 2.0.0, the kit is now 2.1.0. What follows includes the kit's own changes since.That line is the real cost of a fork. The code is yours either way; what you give up is receiving improvements to it, and this is how you find out what you are missing.
Examples#
Pin the UI kit's major and use a platform capability version-neutrally in the same manifest:
app Shop {
model "model/**/*.osy";
use Osyrin.Ui@2; // kit — pinned to major 2
use Osyrin.Storage; // platform capability — version-neutral, no @
}⚠ A version pin belongs on the manifest's use, never on a source file's using. using is a C# import and
carries no version — using Osyrin.Ui@2; is refused with a message saying exactly this.
See also#
- Osyrin.Ui (the UI kit) — what the UI kit contributes and how to fork a control.
- theme tokens — the design tokens a kit's controls share with your theme.
- component — declaring your own components (kit controls are just components).