{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://urdad.org/schemas/urdad.schema.json",
  "description": "JSON data structure spec for exchanging URDAD models (or components thereof)",
  "type": "object",
  "$ref": "#/$defs/Model",
  "$defs": {
    "Element": {
      "type": "object",
      "description": "What every element of a model is: an identity, a name, and the annotations attached to it. Every other definition specialises it, directly or through another.",
      "properties": {
        "id": {
          "type": "string",
          "description": "The element's identifier, unique within the model. Every reference to the element names it."
        },
        "name": {
          "type": "string",
          "description": "The element's name, as people read it in the tool, in diagrams and in generated documentation."
        },
        "annotations": {
          "$ref": "#/$defs/Annotations",
          "description": "The annotations attached to this element."
        },
        "formalizationOf": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "References to informalRequirement annotation IDs that this element formalizes. Set by AI formalization on acceptance, or manually by users authoring formal elements from informal requirements. Provides informal→formal traceability."
        },
        "formalizationSnapshots": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Baseline for each formalizationOf ref. Each entry has the form '<annotationId>|<8-char hex hash>|<quote>'. The hash is compared against the current annotation body to detect 'informal changed since formalization' and surface a re-verify badge; the quote is the verbatim substring the formalization cited, preserved for corpus provenance. Legacy 2-segment entries ('<annotationId>|<hash>') are still parsed (quote reads back as absent)."
        }
      },
      "required": [
        "id",
        "name"
      ]
    },
    "Annotations": {
      "type": "array",
      "description": "The annotations attached to an element, in the order they were added.",
      "items": {
        "$ref": "#/$defs/Annotation"
      }
    },
    "Annotation": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "Free text attached to a model element and stored with it: an informal requirement, a short or detailed description, a comment, concern, question, response or error, with its language and markup.",
      "properties": {
        "body": {
          "type": "string",
          "description": "The annotation's text, in its markup."
        },
        "purpose": {
          "type": "string",
          "enum": [
            "informalRequirement",
            "shortDescription",
            "detailedDescription",
            "comment",
            "concern",
            "question",
            "response",
            "error"
          ],
          "description": "What the annotation is: an informal requirement, a short or detailed description, a comment, a concern, a question, a response or an error."
        },
        "language": {
          "type": "string",
          "enum": [
            "af",
            "da",
            "de",
            "en",
            "es",
            "fr",
            "it",
            "nl",
            "pl",
            "pt",
            "sv"
          ],
          "description": "The language the body is written in, as an ISO 639-1 code."
        },
        "markup": {
          "type": "string",
          "enum": [
            "text_plain",
            "text_markdown",
            "text_html",
            "application_x-latex"
          ],
          "description": "How the body is marked up: plain text, Markdown, HTML or LaTeX, as a media type with '/' written as '_'."
        },
        "userName": {
          "type": "string",
          "description": "The name of the annotation's author, when it has one. Absent on an annotation the tool itself created."
        },
        "userEmail": {
          "type": "string",
          "description": "The email address of the annotation's author, when it has one. Absent on an annotation the tool itself created."
        },
        "createdAt": {
          "type": "string",
          "format": "date-time",
          "description": "Server-stamped ISO-8601 UTC timestamp at which this annotation was first persisted. Write-once: never overwritten on subsequent edits."
        },
        "phase": {
          "type": "string",
          "description": "Server-stamped project phase that was current when this annotation was first persisted (e.g. \"planning\", \"development\", \"sign-off\"). Lets queries filter annotations by phase."
        },
        "revisionId": {
          "type": "string",
          "description": "Server-stamped git commit hash of the model at the moment this annotation was first persisted. Lets queries trace an annotation back to a specific model revision."
        },
        "source": {
          "type": "object",
          "description": "Origin of this annotation when it was imported from an external system. Used for idempotent two-way sync (e.g. Confluence comments).",
          "properties": {
            "system": {
              "type": "string",
              "enum": [
                "confluence"
              ]
            },
            "externalId": {
              "type": "string"
            },
            "externalUpdatedAt": {
              "type": "string",
              "format": "date-time"
            }
          },
          "required": [
            "system",
            "externalId"
          ]
        }
      },
      "required": [
        "body",
        "purpose"
      ]
    },
    "ResponsibilityDomains": {
      "type": "array",
      "description": "The responsibility domains nested directly inside the model root or inside another responsibility domain.",
      "items": {
        "$ref": "#/$defs/ResponsibilityDomain"
      },
      "uniqueItems": true
    },
    "ResponsibilityDomain": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "The packaging unit of a model, mirroring an organisational unit or an external party. It owns service contracts, data types, entities, exceptions, processes, decisions and nested responsibility domains, and is what security policies grant access to.",
      "properties": {
        "dataTypes": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/DataType_polymorphic"
          },
          "description": "The data types this domain owns."
        },
        "entities": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Entity"
          },
          "description": "The entities this domain owns: the data it is responsible for storing."
        },
        "responsibilityDomains": {
          "$ref": "#/$defs/ResponsibilityDomains",
          "description": "The responsibility domains nested inside this one."
        },
        "serviceContracts": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ServiceContract"
          },
          "description": "The service contracts this domain offers."
        },
        "processes": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Process"
          },
          "description": "The processes realising this domain's service contracts."
        },
        "exceptions": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Exception"
          },
          "description": "The exceptions this domain's service contracts may raise."
        },
        "decisions": {
          "type": "array",
          "description": "The DMN decisions this domain holds — each a realization of a pure (isQuery) service contract, beside the processes. Absent when there are none.",
          "items": {
            "$ref": "#/$defs/Decision"
          }
        }
      }
    },
    "Entity": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "Business object whose full field list is aggregated across every DataStructure with a matching entityRef.",
      "properties": {
        "readableBy": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Ids of the ResponsibilityDomains that may read this entity's stored instances directly, and the domains inside them: its published read model. Empty is the default and means only the owning domain (the one containing the entity) reads them; every other domain asks the owner's services. Fields holding personal data are never readable this way."
        }
      }
    },
    "Model": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "The model root. It decomposes into top-level responsibility domains and, unlike a domain, owns no service contracts, data types, processes or exceptions of its own.",
      "properties": {
        "isModel": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies the model root on the wire; always true."
        },
        "responsibilityDomains": {
          "$ref": "#/$defs/ResponsibilityDomains",
          "description": "The model's top-level responsibility domains."
        },
        "timeZone": {
          "type": "string",
          "description": "The model's business time zone, an IANA time-zone id such as 'Africa/Johannesburg'; absent means UTC. today() is the date now() falls on in this zone."
        }
      }
    },
    "FormalSpecification": {
      "type": "object",
      "description": "A condition, constraint or expression written in URDAD-OCL, so that the tool can check and evaluate it. URDAD-FEEL is another notation for the same specification; the model stores URDAD-OCL.",
      "properties": {
        "specificationLanguage": {
          "type": "string",
          "description": "URDAD-OCL, the only specification language; the wire value is \"OCL\". A requirement not yet formalisable is an informalRequirement annotation, not a specification.",
          "enum": [
            "OCL"
          ]
        },
        "body": {
          "type": "string",
          "description": "The specification's text, in URDAD-OCL."
        }
      },
      "required": [
        "body",
        "specificationLanguage"
      ]
    },
    "Requirement": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A requirement placed on the model: a functional requirement of a service contract, or an invariance of a data type or variable. Its formal specification states it so that it can be checked.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "The requirement, stated formally. Absent while it is still stated only informally."
        },
        "requiredBy": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "References to ResponsibilityDomain or Process IDs"
        }
      }
    },
    "Invariance": {
      "type": "object",
      "$ref": "#/$defs/Requirement",
      "description": "A constraint that must hold of every instance of the data type or variable that owns it, always: a state in which it does not hold must never be observable.",
      "properties": {
        "isInvariance": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies an invariance on the wire; always true."
        }
      }
    },
    "DataType": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A type of value in the model: a primitive type, an enumeration, a data structure or a service reference. What every data type has in common is its invariances.",
      "properties": {
        "variant": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "PrimitiveType",
            "Enumeration",
            "DataStructure",
            "ServiceReference",
            null
          ],
          "description": "Which kind of data type this is: the discriminator a reader dispatches on to the definition of that name."
        },
        "invariances": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Invariance"
          },
          "description": "Constraints every value of this type must satisfy."
        }
      }
    },
    "DataType_polymorphic": {
      "type": "object",
      "description": "Any one of the data types, told apart by its `variant`.",
      "properties": {
        "variant": {
          "type": "string",
          "enum": [
            "PrimitiveType",
            "Enumeration",
            "DataStructure",
            "ServiceReference"
          ],
          "description": "Which kind of data type this is: the discriminator a reader dispatches on to the definition of that name."
        }
      },
      "required": [
        "variant"
      ],
      "allOf": [
        {
          "if": {
            "properties": {
              "variant": {
                "const": "PrimitiveType"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/PrimitiveType"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "Enumeration"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/Enumeration"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "DataStructure"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/DataStructure"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "ServiceReference"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/ServiceReference"
          }
        }
      ]
    },
    "PrimitiveType": {
      "type": "object",
      "$ref": "#/$defs/DataType",
      "description": "A data type with no structure of its own — a number, a string, a date or time, and the like — named after one of the built-in types.",
      "properties": {
        "dataType": {
          "type": "string",
          "enum": [
            "boolean",
            "integer",
            "real",
            "decimal",
            "string",
            "date",
            "time",
            "dateTime",
            "localTime",
            "localDateTime",
            "duration",
            "calendarPeriod"
          ],
          "description": "The built-in type this primitive type is."
        }
      }
    },
    "Enumeration": {
      "type": "object",
      "$ref": "#/$defs/DataType",
      "description": "A data type whose values are a fixed list of named options.",
      "properties": {
        "options": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The enumeration's values, in order."
        }
      }
    },
    "ServiceReference": {
      "type": "object",
      "$ref": "#/$defs/DataType",
      "description": "A data type whose values denote a service.",
      "properties": {
        "serviceContract": {
          "type": "string",
          "description": "Reference to a ServiceContract by ID. Optional, and its absence is a statement rather than an omission: the value denotes any service. A named contract means the value denotes that contract or any specialization of it (a contract whose base chain reaches it)."
        }
      }
    },
    "Variable": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A named, typed value: a field of a data structure, or a value a process names for itself. Its invariances constrain every value it may hold.",
      "properties": {
        "dataType": {
          "type": "string",
          "description": "Reference to a DataType by ID"
        },
        "invariances": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Invariance"
          },
          "description": "Constraints every value of this variable must satisfy."
        }
      }
    },
    "Field": {
      "type": "object",
      "$ref": "#/$defs/Variable",
      "description": "A typed field of a data structure, with its multiplicity and how the structure relates to the value it holds.",
      "properties": {
        "relationship": {
          "type": "string",
          "enum": [
            "composition",
            "aggregation",
            "association"
          ],
          "description": "How the owning data structure relates to the field's value: composition (the value is part of it and does not exist without it), aggregation (it holds the value, which may exist on its own) or association (it refers to a value that exists independently)."
        },
        "multiplicity": {
          "type": "string",
          "description": "Multiplicity of the field, e.g. '0..1', '1', '0..*', '1..*'",
          "default": "1"
        },
        "entityRole": {
          "type": "string",
          "enum": [
            "identifies",
            "states",
            "transient",
            "version"
          ],
          "description": "Meaningful only when the owning DataStructure's entityRef is set. What this field's value means toward the entity: 'identifies' names which instance is meant (at most one field per view), 'states' is actual persisted entity data, 'transient' never becomes persisted entity data, 'version' is the version of the instance read, filled by the store and never stored (at most one per entity, an Integer). Optional; absence is a distinct, reported 'unclassified' state, never read as 'transient'."
        },
        "personalData": {
          "type": "string",
          "enum": [
            "standard",
            "retentionObligated"
          ],
          "description": "Meaningful on any field, anywhere -- not scoped to entity views. Whether this field's value is personal data an execution environment would hold about a real person, and under what erasure regime: 'standard' is erasable on request, 'retentionObligated' is subject to a deployment-defined hold. Optional; absence means 'not personal data' and is never reported."
        },
        "unique": {
          "type": "boolean",
          "description": "Meaningful only on a 'states' field of an entity view. Whether no two instances of the entity may hold the same value at this field; an execution environment's entity store refuses a write that would. Optional; absence and false both mean 'not unique'. Set anywhere else, it says nothing and verification warns."
        }
      }
    },
    "DataStructure": {
      "type": "object",
      "$ref": "#/$defs/DataType",
      "description": "A structured data type with typed fields. It may specialise another data structure and may carry invariances; a request, a response, an exception and a message are each described by one. One that names an entity is a view of that entity.",
      "properties": {
        "base": {
          "type": "string",
          "description": "Reference to base DataStructure by ID"
        },
        "entityRef": {
          "type": "string",
          "description": "Reference to an Entity by ID. When set, this DataStructure is an entity view contributing to the entity's aggregated field list."
        },
        "fields": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Field"
          },
          "description": "The structure's own fields, in order. A specialising structure also has every field of its base."
        },
        "abstract": {
          "type": "boolean",
          "default": false,
          "description": "Whether this structure serves only as a base for others to specialise, with no value of its own kind. Absent means false."
        }
      }
    },
    "Exception": {
      "type": "object",
      "$ref": "#/$defs/DataStructure",
      "description": "A data structure raised to tell a service's client that a pre-condition was violated and the service has been refused.",
      "properties": {
        "message": {
          "type": "string",
          "description": "The message telling the client why the service was refused."
        }
      }
    },
    "FunctionalRequirement": {
      "type": "object",
      "$ref": "#/$defs/Requirement",
      "description": "A pre- or post-condition of a ServiceContract. It records nothing about what addresses it — that is the realising Process's statement, held there as a RequirementAllocation naming the step that discharges it. A requirement may belong to a contract in an element library or another domain, so a particular project's allocation cannot live on it.",
      "properties": {}
    },
    "PreCondition": {
      "type": "object",
      "$ref": "#/$defs/FunctionalRequirement",
      "description": "A condition that must hold for the service to be provided. When it does not hold the service is refused, and the client is told so by the exception the pre-condition raises.",
      "properties": {
        "raisedException": {
          "$ref": "#/$defs/Exception",
          "description": "The exception raised when this pre-condition does not hold."
        },
        "exceptionConstraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ExceptionConstraint"
          },
          "description": "The values the condition gives its raised exception when it does not hold, each stated as raised.<path> = <expression> from the request and the caller."
        }
      }
    },
    "PostCondition": {
      "type": "object",
      "$ref": "#/$defs/FunctionalRequirement",
      "description": "What must hold once the service has been rendered. `condition` says *when* the guarantee applies and is not the requirement's own formalSpecification, which says *what* must hold — written as one they cannot be read apart. `messageSendEvents` declares what realising it may dispatch, which is part of the contract a caller depends on: a caller writes a MessageHandler against each declaration.",
      "properties": {
        "isPostCondition": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies a post-condition on the wire; always true."
        },
        "condition": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "When the guarantee applies: the post-condition must hold only when this does. Absent means always. Not the requirement itself, which is its formal specification."
        },
        "messageSendEvents": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/MessageSendEvent"
          },
          "description": "The messages realising this post-condition may cause to be sent — part of the contract a client depends on."
        }
      }
    },
    "ServiceContract": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "The specification of a service: its request and response data structures, its pre-conditions and post-conditions, and the stakeholders requiring them. It names what is to be done, never who does it or how; a process or a decision realises it.",
      "properties": {
        "base": {
          "type": "string",
          "description": "Reference to the base ServiceContract by ID. The contract this one specializes: it imposes every functional requirement of its base. It may add postconditions and functional requirements, and may only waive preconditions (see waivedPreConditions), never add one."
        },
        "isQuery": {
          "type": "boolean",
          "description": "Whether this service is a function: pure, so that applying it to a request denotes a value and nothing else happens. Absent means false — purity is a positive claim and is made deliberately. Only a function may be named in a constraint expression, and only a function is generated as an HTTP GET. The claim is verified against the realizing process, which may then request no impure service and contain no send, await or reaction. Purity only ever accumulates down a base chain: a contract specializing a function is a function and must declare it too, while a specialization of an impure contract may freely be one."
        },
        "cancellable": {
          "type": "boolean",
          "description": "Whether a requester may cancel a request for this service before it is answered: every requester, another process included. Absent means false. A cancelled request has no net effect: the process realizing it undoes what it committed. A request for a service that may not be cancelled is waited for when its requester is interrupted, and then undone through its contract's undone-by. Accumulates down a base chain: a specialization of a cancellable contract is cancellable."
        },
        "requestDataStructure": {
          "$ref": "#/$defs/DataStructure",
          "description": "The structure of a request for this service: what the client provides."
        },
        "responseDataStructure": {
          "$ref": "#/$defs/DataStructure",
          "description": "The structure of the service's response: what the client receives."
        },
        "preConditions": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/PreCondition"
          },
          "description": "The conditions that must hold for the service to be provided; the service is refused when one does not."
        },
        "waivedPreConditions": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Ids of the base chain's preConditions this contract no longer requires — the only thing a specializing contract may say about preconditions. Preconditions move the opposite way to postconditions under specialization: a postcondition may be added (promise more), a precondition may only be waived (accept more), which is what keeps a specialization usable wherever its base was. preConditions is therefore meaningful only where base is absent, and each id here must resolve inside the direct base's effective precondition set."
        },
        "postConditions": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/PostCondition"
          },
          "description": "What must hold once the service has been provided."
        },
        "provisionalStakeholders": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The parties with an interest in the service, named as text until they are modelled: the source of its requirements."
        },
        "publishedTo": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Ids of the ResponsibilityDomains permitted to call this contract directly, as an external entry point. Distinct from provisionalStakeholders: a stakeholder is a requirements-sourcing concept, publishedTo is an access concept — a domain may be either, both, or neither. Empty is the default and means internal-only, reachable only as the target of a RequestService nested inside another contract's own process realization."
        },
        "generatedFor": {
          "type": "string",
          "description": "Id of the Entity this contract is a generated query service of; absent for every contract a modeller wrote. Generating again updates only the contracts that carry it."
        },
        "appliesToCurrentState": {
          "type": "string",
          "description": "Why this contract's change to a stored instance applies to the instance as it is, rather than to a version its requester read — an increment computed from the stored state in the write's own transaction, or a write meant to win. Present and not blank is the declaration; absent, such a change states a pre-condition reading the entity's version."
        },
        "scheduledTrigger": {
          "$ref": "#/$defs/DateTimeEvent",
          "description": "Present exactly when this contract runs on the calendar's own initiative — triggered by the scheduler rather than by a request. Restricted to a DateTimeEvent specifically, never the general event family a Reaction waits on: only a calendar reading needs no antecedent already in flight. Composable with publishedTo — a contract may carry both, or neither, which is the plain internal-only case. Never inherited down a specialization chain."
        },
        "compensation": {
          "$ref": "#/$defs/Compensation",
          "description": "How a committed request to this contract is undone: its 'undone by', the default for every committing request to it that states none of its own. Its request constraints read this contract's 'request' and 'response', as its post-conditions do."
        }
      }
    },
    "RequirementAllocation": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "\"This step discharges that requirement\" — the one statement of responsibility allocation, instance-level. It names an activity rather than a type because a requirement met by notifying a customer is discharged by one particular SendMessage in one particular flow, and one met by a lower-level service by the RequestService that calls it. Any activity qualifies, control-flow constructs included. Each requirement should have at most one. It is held by the Process because the allocation is a statement the process makes about itself — a functional requirement belongs to a ServiceContract that may sit in an element library, so it cannot carry a particular project's allocation. What a process depends on is not stated separately: it is read off the RequestServices it holds.",
      "properties": {
        "toAddress": {
          "type": "string",
          "description": "Reference to a FunctionalRequirement by ID"
        },
        "addressedBy": {
          "type": "string",
          "description": "Reference by ID to the Activity that discharges the requirement"
        }
      }
    },
    "Activity": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A step in a process: a service request, a message sent or awaited, a variable defined, the response populated, or a construct grouping further activities, such as alternate or concurrent flows.",
      "properties": {
        "variant": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "RequestService",
            "RawActivity",
            "DefineVariable",
            "PopulateResponse",
            "SendMessage",
            "AwaitMessage",
            "ActivitySequence",
            "AlternateFlows",
            "ForEach",
            "While",
            "ConcurrentFlows",
            "EventTriggeredFlows",
            "BackgroundFlow",
            "ConditionalFlow",
            "Reaction",
            "MonitoredActivities",
            "Monitor",
            "ExceptionHandler",
            "MessageHandler",
            "Process",
            "RaiseException",
            "CheckPreCondition",
            "Undo",
            null
          ],
          "description": "Which kind of activity this is: the discriminator a reader dispatches on to the definition of that name."
        }
      }
    },
    "Activity_polymorphic": {
      "type": "object",
      "description": "Any one of the activities, told apart by its `variant`.",
      "properties": {
        "variant": {
          "type": "string",
          "enum": [
            "RequestService",
            "RawActivity",
            "DefineVariable",
            "PopulateResponse",
            "SendMessage",
            "AwaitMessage",
            "ActivitySequence",
            "AlternateFlows",
            "ForEach",
            "While",
            "ConcurrentFlows",
            "EventTriggeredFlows",
            "BackgroundFlow",
            "ConditionalFlow",
            "Reaction",
            "MonitoredActivities",
            "Monitor",
            "ExceptionHandler",
            "MessageHandler",
            "Process",
            "RaiseException",
            "CheckPreCondition",
            "Undo"
          ],
          "description": "Which kind of activity this is: the discriminator a reader dispatches on to the definition of that name."
        }
      },
      "required": [
        "variant"
      ],
      "allOf": [
        {
          "if": {
            "properties": {
              "variant": {
                "const": "RequestService"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/RequestService"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "RawActivity"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/RawActivity"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "DefineVariable"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/DefineVariable"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "PopulateResponse"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/PopulateResponse"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "SendMessage"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/SendMessage"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "AwaitMessage"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/AwaitMessage"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "ActivitySequence"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/ActivitySequence"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "AlternateFlows"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/AlternateFlows"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "ForEach"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/ForEach"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "While"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/While"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "ConcurrentFlows"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/ConcurrentFlows"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "EventTriggeredFlows"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/EventTriggeredFlows"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "BackgroundFlow"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/BackgroundFlow"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "ConditionalFlow"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/ConditionalFlow"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "Reaction"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/Reaction"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "MonitoredActivities"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/MonitoredActivities"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "Monitor"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/Monitor"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "ExceptionHandler"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/ExceptionHandler"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "MessageHandler"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/MessageHandler"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "Process"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/Process"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "RaiseException"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/RaiseException"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "CheckPreCondition"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/CheckPreCondition"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "Undo"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/Undo"
          }
        }
      ]
    },
    "ActivitySequence": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Activities carried out one after the other, in order. A process is one, and so is each flow inside a construct.",
      "properties": {
        "activities": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Activity_polymorphic"
          },
          "description": "The activities, in the order they are carried out."
        }
      }
    },
    "RequestService": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "The step at which a process requests a service, naming the service contract it requires rather than a particular provider, and saying what is done when the service raises each of its exceptions or sends a message it declared.",
      "properties": {
        "service": {
          "type": "string",
          "description": "Reference to a ServiceContract by ID"
        },
        "providerSelector": {
          "type": "string",
          "description": "where the provider is read from, as a data path over the data this step can reach — 'request.preferredUnderwriter', 'Quote.nextReinsurer'. Not a party and not a reference: a provider known when the model is written is the contract the request names, because a requester requires a contract and never a party. What only the running process knows is which service reference to ask, so the path must arrive at a ServiceReference typed to the contract requested or to something that specialises it. Absent is a statement rather than a gap: the service is requested of whoever provides that contract, and nothing materialises a selector in its place"
        },
        "instanceKind": {
          "type": "string",
          "enum": [
            "singleInstance",
            "multiInstance"
          ],
          "default": "singleInstance",
          "description": "whether the service is requested once, or once per provider in a set — BPMN's multi-instance characteristic. Not a loop over a collection in the data, which is what a ForEach is: this varies who answers one request, not what is asked"
        },
        "processingMode": {
          "type": "string",
          "enum": [
            "sequential",
            "parallel"
          ],
          "default": "parallel",
          "description": "whether the instances of a multi-instance request may run concurrently or must be worked through in order. Meaningless on a single-instance request"
        },
        "providerMultiplicity": {
          "type": "string",
          "description": "how many providers a multi-instance request is addressed to, in the lexical form <lower>[..<upper>], upper may be * — e.g. '3', '1..*', '2..5'"
        },
        "continueWhile": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "when the request goes on to further providers, stated in continuation polarity — the request carries on while this holds. Meaningful on a multi-instance request: on a sequential one it decides whether the next provider is approached at all, on a parallel one that the outstanding answers are no longer wanted. An early exit and not a bound, which providerMultiplicity is"
        },
        "defaultFlow": {
          "$ref": "#/$defs/ActivitySequence",
          "description": "what is done when the service answered without raising — the middle lane of the composite, between the request and the merge. Not the enclosing sequence after the merge, where every arm rejoins: a step there runs whether or not an exception was handled, a step here only where none was raised"
        },
        "exceptionHandlers": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ExceptionHandler"
          },
          "description": "What the process does when the requested service raises each of its exceptions."
        },
        "messageHandlers": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/MessageHandler"
          },
          "description": "How the process processes each message the requested service declared it may send."
        },
        "responseVariable": {
          "$ref": "#/$defs/ProcessVariable",
          "description": "The name this request binds its response to, for the rest of the enclosing sequence. Its type and multiplicity are derived from the requested contract, never authored: a multi-instance request binds a collection of responses, so the name must then be reached with '->' rather than '.'. Absent means the answer is not named, which is the state almost every request is in."
        },
        "requestConstraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/RequestConstraint"
          },
          "description": "Populates this request's own request object, field-group by field-group, before it is sent."
        },
        "compensation": {
          "$ref": "#/$defs/Compensation",
          "description": "How this request is undone, overriding the requested contract's 'undone by'. Its request constraints read the process's names at the step."
        },
        "compensationNotNeeded": {
          "type": "string",
          "description": "That this request needs no undo, and why: what it commits lapses, or repeating it is harmless. Excludes 'compensation'."
        }
      }
    },
    "RawActivity": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Work that is named but not decomposed: a step whose detail the model does not (yet) state. Its optional formal specification states what it does.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "What the activity does, stated formally. Absent when the model leaves it unstated."
        }
      }
    },
    "Compensation": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "How a committed step is undone: one request to an undoing contract, its request populated by request constraints as a RequestService's is. A pre-condition exception means the service had no net effect, so whatever a process committed before raising one of its own contract's pre-condition exceptions must be undone. Owned by a ServiceContract (its 'undone by') or by a RequestService or SendMessage (their own). Each request constraint populates 'providerRequest', the undoing contract's request.",
      "properties": {
        "service": {
          "type": "string",
          "description": "The undoing contract, by id."
        },
        "requestConstraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/RequestConstraint"
          },
          "description": "What populates the undoing contract's request, under 'providerRequest'."
        }
      }
    },
    "RequestConstraint": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "Populates one field-group of a nested request, before it is sent. Not an Invariance: an Invariance means 'true of every instance of this type, always', where this holds of one request object at one point in one process. Its environment binds 'request' unshadowed plus 'providerRequest': the requested contract's request structure, the object being built.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "Mandatory: the whole reason to create a RequestConstraint is to state a body."
        }
      }
    },
    "ResponseConstraint": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "Populates one field-group of the process's own response — the realised contract's response structure, under the name 'response'. RequestConstraint's twin: its environment is the process environment at the owning PopulateResponse step, 'request' unshadowed, plus 'response', which nothing else in a process may name.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "Mandatory: the whole reason to create a ResponseConstraint is to state a body."
        }
      }
    },
    "ExceptionConstraint": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "States one field-group of the exception a raise raises — the values the handler catching it reads under 'exception'. ResponseConstraint's twin, owned as a set by a RaiseException step and by a PreCondition. Its environment adds 'raised', the exception being raised; never 'exception', which inside a handler is the one caught, so a re-raise reads raised.reason = exception.reason.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "Mandatory: the whole reason to create an ExceptionConstraint is to state a body."
        }
      }
    },
    "RaiseException": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Raises one of the realised contract's exceptions, refusing the service, with the values the exception carries.",
      "properties": {
        "exception": {
          "type": "string",
          "description": "Reference to an Exception by ID"
        },
        "exceptionConstraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ExceptionConstraint"
          },
          "description": "The values given the exception raised, each stated as raised.<path> = <expression> from whatever is in scope at the step."
        }
      }
    },
    "CheckPreCondition": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Checks a pre-condition of the realised contract at this point in the flow, and raises that pre-condition's exception when it does not hold. It references the pre-condition and copies nothing: what is checked is the pre-condition's own formal specification, evaluated as its contract states it, and what is raised is its own exception. The reference is also the step's allocation — a guard addresses the pre-condition it checks, with no RequirementAllocation beside it. Named after the pre-condition it checks.",
      "properties": {
        "preCondition": {
          "type": "string",
          "description": "Reference to the PreCondition checked, by ID"
        }
      }
    },
    "Undo": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Undoes everything the process has committed so far, newest first, and carries on: the undo a refusal runs, without the refusal. Each committed step is undone by its own compensation, else by its contract's undone-by; what is undone is not undone again by a later refusal or Undo. It names nothing and addresses no requirement, and it may not stand inside concurrent work.",
      "properties": {}
    },
    "ExceptionHandler": {
      "type": "object",
      "$ref": "#/$defs/ActivitySequence",
      "description": "What a request does when the service it asks raises one exception: recover and rejoin the flow, raise one of the calling process's own pre-condition exceptions, or — empty and marked ignoresException — ignore it. Every exception the requested contract's pre-conditions raise must have one; verification refuses a request that leaves one unhandled.",
      "properties": {
        "exception": {
          "type": "string",
          "description": "Reference to an Exception by ID"
        },
        "ignoresException": {
          "type": "boolean",
          "description": "The modeller's statement that an empty handler is meant: the exception is ignored and the flow carries on after the request. Absent means false — an empty handler is then a decision not yet taken, reported as a warning. Meaningless once the handler has a body."
        }
      }
    },
    "MessageHandler": {
      "type": "object",
      "$ref": "#/$defs/BackgroundFlow",
      "description": "A lane of a RequestService that processes a message the requested service declared it may send — ExceptionHandler's counterpart on the other side of the composite widget. A BackgroundFlow, and that is the claim rather than a drawing convenience: the request carries on the moment the service answers, so the lane runs to its own end rather than rejoining the flow. `messageEvent` names the MessageSendEvent a post-condition owns, not the message's data structure, because two post-conditions may declare a send of the same structure meaning different things.",
      "properties": {
        "variant": {
          "type": "string",
          "const": "MessageHandler",
          "description": "Always MessageHandler: the discriminator a reader dispatches on."
        },
        "messageEvent": {
          "type": "string",
          "description": "Reference to a MessageSendEvent by ID"
        }
      }
    },
    "AlternateFlows": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "A branch of which exactly one flow is taken: the first conditional flow whose condition holds, or the default flow when none does. All flows rejoin at a merge.",
      "properties": {
        "conditionalFlows": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ConditionalFlow"
          },
          "description": "The conditional flows, in the order their conditions are tested."
        },
        "defaultFlow": {
          "$ref": "#/$defs/ActivitySequence",
          "description": "The flow taken when no condition holds."
        }
      }
    },
    "ConditionalFlow": {
      "type": "object",
      "$ref": "#/$defs/ActivitySequence",
      "description": "A flow taken when its condition holds: an activity sequence with the formal specification of that condition.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "The condition under which this flow is taken."
        }
      }
    },
    "ConcurrentFlows": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Concurrency of every kind: unconditional flows and conditional flows both terminate on the join, background flows outlive it. At least one flow must be unconditional (an ActivitySequence, possibly empty), so that the join is reached even when no condition holds.",
      "properties": {
        "concurrentFlows": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ConcurrentFlow_polymorphic"
          },
          "description": "The flows, all started together at the fork."
        },
        "synchronization": {
          "type": "integer",
          "minimum": 1,
          "description": "How many lanes the join waits for. Absent: every lane that runs. A count n: the join is passed once n lanes have arrived, and the lanes still running are then stopped — waits and unclaimed tasks at once, work that may already have acted waited for — with what they committed left committed until undone. A background lane never arrives and is not counted. With a count, no lane's names are in scope after the join."
        }
      }
    },
    "ConcurrentFlow_polymorphic": {
      "type": "object",
      "description": "Any one of the kinds of flow a ConcurrentFlows may hold, told apart by its `variant`. `Path` is the name older models wrote for an ActivitySequence.",
      "properties": {
        "variant": {
          "type": "string",
          "enum": [
            "ActivitySequence",
            "BackgroundFlow",
            "ConditionalFlow"
          ],
          "description": "Which kind of flow this is: the discriminator a reader dispatches on. `Path` is read as ActivitySequence."
        }
      },
      "required": [
        "variant"
      ],
      "allOf": [
        {
          "if": {
            "properties": {
              "variant": {
                "const": "ActivitySequence"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/ActivitySequence"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "BackgroundFlow"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/BackgroundFlow"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "ConditionalFlow"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/ConditionalFlow"
          }
        }
      ]
    },
    "Event": {
      "type": "object",
      "description": "What starts a Reaction: a message arriving, an interval elapsing, or a clock striking. An event is never a free-standing element — it is owned by the reaction it triggers and reached only through that reaction's trigger.",
      "properties": {
        "variant": {
          "type": "string",
          "enum": [
            "MessageEvent",
            "DurationEvent",
            "DateTimeEvent"
          ],
          "description": "Which kind of event this is: the discriminator a reader dispatches on to the definition of that name."
        },
        "isEvent": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies an event on the wire; always true."
        }
      },
      "required": [
        "variant"
      ],
      "allOf": [
        {
          "if": {
            "properties": {
              "variant": {
                "const": "MessageEvent"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/MessageEvent"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "DurationEvent"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/DurationEvent"
          }
        },
        {
          "if": {
            "properties": {
              "variant": {
                "const": "DateTimeEvent"
              }
            }
          },
          "then": {
            "$ref": "#/$defs/DateTimeEvent"
          }
        }
      ]
    },
    "MessageEvent": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A message has arrived. `dataStructure` names the message itself, and that data structure is the shared element: a SendMessage naming the same one is what sends this message. That correlates receivers with senders — neither names the other; messageKey and expectedKey say which running instance of a receiver a message is for.",
      "properties": {
        "variant": {
          "type": "string",
          "const": "MessageEvent",
          "description": "Always MessageEvent: the discriminator a reader dispatches on."
        },
        "isEvent": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies an event on the wire; always true."
        },
        "dataStructure": {
          "type": "string",
          "description": "Reference to the DataType that is the message, by ID"
        },
        "messageKey": {
          "type": "string",
          "description": "Which instance this wait is for: a data path rooted at `message` into the awaited structure (`message.claimId`), whose value must equal what expectedKey evaluates to. Stated together with expectedKey or not at all; every correlated receiver of one structure uses the same path, because a publish carries one key. See docs/design/messageCorrelation.md."
        },
        "expectedKey": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "The value a message's messageKey must equal for it to reach this instance, evaluated in the receiver's own scope when the wait opens (`request.claim.id`)."
        }
      }
    },
    "MessageSendEvent": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A message realising a post-condition may cause to be sent. An event, and deliberately not one a Reaction may be triggered by — those three say the model is waiting for something, this says the contract may cause something. `dataStructure` names the message itself, the same shared element a SendMessage and a MessageEvent name; `recipient` names the party addressed, as a role and never an address. There is no recipientSelector: a data path picking one instance of a party belongs to a particular send in a particular flow, not to the contract that permits it. A caller writes a MessageHandler naming this declaration, which is what correlates the two sides.",
      "properties": {
        "variant": {
          "type": "string",
          "const": "MessageSendEvent",
          "description": "Always MessageSendEvent: the discriminator a reader dispatches on."
        },
        "isEvent": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies an event on the wire; always true."
        },
        "dataStructure": {
          "type": "string",
          "description": "Reference to the DataType that is the message, by ID"
        },
        "distribution": {
          "type": "string",
          "enum": [
            "workItem",
            "notification"
          ],
          "default": "workItem",
          "description": "whether the message dispatches a unit of work exactly one consumer must discharge, or announces a fact any number of parties may react to"
        },
        "recipient": {
          "type": "string",
          "description": "Reference by ID to the ResponsibilityDomain addressed — a role, never an address"
        }
      }
    },
    "DurationEvent": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "An interval has elapsed, measured from the moment the process reached the gateway. There is deliberately no anchor field: the position in the flow is the anchor.",
      "properties": {
        "variant": {
          "type": "string",
          "const": "DurationEvent",
          "description": "Always DurationEvent: the discriminator a reader dispatches on."
        },
        "isEvent": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies an event on the wire; always true."
        },
        "duration": {
          "type": "string",
          "description": "ISO 8601 duration, e.g. 'PT72H' or 'P30D'"
        },
        "deploymentOverridable": {
          "type": "boolean",
          "description": "Whether a deployment may supply a different value than the one written here. Absent or false means fixed, the default. The model keeps its own literal either way; this only ever permits an external fact to supersede it. No declared bound — state any reasonable one as prose in this event's detailedDescription annotation instead."
        }
      }
    },
    "DateTimeEvent": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A clock has struck. The time zone is required alongside the schedule because a cron expression names a wall-clock reading, and the UTC offset of a future local time is not knowable in advance — daylight-saving rules change between now and then.",
      "properties": {
        "variant": {
          "type": "string",
          "const": "DateTimeEvent",
          "description": "Always DateTimeEvent: the discriminator a reader dispatches on."
        },
        "isEvent": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies an event on the wire; always true."
        },
        "schedule": {
          "type": "string",
          "description": "Five-field POSIX cron expression, e.g. '0 2 1 * *'"
        },
        "timeZone": {
          "type": "string",
          "description": "IANA time-zone id whose wall clock the schedule is read against, e.g. 'Africa/Johannesburg'"
        },
        "deploymentOverridable": {
          "type": "boolean",
          "description": "Whether a deployment may supply a different value than the one written here. Absent or false means fixed, the default. The model keeps its own literal either way; this only ever permits an external fact to supersede it. No declared bound — state any reasonable one as prose in this event's detailedDescription annotation instead."
        }
      }
    },
    "Reaction": {
      "type": "object",
      "$ref": "#/$defs/ActivitySequence",
      "description": "One flow of an EventTriggeredFlows: the activities carried out when its event occurs. The same shape as a ConditionalFlow — an activity sequence plus the one thing that says when it is taken. The trigger is owned, not referenced, so an event never appears anywhere else in the model.",
      "properties": {
        "trigger": {
          "$ref": "#/$defs/Event",
          "description": "The event whose occurrence selects this flow. Owned by the reaction."
        }
      }
    },
    "EventTriggeredFlows": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "A branch whose flow is chosen by something happening rather than by a condition being tested. The process waits until one of the reactions' events occurs. Unlike AlternateFlows there is no default flow: there is no 'no event occurred' case to fall through to, so a default would be a flow that could never be taken. A timeout is a reaction triggered by a DurationEvent.",
      "properties": {
        "reactions": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Reaction"
          },
          "description": "The flows, one per event awaited; the first event to occur selects its flow."
        }
      }
    },
    "Monitor": {
      "type": "object",
      "$ref": "#/$defs/ActivitySequence",
      "description": "One monitor of a MonitoredActivities: what it listens for, whether it interrupts the body, and the activities it runs when it fires. The shape of a Reaction with a flag, and a different meaning: a reaction is chosen, a monitor fires — a non-interrupting one every time it fires, beside the body. The trigger is owned, as a reaction's is.",
      "properties": {
        "trigger": {
          "$ref": "#/$defs/Event",
          "description": "The event the monitor listens for. Owned by the monitor."
        },
        "interrupting": {
          "type": "boolean",
          "default": true,
          "description": "Interrupting: when it fires the body is cancelled — waits and unclaimed tasks at once, work that may have committed waited for — then its activities run and the process continues after the block. Not interrupting: its activities run as a background lane each time it fires, and the body carries on. A BPMN boundary event's distinction, with its default."
        }
      }
    },
    "MonitoredActivities": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "A stretch of a process watched for something happening while it runs — BPMN's event sub-process, and its boundary events on a sub-process or a task. The monitors listen from entering the block until its body completes. A monitor's activities see only what was bound before the block, never the body's; after the block, what the body bound is in scope only when no monitor can interrupt it. One entry, one exit, as every block.",
      "properties": {
        "activitySequence": {
          "type": "object",
          "$ref": "#/$defs/ActivitySequence",
          "description": "The body: the activities watched while they run."
        },
        "monitors": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Monitor"
          },
          "description": "The monitors listening while the body runs."
        }
      }
    },
    "SendMessage": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Sends a message — the other half of a MessageEvent. `dataStructure` names the message, and a reaction whose MessageEvent names that same data structure receives it. `recipient` names the party addressed and `recipientSelector` picks which instance of it; neither is an address, since how one reaches a party is a design decision. How many parties process the message is separate again, and is said by `distribution`.",
      "properties": {
        "dataStructure": {
          "type": "string",
          "description": "Reference to the DataType that is the message, by ID"
        },
        "distribution": {
          "type": "string",
          "enum": [
            "workItem",
            "notification"
          ],
          "default": "workItem",
          "description": "whether the send dispatches a unit of work exactly one consumer must discharge, or announces a fact any number of parties may react to. It is stated on the sender because no other party holds the intent: the correlating data structure is a plain data type, and a single receiver looks the same under both."
        },
        "recipient": {
          "type": "string",
          "description": "Reference by ID to the ResponsibilityDomain that is the party addressed — a role, never an address. An email address or a queue name is a design decision, which is what lets one send be realised as an email to a private client and as a queue message to a corporate one; contact details are fields of the party's own data structure, not of the message. Optional under both distributions, but a workItem naming none is a point-to-point send with no point, and process verification reports it."
        },
        "recipientSelector": {
          "type": "string",
          "description": "The data path identifying which instance of the recipient party is addressed, such as `request.order.client`. Prose the model does not resolve, in the same form ForEach takes its collections in — without it the model says 'a Client' where the process means one particular client."
        },
        "messageConstraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/MessageConstraint"
          },
          "description": "What the message carries, field-group by field-group, under `sent`."
        },
        "compensation": {
          "$ref": "#/$defs/Compensation",
          "description": "How this send is undone (a correction: a message cannot be unsent). Its request constraints read the process's names at the step."
        },
        "compensationNotNeeded": {
          "type": "string",
          "description": "That this send needs no undo, and why: what it commits lapses, or repeating it is harmless. Excludes 'compensation'."
        }
      }
    },
    "AwaitMessage": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Blocks until a message of a particular type arrives, then the process carries on — the receive task beside SendMessage's send task. `dataStructure` names the message, the same element a SendMessage names to send it. Reach for this rather than an EventTriggeredFlows when the process is waiting rather than choosing: a single-reaction gateway says the same thing but draws a diamond, three edges and a merge to say it. There is no timeout — a wait that gives up is a branch, which is what an EventTriggeredFlows with a DurationEvent says.",
      "properties": {
        "dataStructure": {
          "type": "string",
          "description": "Reference to the DataType that is the message, by ID"
        },
        "messageKey": {
          "type": "string",
          "description": "Which instance this wait is for: a data path rooted at `message` into the awaited structure (`message.claimId`), whose value must equal what expectedKey evaluates to. Stated together with expectedKey or not at all; every correlated receiver of one structure uses the same path, because a publish carries one key. See docs/design/messageCorrelation.md."
        },
        "expectedKey": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "The value a message's messageKey must equal for it to reach this instance, evaluated in the receiver's own scope when the wait opens (`request.claim.id`)."
        }
      }
    },
    "ForEach": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Carries out its body once for each element of a collection, in order or concurrently, optionally stopping early and collecting each iteration's result.",
      "properties": {
        "sourceCollection": {
          "type": "string",
          "description": "the expression resolving the collection worked through, e.g. request.items"
        },
        "resultCollection": {
          "type": "string",
          "description": "the expression naming where the body's per-element results are collected, e.g. response.assessments"
        },
        "elementVariable": {
          "type": "string",
          "description": "the variable bound to each element of the source collection during iteration, e.g. item"
        },
        "processingMode": {
          "type": "string",
          "enum": [
            "sequential",
            "parallel"
          ],
          "default": "sequential",
          "description": "whether elements must be worked through in order or may be processed concurrently"
        },
        "continueWhile": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "condition under which the for-each carries on, in continuation polarity — it stops before every element has been processed once this no longer holds"
        },
        "activitySequence": {
          "type": "object",
          "$ref": "#/$defs/ActivitySequence",
          "description": "The body, carried out once for each element."
        },
        "carriedVariables": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/LoopVariable"
          },
          "description": "What changes across the loop's iterations - a fold. Without these a loop reads the same values every time round, which for a While means it can never terminate."
        }
      }
    },
    "While": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Repeats its body while a guard holds — the sibling of ForEach, not a merger with it. A ForEach terminates because its collection is finite; a While only because its guard eventually fails, which is why it alone carries maxIterations and why it carries no processingMode: iteration n+1 depends on the state after iteration n, so a parallel while is not a coherent thing to express.",
      "properties": {
        "continueWhile": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "the guard, in continuation polarity — the loop proceeds while this holds"
        },
        "testBefore": {
          "type": "boolean",
          "default": true,
          "description": "whether the guard is tested before the first iteration (an ordinary while-loop) or after it (a do-until, whose body always runs at least once)"
        },
        "maxIterations": {
          "type": "integer",
          "minimum": 0,
          "description": "an upper bound on iterations, when the modeller can state one — an integer rather than an expression so that it can be analysed, as BPMN's loopMaximum is"
        },
        "activitySequence": {
          "type": "object",
          "$ref": "#/$defs/ActivitySequence",
          "description": "The body, repeated while the guard holds."
        },
        "carriedVariables": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/LoopVariable"
          },
          "description": "What changes across the loop's iterations - a fold. Without these a loop reads the same values every time round, which for a While means it can never terminate."
        }
      }
    },
    "BackgroundFlow": {
      "type": "object",
      "$ref": "#/$defs/ActivitySequence",
      "description": "A flow of concurrent flows that runs on independently and ends in a flow end of its own instead of arriving at the join.",
      "properties": {
        "isBackgroundFlow": {
          "type": "boolean",
          "const": true,
          "description": "A constant marker that identifies a background flow on the wire; always true."
        }
      }
    },
    "Process": {
      "type": "object",
      "$ref": "#/$defs/ActivitySequence",
      "description": "The business process realising a service contract: the activities it carries out, the control flow connecting them, and which step addresses each of the contract's functional requirements.",
      "properties": {
        "serviceContract": {
          "type": "string",
          "description": "Reference to a ServiceContract by ID"
        },
        "requirementAllocations": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/RequirementAllocation"
          },
          "description": "Which step of this process addresses each functional requirement of the contract it realises."
        },
        "unplacedActivities": {
          "$ref": "#/$defs/ActivitySequence",
          "description": "Activities allocated to a functional requirement but not yet positioned in the flow. Allocating a requirement to a step says what the process must do and nothing about when, so an activity created in the Responsibility Allocation View waits here until the modeller places it on a transition. Its order carries no meaning — it is the order things were allocated in, not a sequence anything runs, and nothing lays it out."
        }
      }
    },
    "ProcessVariable": {
      "type": "object",
      "$ref": "#/$defs/Variable",
      "description": "A value a process names for itself: what a DefineVariable introduces, what a RequestService binds its response to, and what a loop carries across its iterations. A Variable with a multiplicity, and deliberately not a Field — a field also carries 'relationship', which describes how an owning structure relates to the value, and a local has no owning structure. Process data is lexically scoped and single-assignment: a variable is introduced and bound at one point, and its scope is the remainder of the enclosing ActivitySequence together with everything nested in it.",
      "properties": {
        "multiplicity": {
          "type": "string",
          "description": "Multiplicity of the variable, e.g. '0..1', '1', '0..*', '1..*'. Not decoration: a name bound to a collection must be reached with '->' and one bound to a single value with '.'.",
          "default": "1"
        }
      }
    },
    "LoopVariable": {
      "type": "object",
      "$ref": "#/$defs/ProcessVariable",
      "description": "A process variable a loop carries across its iterations - a fold. Single assignment has one consequence that must be answered: if nothing is ever re-bound, a While guard reads the same values every time round and can never terminate. The two expressions are two fields because they are evaluated in two different environments: 'initialiser' at the loop, outside it; 'next' at the end of the body, inside it.",
      "properties": {
        "initialiser": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "What the variable starts as, evaluated in the environment outside the loop."
        },
        "next": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "What the variable becomes, evaluated in the environment at the end of the loop body."
        }
      }
    },
    "DefineVariable": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Creates and initialises a process variable. Defining and binding in one step is what removes the definite-assignment problem rather than solving it: a variable cannot exist unbound, so nothing has to compute whether it was assigned on every path into a merge, and one introduced inside a branch or a loop body simply leaves scope at the end of that block.",
      "properties": {
        "variable": {
          "$ref": "#/$defs/ProcessVariable",
          "description": "The variable introduced. Owned: there is nothing to set the slot to, so it cannot be deleted on its own."
        },
        "initialiser": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "What the variable is bound to, in the environment at this point in the process. Optional only so a half-authored step is a legitimate work-in-progress state."
        }
      }
    },
    "PopulateResponse": {
      "type": "object",
      "$ref": "#/$defs/Activity",
      "description": "Populates part of the process's own response, from whatever is in scope where it stands — so each branch may build the response its own way. Every path through the process that completes must populate each response field once.",
      "properties": {
        "responseConstraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ResponseConstraint"
          },
          "description": "The population, field-group by field-group, each stated in assignment normal form: response.<path> = <expression>."
        }
      }
    },
    "MessageConstraint": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "States one field-group of the message a SendMessage sends, as `sent.<field> = …` — what the receiver reads under `message`. ExceptionConstraint's twin for a send; `sent` rather than `message`, since inside a message handler that is the message received. See docs/design/messageCorrelation.md §11.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "Mandatory: the whole reason to create an ExceptionConstraint is to state a body."
        }
      }
    },
    "Decision": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A DMN decision: a realization of a service contract declaring isQuery, answering a request by rules (a decision table) or by one expression (a literal expression). Every cell is an element with its own id and formal specification.",
      "properties": {
        "serviceContract": {
          "type": "string",
          "description": "Id of the pure service contract this decision realizes."
        },
        "kind": {
          "type": "string",
          "enum": [
            "table",
            "literalExpression"
          ],
          "description": "A decision table, or one literal expression filling the one output."
        },
        "hitPolicy": {
          "type": "string",
          "enum": [
            "unique",
            "first",
            "any",
            "ruleOrder",
            "collect",
            "priority",
            "outputOrder"
          ],
          "description": "Which matching rules a table's answer comes from. priority and outputOrder are read but refused: they rank outputs by allowed values URDAD does not record."
        },
        "aggregator": {
          "type": "string",
          "enum": [
            "sum",
            "min",
            "max",
            "count"
          ],
          "description": "Under collect only: what the matched rules' outputs come to. count counts matched rules (DMN 1.2)."
        },
        "inputs": {
          "type": "array",
          "description": "The input columns, in order.",
          "items": {
            "$ref": "#/$defs/DecisionInput"
          }
        },
        "outputs": {
          "type": "array",
          "description": "The output columns, in order.",
          "items": {
            "$ref": "#/$defs/DecisionOutput"
          }
        },
        "rules": {
          "type": "array",
          "description": "The rules, in table order — which matters under first and ruleOrder.",
          "items": {
            "$ref": "#/$defs/DecisionRule"
          }
        },
        "literalExpression": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "For a literal-expression decision: the expression, over request, giving its one output."
        }
      }
    },
    "DecisionInput": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "An input column. Its name is what the cells test and what output cells and later inputs may name.",
      "properties": {
        "expression": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "What the input is, over request and earlier inputs, e.g. request.applicant.age."
        }
      }
    },
    "DecisionOutput": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "An output column, filling one field of the realized contract's response.",
      "properties": {
        "field": {
          "type": "string",
          "description": "Id of a field of the realized contract's response structure."
        }
      }
    },
    "DecisionRule": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "One rule of a decision table: a cell per input column, all of which must hold, and a cell per output column.",
      "properties": {
        "inputEntries": {
          "type": "array",
          "description": "One per input column, in order.",
          "items": {
            "$ref": "#/$defs/DecisionInputEntry"
          }
        },
        "outputEntries": {
          "type": "array",
          "description": "One per output column, in order.",
          "items": {
            "$ref": "#/$defs/DecisionOutputEntry"
          }
        }
      }
    },
    "DecisionInputEntry": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A rule's condition on one input, stored as URDAD-OCL over the input's name (age < 18); read in URDAD-FEEL as unary tests (< 18). Absent or empty matches anything.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "The condition, in URDAD-OCL over the input's name. Absent or empty matches anything."
        }
      }
    },
    "DecisionOutputEntry": {
      "type": "object",
      "$ref": "#/$defs/Element",
      "description": "A rule's value for one output column. Absent or empty gives null.",
      "properties": {
        "formalSpecification": {
          "$ref": "#/$defs/FormalSpecification",
          "description": "The value, in URDAD-OCL. Absent or empty gives null."
        }
      }
    }
  }
}
