Range Retention (Preview)
Today a compiled model freezes every dependency range into an exact pin: System-[2.5,3.0) in ckModel.yaml becomes System-2.5.0 in the compiled model, and a tenant must hold exactly that version (see Versioning rules). Every new System version therefore forces every dependent to be rebuilt and republished.
Range retention keeps the declared range in the compiled model instead. A range-retaining model stays usable when an additive minor of its dependency is installed — no rebuild, no version bump.
Range retention is switched on with the flag OctoCkRangeRetention=true. It is off by default; with the flag off the compiled output is byte-identical to before. Use it for local evaluation only and never publish range-retaining base models into a shared catalog (see the operating rule); the one-time re-pin of all models is planned for a later phase.
Switching It On
| Where | How |
|---|---|
| MSBuild (model project) | <OctoCkRangeRetention>true</OctoCkRangeRetention> or dotnet build -p:OctoCkRangeRetention=true |
| Environment | OctoCkRangeRetention=true (MSBuild picks up an exported variable as a property) |
octo-ckc Compile | -rr true (defaults to the environment variable) |
The flag works for CK language 1 and 2 models alike.
What the Compiler Writes
# ckModel.yaml (source)
modelId: Acme.Plant-1.0.0
ckLanguage: 2
dependencies:
- System-[2.5,3.0)
- Acme.Assets-[1.0,2.0)
# compiled with OctoCkRangeRetention=true
dependencies: # exact closure, kept for older readers and the SemVer diff
- Acme.Assets-1.0.0
- System-2.5.0
dependencyRanges: # every declared dependency: range verbatim + floor
- range: Acme.Assets-[1.0,2.0)
floor: 1.0.0
- range: System-[2.5,3.0)
floor: 2.5.0
usedSurface: # what this model uses of System (sorted)
- System@2/Entity-1
- System@2/Entity-1.Name
usedSurfaceHash: sha256:…
minEngineVersion: 3.5.1
types:
- typeId: Pump
derivedFromCkTypeId: Acme.Assets@1/Device-1 # major-qualified, version-less
implements:
- Acme.Assets@1/Calibratable-1
dependencyRangeslists every declared dependency with its range and its floor.- The floor is the declared lower bound (
[2.5,3.0)→2.5.0), never "the highest version in the catalog". An exclusive lower bound gets the next patch ((2.4,3.0)→2.4.1). - Every reference into a dependency is major-qualified and model-version-less:
Model@Major/Element-n, e.g.System@2/Entity-1. References to the model's own elements stay concrete. usedSurfacelists, per declared dependency, the elements and members of that dependency the model uses: base types and records, implemented and extended interfaces, reused attribute definitions, records and enums used as value types, association roles and targets (element level,System@2/Entity-1), and attribute paths of indexes andownerAttributePathinto an inherited dependency type (member level,System@2/Entity-1.Name).usedSurfaceHashis a sha256 over the list. A later import check (F2.5) uses it to tell which consumer a dependency change would break. It cannot see behavioural or semantic changes (defaults, display rules, the meaning of a value), and references into undeclared, transitive dependencies are not listed.minEngineVersionis set and the model is published underck-models/v3/.
Compile-time checks
- One major per range. Because references are stored as
Name@<major>of the floor's major, every declared range must stay inside one major.System-[2.5,3.0)is accepted;System-2.5(=>= 2.5.0),System-[2.0,),System-[2.5,3.0]andSystem-[2.5,4.0)are rejected ("... admit more than one major version ..."). A new major is a deliberate change of the dependent. - Compile against the floor, verify against the highest. The model is resolved and validated against the highest version in the range, but every element it references in a dependency must already exist in the floor version (or, if the floor itself was never published, in the lowest available version in the range). Otherwise: "references elements that do not exist at the floor of its dependency range ...". Raise the floor in
ckModel.yamlif you need a newer element. - Transitive references. A model may use elements of a model it does not declare (through another dependency). Those references are major-qualified and floor-checked too, against the guarantee of the intermediate model; a miss asks you to declare the model in
ckModel.yaml. Declaring every model you reference is recommended.
How a Range-Retaining Model Resolves
A dependency of a range-retaining model is satisfied when the installed (or catalog) version is inside the range and at or above the floor. Major-qualified references bind to the installed version of the same name and major. Classic models keep their exact-pin semantics.
Example: Acme.Plant-1.0.0 above was compiled against Acme.Assets 1.0.0. When a tenant upgrades to an additive Acme.Assets 1.1.0, Acme.Plant stays Available and resolves against 1.1.0 — without recompiling. A classic (exact-pinned) dependent in the same tenant goes ResolveFailed until it is rebuilt.
In a tenant
- A range-retaining model is stored with its
dependencyRanges; its references are persisted in the version-less form (System@2/Entity-1) and bound to the installed version only when the model is loaded. - After every import the engine re-validates the installed models. A range-retaining model is judged by its range and floor, a classic model by its exact pins. So an additive minor of a dependency leaves range-retaining dependents
Available, while exact-pinned ones goResolveFailed. - Installing a dependency below the floor (for example by an explicit downgrade) makes the dependent
ResolveFailed; the log names the range, the floor and the installed version (Acme.Assets-[1.0,2.0) (floor 1.1.0): installed Acme.Assets-1.0.0). It recovers automatically once a satisfying version is installed again.
Known Limitations
- Mixed exact pin and range on one model in a compile. If a range-retaining compile pulls in a classic dependency that exact-pins
System-2.5.0and declaresSystem-[2.5,3.0)itself while the catalog already holdsSystem 2.6.0, the two resolve to different versions and the compile fails with message 66 (Multiple versions of construction kit model 'System'). Rebuild the exact-pinned dependency with range retention. In a tenant this cannot happen — a tenant holds exactly one version. - Two ranges on one model in a compile. Ranges on the same model are resolved independently in a compile. If one dependency needs
B-[1.0,1.5)and anotherB-[1.2,2.0), they may resolve to different versions (1.4 and 1.8) and the compile fails with message 66, although 1.4 satisfies both. Align the ranges. - SemVer. The SemVer gate does not classify
dependencyRangesyet; the exactdependenciesclosure is still diffed as before. - Models compiled without the flag keep their exact pins and exact references until they are rebuilt with it.