Skip to content

2. Foundations

Contents of DISL 0.8

2.1 Serialization

A DISL specification is a JSON text (RFC 8259) encoded in UTF-8 without a byte-order mark. Its top-level value MUST be an object. The file extension SHOULD be .dis.

A specification SHOULD declare the schema it conforms to, and MUST declare the DISL version it targets:

{
  "$schema": "https://etalii.net/adp/disl/schema/0.2/disl.schema.json#/$defs/Specification",
  "disl": "0.2",
  "language": { "id": "org.example.statemachine", "version": "1.0.0" },
  "metamodel": { "types": {} }
}

The schema is published by the ADP website at https://etalii.net/adp/disl/schema/<version>/disl.schema.json.

Tools MAY accept YAML or JSON5 input for authoring convenience and convert it to JSON before processing, but the normative form is JSON. Because JSON has no comments, explanations belong in doc objects (section 2.4), which has the side effect that they reach end users.

Duplicate keys within one JSON object are a specification error. Property order in specifications carries no meaning except where this specification says so explicitly (arrays are always ordered; the order of toolbox groups, form fields, labels and constraints is significant).

2.2 Identifiers

Identifiers name types, attributes, styles, shapes, tools, constraints and all other named objects.

  • A simple identifier matches ^[A-Za-z_][A-Za-z0-9_]*$. Type names, attribute names, parameter names, port names and function names MUST be simple identifiers so that they are valid CEL identifiers.
  • A qualified identifier matches ^[A-Za-z_][A-Za-z0-9_-]*(\.[A-Za-z_][A-Za-z0-9_-]*)*$ and is used for language IDs, theme tokens (color.surface), names from imports (std.cylinder) and plugin names.
  • Identifiers are case-sensitive. Two identifiers in the same namespace MUST NOT differ only in case, to keep specifications and DID definitions portable across case-insensitive systems.
  • Where an object is stored in a map, the map key is its identifier. Where it is stored in an array, it carries an id property.

Namespaces. Each of the following is a separate namespace: types (node types and relation types share one namespace), enums and data types (share one namespace, which must also not clash with types), axes, coordinate systems, snapping profiles, styles, shapes, markers, icons, forms, tools, templates, constraints, operations, functions, layouts and viewpoints. Imported specifications contribute names prefixed with their alias (alias.Name).

Reserved names. The following MUST NOT be used as attribute names, because they are built-in fields of elements in CEL (section 12.2): id, type, kind, parent, children, descendants, ancestors, incoming, outgoing, source, target, sourcePort, targetPort, ports, owner, view, diagram, self, value, item, index, env, old, event, detail, and any name beginning with _ or $. CEL keywords (in, as, break, const, continue, else, for, function, if, import, let, loop, package, namespace, return, var, void, while, true, false, null) are also excluded.

2.3 Localized text, messages and reasons

Any human-facing string (labels, messages, descriptions, placeholders) is a LocalizedText: either a plain string or an object mapping BCP 47 language tags to strings.

{
  "label": "State",
  "label": { "en": "State", "de": "Zustand", "nl": "Toestand" }
}

When a map is given, it SHOULD include the locale declared in language.defaultLocale (default "en"). Runtimes select the best match for the user's locale (RFC 4647 lookup) and fall back to the default locale, then to the first entry in the map.

Where a human-facing string must contain computed values (constraint messages, dynamic tooltips), the object form { "cel": "..." } returning a string (or a map(string, string) keyed by locale) is used instead. DISL deliberately has no second template syntax.

