Versioning and Compatibility
A data model written for one release of the platform keeps loading, and keeps behaving the same, on every later release. You never have to rewrite your repository because the platform moved on: when the format of the definition files changes, the platform converts older files itself, every time it loads them.
This page explains what is versioned, how a version is declared, what happens when a model has errors, and how to move a repository to the current format.
What is versioned
| What | Versioned by | Guarantee |
|---|---|---|
| The format of the class files (JSON keys, enum values, how references are written) | version in each class file | Every earlier format keeps loading. See Definition format versions. |
The JavaScript script API (execute / produce, the api methods, the shape of dataObject and params) | apiVersion on each script | A script keeps the API it was written for. See Script API versions. |
| Gateway contracts (the gateway name, its parameters and targets, or the archetype it works on) | apiVersion on each gateway script | Covered for stable gateways; beta gateways may still change. See Beta features. |
| Formula functions and template helpers | Platform release | Functions are only ever added. |
Commons classes (commons.*, system.*) | Platform release | Changes are made by the platform, including any conversion of stored data. |
| Archetype slot names | Platform release | Slot names are frozen: gateways and AI integrations rely on them. |
Adding something never needs a new version: a new optional property, a new enum value, a new formula
function or a new api method is simply available from the release that introduces it. A new version
is only introduced for a change that would otherwise break existing files: a renamed or removed
property, or a different way of writing a value.
Definition format versions
Every class file, index.json included, declares the format it is written in:
{
"$schema": "https://api.mosterd.com/schema/definition-v1.schema.json",
"version": 1,
"class": "my-project.employee",
"kind": "BASIC",
"inherits": "commons.person"
}
- The current format is version 1. The Model Format Changelog lists what changed per version.
- A file without
versionis format 0, the format from before versioning was introduced. - The version is per file. A repository can mix formats, so you can move it to a newer format one file at a time.
- Older formats keep loading, permanently. The platform converts each file to the current format when it loads it. The conversion is purely syntactic: an older file and its converted counterpart describe exactly the same class.
- A file that declares a newer version than the platform supports is refused. It was written for a later release.
Converting on load means your repository does not have to change when the platform does. Upgrading the files themselves is optional; it gives you the current vocabulary, strict checking of your own files against the current format, and editor support through the JSON Schema. See Upgrading a repository.
Editor support
Each format version has a published JSON Schema:
https://api.mosterd.com/schema/definition-v1.schema.json
Point a class file at it with "$schema" and editors such as VS Code and IntelliJ offer autocompletion,
documentation on hover, and validation while you type. The platform ignores "$schema"; it is only an
editor hint. The schema of a released version never changes.
Instead of adding "$schema" to every file, you can also map the schema to your repository in the
editor settings. For VS Code, in .vscode/settings.json:
{
"json.schemas": [
{
"fileMatch": ["*.json", "!.vscode/*.json", "!package*.json"],
"url": "https://api.mosterd.com/schema/definition-v1.schema.json"
}
]
}
Loading a model: checks and refusal
Every time a model is loaded — after a push to its repository, or when an administrator reloads it — the platform checks it as a whole before it takes effect:
- Every property must be known. A misspelled or obsolete property is an error, not something that is silently ignored.
- Every reference must resolve. Fields named in formulas, views, layouts and script
triggers/parameters/targets must exist on the class (inherited fields and fields added by extensions
included); scripts named by a layout
ACTIONmust exist; relation references must name a class the relation actually leads to. - Every referenced file must exist:
javascriptFile,utilitiesFiles,templateFile,promptFile,schemaFile. - Field names are lowerCamelCase: a lowercase letter followed by letters and digits
(
invoiceDate,vatNumber2). A field name never contains_; names starting with_are reserved for the platform's own properties (_class,_creationDate,_modificationDate).
When a model has errors
A model with one or more errors is refused, and the platform reports all errors at once, each with the file and the JSON pointer of the offending element:
ERROR employmentAgreement.json /layouts/0/items/3/name: Class 'my-project.employmentAgreement' has no field 'salry'
A refused model never replaces a working one: the model that was active keeps serving the tenant until a corrected version loads. Your users are not affected by a push that breaks the model. Only a tenant that has no working model to fall back on — the very first load — is unavailable until the errors are fixed.
For a file in an older format, paths refer to the file after conversion to the current format, and the
message says so. For example, a format 0 file with a typo inside valueTypes is reported at
/fields/....
Not every finding refuses a model:
| Severity | Effect | Examples |
|---|---|---|
ERROR | The model is refused. | Unknown property, unknown field, missing script file. |
WARNING | The model loads; you should act on it. | A script names a gateway this platform installation does not have. |
INFO | For your awareness only. | A script uses a beta gateway. |
A gateway that does not exist is a warning rather than an error, because which gateways are available is a property of the platform installation, not of your model.
Model status
The outcome of the latest load is the tenant's model status. Administrators see it on the Model
page in the app's system section, which also shows a notification when the latest model was refused.
Through the API, the status is available at GET /admin/{tenant}/modelStatus.
| State | Meaning |
|---|---|
LOADED | The latest model loaded and is active. |
REFUSED | The latest model was refused; the previously loaded model is still active. |
FAILED | The latest model was refused and there is no earlier model to fall back on: the tenant is unavailable. |
Upgrading a repository
When files in your repository are written in an older format, the Model page offers them for download,
converted to the current format. The status reports how many files that concerns (upgradableFiles).
- On the Model page, choose Download upgrade. You get a zip, for example
acme-definition-format-1.zip. - Unzip it over the root of your repository. The files have their paths relative to the repository root, so they overwrite the originals in place. Files that are already current are not in the zip.
- Review the changes with
git diff, together withUPGRADE-REPORT.mdfrom the zip (do not commit the report itself). - Commit and push. The model reloads, and its status shows no files left to upgrade.
The upgraded files load into exactly the same model as the originals. The upgrade:
- converts every file to the current format: renamed properties, enum values, file extensions (see the changelog);
- adds
"$schema"as the first property, for editor support; - keeps the order of properties and the file's indentation unit (two spaces, four spaces, a tab). Blank lines and hand-made line breaks are not kept: the file is laid out again, with short objects on one line.
UPGRADE-REPORT.md lists the upgraded files, anything the conversion wants to point out, and the
model's current errors and warnings. A model that is refused because of reference errors can still be
upgraded; the upgrade does not fix those errors, and the report lists them. Only a model whose files
cannot be read at all (for example invalid JSON) cannot be upgraded.
Through the API, the download is GET /admin/{tenant}/modelUpgrade.
Script API versions
The JavaScript of a script is never converted: it keeps calling the API it was written for. Each script
therefore records that API version in apiVersion:
{
"name": "logNoteChange",
"type": "JAVASCRIPT",
"apiVersion": 1,
"javascriptFile": "scripts/logNoteChange.js"
}
- A script without
apiVersionuses version 1. Omitting it never silently moves a script to a newer API. - Scripts from a format 0 file get
apiVersion: 0when they are converted. Version 0 is the same API as version 1. - A version the platform does not know refuses the model.
- Only the methods of the script API are reachable from JavaScript; see JavaScript Scripts for the list.
For a gateway script, apiVersion names the version of that gateway's contract instead
(default 1). A version higher than the gateway supports is a warning.
Beta features
A beta feature works, but is outside the compatibility guarantee: it may still change in a later
release, and if it does, your model may need to change with it. A script that uses a beta gateway is
reported as an INFO finding in the model status.
Currently beta:
- the gateways marked beta in the Gateway reference;
migrationdefinitions.
Phasing out features
Syntax changes never break a model: the platform keeps converting older formats. A feature that is genuinely removed follows a phase-out policy:
- The feature is marked deprecated in the changelog.
- For at least 12 months, a model that uses it loads with a
WARNINGin its model status. - After that period the feature is removed. A model that still uses it keeps loading: the platform
ignores the feature and reports a
WARNINGthat it no longer has any effect — the same way it treats a script that names an unknown gateway. The changelog states per removed feature what happens to it.