Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / UI

Pinning a kit version (using Ui@2)

using Ui@2; — pin the major version of a forkable kit; platform capabilities are version-neutral

A kit like `Ui` is versioned independently of the platform, so you pin the major you build against with `using Ui@2;`. The number is a stability floor that never auto-crosses the next major. Platform capabilities (`Storage.Blob`, `Content.Markdown`, …) are version-neutral — they ride the platform version — so pinning one is a compile error.

stable1 example compiled by CIuikitversioningusing

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 pin

The 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.Blob to 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 capability

Each 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.lock

After 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 customization

A 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).

Related

Osyrin.Ui (the UI kit)

The bundled UI kit — ready-made styled controls like `Button`, the shared design-system vocabularies (`Tone`, `Size`)…

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

theme tokens

A `theme` block names your app's design tokens — colors, spacing, radii, and more — as reusable values. A token can…