Message. A Message is a LocalizedText or a CEL value { "cel": …, "resultType"?, "doc"? } that returns a string or a map(string, string) keyed by locale ($defs/Message). It is the one shape of every sentence and label a runtime shows to the user. The positions this document types as Message are: constraint and field validation messages (8.2, 7.5); quick-fix labels (8.5); notation tooltips (6.9, 6.10); handle labels (6.8); the labels of tools, context tools, operations, forms, form items and sections (7.2, 7.3, 7.5, 9.3); form placeholders, button labels and the texts of absent and empty values (7.5, 4.3); confirmations (9.5); refusals and every Reason (below); built-in messages and refusals (8.1) and standard messages (9.1); and every human-facing text property added in 0.2. Metamodel labels (of the language, types, attributes, enums and enum values) and doc objects stay LocalizedText, because they name things independently of any element and are read where no CEL context exists.

  • In a Message position, an object that has a cel property MUST be read as CEL, never as a LocalizedText map. A LocalizedText map MUST NOT use cel as a language tag, and cel is not a BCP 47 tag.
  • A CEL Message is type-checked in the context of the property that holds it; each property names that context. It MUST return string or map(string, string). When it fails to evaluate, the runtime MUST show the position's default text instead (for a label, the label derived from the identifier, 2.4; for a refusal or reason, a runtime-worded sentence naming the rule or reason id) and log a diagnostic, as for visual properties in 2.5.
  • Messages are evaluated against the model, not against what is drawn: a Message MAY name an element that is filtered out, truncated or otherwise not drawn.
  • A runtime MUST evaluate a CEL Message each time it presents it, for the target it is presented for, so a label or reason always describes the current state.
{ "kind": "operation", "operation": "toggleFold",
  "label": { "cel": "self.view.collapsed ? 'Expand' : 'Collapse'" } }

Reason. A Reason says why something is refused, read-only or unavailable ($defs/Reason). It is one of:

Form Applies
a Message always
{ "when": Expression, "message": Message, "id"?: SimpleId, "doc"?: Doc } when when holds; without when, always
{ "reason": "<id>" } when the named reason of behavior.reasons (9.1) applies

Reasons are used as ordered lists, Reason[]: refusals (6.9, 6.10, 8.4), read-only reasons (4.3, 7.5), unavailable entries (7.2, 7.3, 9.3), the diagram-wide edit gate (9.1), budgets (3.2) and ephemeral ids (11.5). when and a CEL message are evaluated in the context of the property that holds the list.

  • The applicable reason of a list is the first one that applies. A runtime MUST show the applicable reason, and only that one, as the explanation; it MAY offer the others on request.
  • A {reason} entry MUST name a reason declared in behavior.reasons; a reference to an undeclared reason is a specification error.
  • A runtime MUST show a refusal or unavailability sentence where the user is looking (the refusal line or cursor tooltip, a tooltip on a disabled entry, beside the property row), and MUST show the same sentence wherever the same reason applies, whatever the surface (canvas, menu, toolbar, form, keyboard shortcut).
"readOnlyReasons": [
  { "when": "self.labelledThroughXl", "message": "This concept's labels are stated through SKOS-XL, which this reading does not resolve. Edit them as triples instead." },
  { "reason": "truncated" },
  "Alternate and hidden labels are edited as triples."
]

2.4 Documentation objects

Every object in a specification MAY carry a doc property — the language itself, types, attributes, enum values, data types, axes, coordinate systems, snapping rules, styles, shapes, shape parameters, handles, markers, node and edge notations, labels, compartments, forms, fields, tool groups, tools, templates, constraints, hooks, operations, layouts, persistence settings and migrations. This is the mechanism that keeps users informed: a runtime can explain any element, field, tool, rule or snapping behavior it presents.

doc is either a LocalizedText (shorthand for summary) or a Doc object:

Property Type Meaning
summary LocalizedText One sentence of plain text. Used for tooltips, palette entries and hover cards.
description LocalizedText Longer explanation in CommonMark. Used in help panels, inspector help and generated reference docs.
rationale LocalizedText Why this rule or design exists. Particularly useful on constraints and snapping rules.
examples array of {title, description, value} Illustrations; value is arbitrary JSON (for example a sample attribute value).
seeAlso array of {title, href} Links. External links are absolute URIs; internal links are JSON Pointer fragments into the specification (#/metamodel/relations/Transition).
tags array of strings Keywords; used by toolbox search and documentation indexes.
audience array of "user", "author", "developer" Intended readers; "author" means the tool engineer. Absent means all. Runtimes SHOULD show only user documentation to end users when an audience is declared.
since string Language version (SemVer) in which the object appeared.
deprecated boolean or {since, message, replacedBy} Marks the object deprecated. Runtimes SHOULD warn when a deprecated type, attribute or tool is used and MAY offer replacedBy as a quick fix.
image string (URI) An illustration, for example a rendered sample.
helpUrl string (URI) A page with extended help; runtimes SHOULD render a "Learn more" link.
{
  "doc": {
    "summary": "A condition the system can be in.",
    "description": "A **state** is a stable situation in which the system waits for a trigger.\n\nStates may declare an *entry action* that runs each time the state is entered.",
    "tags": ["behavior", "core"],
    "seeAlso": [{ "title": "Transitions", "href": "#/metamodel/relations/Transition" }]
  }
}

Documentation surfaces. A conforming runtime at level Standard or above (section 15) SHOULD:

  • show a tool's doc.summary as its tooltip, and match label, doc.summary and doc.tags in toolbox search;
  • show an attribute's or field's doc.summary beneath or beside the form field, and doc.description on demand;
  • show a constraint's label, doc.summary and doc.rationale alongside every finding it reports, and list its quick fixes;
  • show the doc of axes, coordinate systems and snapping rules in canvas settings, ruler tooltips or a status bar while dragging (for example "Snapping to working days: tasks start at the beginning of a working day");
  • show a node or edge type's doc in a hover card on the canvas when a help mode is active;
  • offer a help view that renders the whole specification's documentation, including rendered samples of shapes and markers.

A label is distinct from doc: it is the short display name of an object (for example "Initial state" for the type InitialState). If label is absent, runtimes derive one from the identifier by splitting camel case and snake case and capitalising the first word only (InitialState → "Initial state", due_date → "Due date").

2.5 Expressions and dynamic values

All dynamic values are written in CEL. DISL uses four embeddings, and this document states for every property which one applies.

(a) Expression. A property of type Expression always contains CEL. It is written either as a bare string or as an object that adds documentation and an expected result type:

{
  "rule": "self.name != ''",
  "rule": { "cel": "self.name != ''", "resultType": "bool", "doc": "Every state needs a name." }
}

If resultType is present, validators MUST check that the static type of the expression is assignable to it.

(b) Bindable value. A property of type Bindable<T> accepts a literal of type T or one of these objects:

Form Meaning
{ "cel": "…" } Computed from a CEL expression.
{ "attribute": "path" } The value of an attribute of the current element (dotted path into structs, for example "address.city"). When the property is user-editable (a label, a placement coordinate), the binding is two-way: edits write back to the attribute.
{ "token": "name" } A theme token (section 6.2), resolved for the current theme mode.
{ "param": "name" } A parameter of the enclosing shape, marker or template.

Because literals may be strings, a bare string in a Bindable position is always a literal; a CEL expression there MUST use the {"cel": …} form.

(c) Geometry expression. In shape paths, handle positions and part bounds (section 6.8), a coordinate is a GeomExpr: a number (canvas units), a percentage string ("50%" of the relevant dimension), or any other string, which is CEL returning double or int. Geometry is so dense with expressions that the shorter form pays for itself.

(d) Action expression. In behavior actions, template fragments and migration steps (sections 7.4, 9 and 11.9), every value position is an Expression, so strings are always CEL. A string literal is therefore written with CEL quotes: "'Untitled'". The exceptions are keywords and names that the schema restricts to fixed values or identifiers — as, call, plugin, severity, algorithm, form, label (label id) — which are never CEL.

Evaluation guarantees. Expressions are free of side effects by construction. Runtimes MUST impose a cost limit using CEL's cost estimation, and SHOULD reject at validation time any expression whose worst-case estimated cost exceeds the limit declared in language.limits.celCost (default 1 000 000). At run time, an expression that fails (for example division by zero, missing key, cost exceeded) is handled according to its context:

Context On evaluation error
Constraint rule Reported as a finding with the constraint's severity and the message "could not be evaluated: …"; never silently passes.
Visual property, label, visibility The property falls back to its default; a diagnostic is logged; rendering continues.
Placement binding The element is drawn at its last valid position and marked as invalid.
Snapping function The unsnapped value is used.
Behavior action, hook, operation The whole transaction is rolled back and the error reported to the user.
Migration Loading the DID definition fails with a descriptive error; the original file is left untouched.
Derived element (4.11) A failing from yields no elements of that type and one finding std.derivedFailed naming the type and the error; a failing per-item expression drops that item, with one finding per type and error message. The rest of the diagram is drawn.
Plugin function call (13.1) When the plugin is absent, the call evaluates the declared fallback; without one, the call fails and is handled by the row of the context it appears in (a constraint reports "could not be evaluated", never passes). One std.pluginMissing finding is reported per missing plugin, not per call.

Static checking. Every expression is evaluated in a well-defined context that determines which variables exist and what types they have (section 12.3). Validators MUST type-check every expression in its context, using attribute types from the metamodel. Expressions whose type cannot be determined statically (for example because they use dyn) are permitted but SHOULD produce a validator warning.

2.6 Values and units

Kind Representation
Canvas length A number in canvas units (the unit of the coordinate system's numeric axis, px by default), or a string with an explicit unit: "12px", "4mm", "0.5in", "1.5em", "50%". Percentages refer to the containing box's corresponding dimension. em refers to the current font size.
Screen length Quantities that must stay constant on screen regardless of zoom — hit areas, snap tolerances, handle sizes, minimum marker sizes — are always in screen pixels and the property name ends in ScreenPx.
Color A CSS Color Level 4 string: "#1D9E75", "#1D9E75CC", "rgb(29 158 117 / 80%)", "hsl(160 69% 37%)", "oklch(0.62 0.12 165)", named colors, "transparent", "currentColor" (the resolved text color of the element).
Angle A number of degrees, clockwise, 0 pointing in the positive x direction.
Fraction A number from 0 to 1 inclusive.
Size [width, height].
Insets A number (all sides), [vertical, horizontal] or [top, right, bottom, left].
Point [x, y] for numeric axes, or {"x": …, "y": …} when axis values are timestamps or categories.
Timestamp An RFC 3339 string ("2026-10-01T09:00:00Z"), a full-date string ("2026-10-01") or an epoch number, as declared by the axis (section 5.4).
Duration An ISO 8601 duration ("P3D", "PT15M", "P1W") or a CEL duration string ("15m", "36h"). Calendar durations (P1M, P1Y) are only allowed where the property explicitly accepts calendar units, such as time snapping.

2.7 References

  • Type references are identifiers from the type namespace, optionally prefixed with an import alias (bpmn.Task). Wherever a single type is expected, a list of types MAY be given where the schema allows it; a type reference always includes all subtypes.
  • Library references — to styles, shapes, markers, icons, forms, tools, snapping profiles — are identifiers of entries in the respective library, or an inline object of the same kind.
  • JSON Pointer references ("#/notation/styles/base") are used in doc.seeAlso and in error reports.
  • A reference to an undefined name is a specification error. A reference to a deprecated object is a validator warning.

2.8 Extension properties

Any object in a specification or DID definition MAY contain properties whose names begin with x- (for example x-acme-simulation). Their content is unconstrained. Conforming tools MUST ignore extension properties they do not understand and MUST preserve them unchanged when rewriting a file. Section 13 describes plugins, the structured way to add behavior.

2.9 Versioning

  • disl (required) is the version of DISL the specification targets, as "major.minor": "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7" or "0.8". A runtime MUST refuse a specification with a higher major version than it supports and SHOULD warn for a higher minor version. The deprecated alias of section 18 is still read.
  • DISL 0.2 is a superset of 0.1. A 0.2 runtime MUST read a specification that declares "disl": "0.1", or whose $schema names the 0.1 schema (https://etalii.net/adp/disl/schema/0.1/disl.schema.json), as a 0.2 specification with the same meaning, apart from the clarifications listed in Changes from 0.1. A validator MUST validate such a specification against the 0.2 schema and MUST NOT report its 0.1 version or schema address as deprecated.
  • DISL 0.3 is a superset of 0.2. A 0.3 runtime MUST read a specification that declares "disl": "0.1" or "disl": "0.2" with its meaning, apart from the clarifications listed in Changes from 0.2. While 0.3 is a draft, its schema is published at the 0.2 address (https://etalii.net/adp/disl/schema/0.2/disl.schema.json), which accepts every 0.2 specification unchanged; a specification that uses a construct new in 0.3 SHOULD declare "disl": "0.3", and a 0.2 runtime SHOULD warn about it as a newer minor version.
  • language.version (required) is the semantic version (SemVer 2.0.0) of the defined language. DID definitions record the language version they were written with; this drives migrations (section 11.9).
  • A change that can make previously valid DID definitions invalid or change their meaning is a major change; adding optional constructs is a minor change; documentation, visual and toolbox-only changes are patch changes. Validators MAY warn when a version increment does not match the observed difference to a previous specification.

Referenced from