MN 114: Primmel Language Specification

1. Foreword

This document is the language specification for Primmel version 3 , the executable modelling language for SMART standards. It is the first edition of the specification of Primmel v3, and it supersedes MN 113 (the Multi-Modal Modelling Language specification) as the complete language reference. MN 113 remains the record of the v2 surface, every construct of which Primmel v3 continues to accept (Annex D).

Document identity:

Document MN 114, Primmel Language Specification
Language version specified Primmel version 3.2 (v3.2)
Edition 1
Date 2026-09-05
Status Draft, published for comment
Reference implementation @primmel/primmel (primmel-ts) 1.9.0

Revision note (2026-09-05). This revision of the draft removes the roadmap marks of the v3.2 extension set: the condition of the verification doctrine is met. Every v3.2 construct and checker rule (C110 to C118) is implemented by the reference toolchain at @primmel/primmel 1.9.0, and the standalone conformance suite at version 3.2.0 pins each rule's error-severity leg (clauses CON-01 to CON-09; 112 cases across 37 clauses, every case passing). The specification text is otherwise unchanged — the removal is normative activation, not new content — with two register-keeping exceptions: the calculation-signature example of 13.7.1 now shows the entry form the toolchain ships (the v2 <name> : <type> { ... } spelling), and the three rollout legs the toolchain reports at warning severity in 1.9.0 are stated as such where the rules are described (C110's cross-field alias overlap, C115's unit resolution, C118's unit resolution — the error forms tighten when the estate's terms and quantity registers are re-authored, tracked in TODO.primmel/11d). The proof by consumption: the OIML SMART estate (oimlsmart/smart#256) declares R 144's capabilities axis as a top-level dimension and projects all six facet families byte-clean in both codec directions.

Revision note (2026-09-08). This revision of the draft adds the requirement-namespace pin to the composition rules ( 7.6 , checker rule C119): a requirement scope a composed package declares is owned by that package, and a downstream package never declares a requirement class or requirement at or under an owned namespace — the language-level form of the layering doctrine the OIML SMART estate enforces at build time. The reference toolchain ships the rule at @primmel/primmel 1.10.0; the conformance suite at version 3.3.0 pins it (clause PKG-06; 118 cases across 38 clauses).

Revision note (2026-09-04). This revision of the draft adds the v3.2 extension set — the consumption constructs of primmel/spec#18, from the measured requirements of a production retrieval consumer: the term alias family ( aliases , colloquial , spelling-tagged variants; 13.10.1 ), the typed applicability dimension (the top-level dimension construct and the applicability namespace, 10.6 , 11.1.1 ), the resolvable cross-standard reference (the document-anchor grammar and the structured reference identity, 14.7 ), the forward lineage edge (manifest superseded_by and the edition lineage graph, 9.3.1 ), and units-typed quantities (the calculation signature's quantity_kind + range facets and the limit's quantity { kind unit } block, 13.7.1 , 11.1.2 ), and the verdict chain with the instance-parameter schema (verdict inputs may name verdicts; requirement parameters carry unit and bind , 11.3 , 11.1.3 ), and the impact-graph export (the impact edge registry and the impact-graph/1 projection, 17.8 ), with their checker rules (C110 to C118, Annex B). The addition is strictly additive: every construct, facet, and rule of the 2026-08-28 revision is unchanged. Unlike the v3.1 set, the v3.2 constructs land ahead of their kernel implementation: each is marked roadmap with its tracking reference and makes no normative claim until the toolchain wave ships (the verification statement below applies to the unmarked content). The same revision brings the rule catalog back into register with the toolchain (C109, published in primmel-ts 1.8.1, was missing from the 2026-08-28 printing of Annex B).

Revision note (2026-08-28). This revision of the draft adds the v3.1 extension set: the dataspace as a model object, the policy construct in Primmel's own policy grammar, the trust reference form, and the generalized correspondence annotations (clause 19), with their checker rules (C104 to C108, Annex B) and their grammar productions (clause 18). The addition is strictly additive: every construct, facet, and rule of the 2026-08-20 revision is unchanged.

This document is published as a draft for comment . Comments are invited by issue on the public source repository, https://github.com/primmel/spec/issues , which is the named contact for this draft. The draft status will be lifted after the comment window closes and the received comments have been dispositioned.

Verification statement. Every normative statement in this document describes behavior that the reference implementation, the primmel-ts toolchain at the version named above, exhibits today. No construct, rule, or format is specified here aspirationally. Where a clause can be checked mechanically, the clause says how: the construct registry of Annex A is derived from the parser configuration of the toolchain ( packages/primmel/src/ser-des/config/index.ts ), and the checker rule catalog of Annex B reproduces the machine-readable registry that primmel check --rules prints ( packages/primmel/src/check-rules.ts ). A reviewer can verify either annex against the toolchain source within one command. Content that is planned but not implemented is marked roadmap with its tracking reference and makes no normative claim.

2. Introduction

Primmel is a domain-specific language for representing SMART standards in an executable form. A Primmel-modelled standard is a structured artifact whose subjects, requirements, processes, data requirements, and evidential requirements are defined precisely enough that a computer can validate them, query them, execute them, and reason about them.

Primmel v2, formerly MMEL and specified in MN 113, established the four pillars of the language: data (records, enumerations, registries), process (events, gateways, canvases), compliance (provisions with RFC 2119 modality, traceable to source clauses), and measurement (typed values with validation expressions), together with the cross-cutting artifacts (mappings, references, approvals). Primmel v3 keeps all of it and adds the layer the compliance use case always needed: the subject . The delta, stated once:

Primmel v2 (MN 113) Primmel v3 (this document)
Provisions as text plus modality Subjects with a full anatomy (IS / HAS / DOES); requirements as constraints bound to subject aspects
Processes as canvas nodes Processes as recursive subjects: abstract or executable, with signature, state, registers, and executors
One model kind Reference and implementation model kinds, related by mapping; packages with `uses` composition, five package kinds, and editions
Mapping coverage (full / minimal / partial) The same calculus, machine-checked against typed anchor sets
Data classes and registries The same, plus quantities and units, the IS/HAS value duality, series, and time primitives
References to clauses Clause and fragment provenance as typed relations (`ref`), sentence-level text-coverage audit, and multilinguality (ISO 24229 spelling codes)

The design center of v3 is a single claim: a requirement is a *constraint bound to a subject model , a test is an operation on that subject*, evidence is recorded exhibition , and a verdict is a re-executable judgment . The document of the standard is reconstructed from the model as a view; the model is the source of truth for the machine, and the published text remains the source of truth for the committee.

The conceptual foundations of the language (the IS–HAS–DOES modelling system, its closure, completeness, and extensibility proofs, and the kernel/surface architecture) are published separately in the Primmel documentation volume ( primmel/primmel-smart-docs , volumes 0 and I). This specification is self-contained: it normatively specifies the syntax, the semantic model, the serialization, and the conformance requirements, and it cites the foundations informatively where a design rationale aids the reader.

The reference usage of Primmel v3 is the OIML SMART program, which authors its Recommendation models (R 60, R 91, R 129, R 144) and scheme framework as Primmel v3 packages and runs its certification workflows from them. The language itself is program-agnostic: nothing in this document is specific to one standards program.

3. Scope

This document specifies the Primmel modelling language, version 3. It specifies:

  1. the lexical structure and the syntax of the Primmel model text (the .prl file), including every top-level construct and its facets;
  2. the semantic model: the tier system, the subject anatomy (IS / HAS / DOES), requirements and conformance tests bound to subjects, processes as recursive subjects, data and quantity modelling, references and relations, and multilinguality;
  3. the serialization rules: the file types ( .prl, .prm), the package directory format, the include directive, and the canonical emission rules that make parse and serialize a round trip;
  4. conformance requirements: what a conforming Primmel document, parser, serializer, checker, and package shall, should, and may do.

This document does not specify:

This document is platform-independent and tool-agnostic: it defines a text language and the behavior of tools that read and write it, not any one tool.

4. Normative references

The following documents are referred to in the text in such a way that some or all of their content constitutes requirements of this document. For dated references, only the edition cited applies. For undated references, the latest edition of the referenced document (including any amendments) applies.

5. Terms and definitions

For the purposes of this document, the following terms and definitions apply.

5.1. subject

model element that answers the three questions of the subject anatomy: what it is (IS), what it exhibits (HAS), and what it does (DOES)

Note 1 to entry: The subject is the central model kind of the primary tier ( 7.2 ). A subject may be a physical or logical object type (an instrument), a process, or any other bearer of claims.

5.2. aspect

named, typed slot of a subject, belonging to exactly one aspect family (IS, HAS, or DOES)

5.3. entity kind

one of the five subject-chain construct kinds of the primary tier: instrument , attribute_definition , capability , behavior , condition_set

Note 1 to entry: The subject construct is the general form of which instrument is the measuring-instrument specialization ( 10.1 , 10.2 ).

5.4. package

one directory holding a Primmel model: a package.primmel manifest plus content files merged by convention ( 16.3 )

5.5. package kind

classification of a package declared in its manifest: core , module , rec , product_reference , or certification_program

5.6. reference model

model published by a standards publisher that states what a conforming subject is, has, and does

5.7. implementation model

model published by an adopting organization, related to a reference model by a declared mapping

5.8. mapping

element-by-element declared alignment of an implementation model to a reference model, with a coverage calculus

5.9. requirement

constraint over the aspect paths of a subject, carrying a statement, an optional formal limit, modality, and provenance

5.10. conformance test

operation on a subject that constrains inputs, environmental context, and state, and observes outcomes against acceptance criteria

5.11. verdict

named, reusable derivation of a judged quantity from test inputs, referenced by requirements, tests, and forms instead of restated

5.12. instance

model element declaring one member of a subject chain, with its own exhibited values and its upward chain links

5.13. predicate

declared relation kind usable in a ref triple; the predicate registry of a package is data, and an undeclared predicate is an error (C103)

5.14. provenance

the recorded derivation of a model element from a source document clause or fragment, expressed as a derives-from reference or a legacy source block

5.15. tier

one of the five dependency levels of the language: foundations, primary, secondary, tertiary, cross-cutting ( 7.2 )

5.16. edition

a dated state of a package in its lifecycle, with a status and a validity window, registered in the package manifest

5.17. allowlist

the per-package .primmel-allowlist.prl file that marks known checker findings as KNOWN debt and fails on STALE entries ( 17.4 )

5.18. strict parse mode

parser mode in which an unrecognized top-level keyword is an error; contrast with the default lenient mode, which skips unknown keywords for forward compatibility ( 9.5 )

5.19. canonical form

the single emission spelling a conforming serializer produces for a model, such that loading and re-serializing is a fixed point ( 16.5 )

5.20. dataspace

model object declaring one dataspace: its participant classes, artifact classes, policy sets, trust anchors, compatibility register, and governance citations ( 19.1 )

5.21. policy

usage-policy set as first-class model content: rules of kind permission, obligation, or prohibition over a dataspace's artifact classes and their actions, with constraints in the embedded expression dialect ( 19.2 )

5.22. trust reference

model-level reference to a trust-plane identity (an organization by its registry identifier, optionally a key by key id), opaque to the model and resolved by the consuming platform at runtime ( 19.3 )

5.23. correspondence

declared alignment of a model element to an external concept (maps-to, never is-defined-by), expressed as a corresponds annotation and consumed by the expression codecs ( 19.4 )

5.24. alias

admitted alternate designation of a term, carried as data in the term's alias family ( 13.10.1 ) so that consumption systems can bridge vocabulary a user types to the defined term

5.25. colloquial form

informal, everyday phrasing recorded on a term (the colloquial facet of 13.10.1 ) that is not a designation of the concept: it carries no normative force and exists to bridge retrieval

5.26. applicability dimension

named, typed axis of the model that an applicability { ... } entry keys on: a classification dimension of an instrument, an attribute definition declared is_dimension true , or a top-level dimension declaration ( 10.6 ), with a declared value set, optional subsumption links, and a match mode ( 11.1.1 )

5.27. document anchor

the address of a document or of a clause within it: a document URN, optionally suffixed by #clause-<clause> in the document's own clause numbering ( 14.7 )

5.28. edition lineage

the declared succession graph of a package's editions: the editions register order, the supersedes / replaces / superseded_by edges, the lifecycle status , and the validity windows of the package manifest ( 9.3.1 )

5.29. acceptance chain

the explicit decision graph of a judgment, every hop a citable model element: requirement → the limit's accepts verdict binding → the verdict's inputs (symbols and sub-verdicts) → each verdict's acceptance decision ( 11.3 )

5.30. impact graph

the adjacency form of the model's edge-bearing facets, exported by the toolchain as a versioned one-way projection with a forward and a reverse index, consumed for change-impact reads ("what does this change affect?") without walking the authored trees ( 17.8 )

6. Conformance

6.1. Normative keyword convention

The key words MUST , MUST NOT , SHOULD , SHOULD NOT , and MAY in this document are to be interpreted as described in RFC 2119, as clarified by RFC 8174, and they appear in small capitals as here only when used in that sense. Modality keywords that are part of the modelled content of a standard (the modality facet values SHALL, SHOULD, MAY, CAN, MUST; see 11.6 ) are data of the language, not conformance keywords of this specification.

6.2. Conformance classes

This document defines five classes of conformance:

  1. a conforming document (a Primmel model text);
  2. a conforming parser;
  3. a conforming serializer;
  4. a conforming checker;
  5. a conforming package.

6.3. Conforming document

A document is a conforming Primmel v3 document if and only if:

  1. it is a UTF-8 text whose tokenization under the lexical rules of lexical-structure succeeds: every quoted string terminates, every block closes, and escapes are used only as defined;
  2. its top level is a sequence of declarations each of which is either a special declaration ( special-declarations) or a construct declaration whose keyword is registered in annex-a-constructs, with an identifier and a block payload as that annex states;
  3. within each construct, every facet it uses is one of the facets the construct accepts ( subject-model to multilinguality, dataspaces-policies-trust, and annex-a-constructs); a document that relies on an unregistered facet is not a conforming document, even though a lenient parser will accept and skip that facet ( parse-modes);
  4. it declares no identifier twice within the same construct collection ( identifiers).
NOTE
A conforming document may still carry checker findings ( Annex B ): dangling cross-references, coverage warnings, and similar semantic findings are checker matters, not document conformance matters.

6.4. Conforming parser

A conforming parser:

  1. MUST implement the lexical rules of lexical-structure exactly: both comment openers, quoted strings with the two escape sequences, and nestable quote-aware blocks;
  2. MUST accept every construct keyword registered in annex-a-constructs, with its aliases, and MUST parse every facet of every construct as specified in subject-model to multilinguality and dataspaces-policies-trust;
  3. MUST accept facet order freely within a construct block and MUST accept declaration order freely at the top level;
  4. MUST report a duplicate identifier within a construct collection as a parse-time issue, and MUST NOT silently discard the earlier declaration without reporting;
  5. MUST reject a truncated declaration (a keyword expecting an identifier or a payload that is absent) with an error;
  6. MUST reject an unterminated string or block with an error;
  7. in strict parse mode, MUST reject an unrecognized top-level keyword with an error; in lenient parse mode (the default), MUST skip an unrecognized top-level keyword and continue, and MUST skip an unrecognized facet within a known construct together with its value, preserving the parse of the remainder ( parse-modes);
  8. when loading from files, MUST implement the include preprocessing of include-directive, including cycle detection;
  9. when reading a .prm file, MUST accept both the MMEL_MAP and the Primmel_MAP type spellings and MUST preserve authored spellings ( prm-format).

A conforming parser SHOULD attach source positions (line and column) to its errors and issues, as the reference implementation does.

6.5. Conforming serializer

A conforming serializer (a dump implementation):

  1. MUST emit the canonical form of canonical-form for every model element it serializes, including the quoting rule (quote exactly when a value contains whitespace, a brace, or a quote, ends in a colon, or begins with a comment opener) and the escaping rule ( \" and \\);
  2. MUST be a fixed point under load: for every model m, loading the serialization of m and serializing again MUST yield the identical text ( load ∘ dump is idempotent on the model, and dump ∘ load ∘ dump is textually stable);
  3. MUST emit a URN-anchored provenance record in the canonical `ref derives-from` form and MAY emit a free-text provenance record in the legacy source block form ( provenance-forms);
  4. MUST NOT emit a construct in a spelling the parser does not accept.

6.6. Conforming checker

A conforming checker:

  1. MUST implement every rule of the catalog in annex-b-rule-catalog at its catalogued severity, and MUST run the normal-level rules at the default level;
  2. MUST additionally run the audit-level rules when invoked at audit strictness, and MUST enforce the coverage budgets at that level ( check-levels);
  3. MUST promote every warning to an error at the strict level, excepting only findings marked KNOWN by the package allowlist and budget-covered coverage findings ( check-levels, allowlist);
  4. MUST treat a STALE allowlist entry (one that matches no current finding) as an error, and MUST treat a KNOWN entry as printed but never failing ( allowlist);
  5. MUST exit non-zero when any error-severity finding survives.

A conforming checker SHOULD print its findings with rule identifiers from Annex B , so that allowlist entries and audit records can cite them.

6.7. Conforming package

A package is a conforming Primmel v3 package if and only if:

  1. it is a directory laid out per package-layout, with a package.primmel manifest whose declared facets are those of package-manifest;
  2. every model file in it is a conforming document ( conforming-document);
  3. its manifest declares a package kind from the five kinds of package-kinds when it declares one;
  4. its composition declarations ( uses, provides, requires, maps_to) satisfy the composition rules of composition-rules: imports resolve, nothing imported is redefined, the import graph is acyclic, and no namespace is both imported and mapped;
  5. it passes the checker at the default level with zero error-severity findings.
NOTE
A conforming package may carry warning-severity findings; programs that require zero warnings run the checker at the strict level ( 17.2 ).

6.8. Verification method

Conformance of a parser, serializer, or checker is demonstrated by executing the behaviors this clause requires against the reference implementation's public test suite, which exercises every construct of Annex A and every rule of Annex B ( packages/primmel/test/ of the primmel-ts repository). A standalone conformance test suite, runnable against third-party implementations, ships with the reference toolchain (the conformance/ directory of primmel-ts, version 3.2.0, with a clause-tagged corpus and a third-party adapter contract); the suite is the execution form of this clause, and adopting it as a conformance requirement is roadmap for a later edition of this document.

7. Language overview

7.1. The modelling claim

A Primmel model expresses what a standard says (reference content), what a conforming subject is, has, and does (subject content), how conformity is checked (operations and evidence), and how conformity is established and judged (workflow and verdicts), in one language. Four consequences organize everything else:

  • secondary models anchor to the subject and own no subject facts (a requirement binds aspect paths; it never restates them);
  • facts and judgments are separate tiers (a test report contains no verdicts; a verdict is a re-runnable function over definitions and facts);
  • growth happens inside the semantic sorts (new kinds, properties, values, transitions), never beside them;
  • the document of the standard is a reconstruction from the model, not the source of it.

7.2. The tier system

Every model element lives on exactly one tier, and dependencies point only upward:

Tier What lives there Examples
Foundations What everything references; anchored to nothing in the subject Vocabulary registers, parties and roles, units and value types, time primitives
Primary The subject and its aspects The instrument chain, attribute definitions, behaviors, condition sets, state
Secondary Models anchored to primary aspect paths Requirements (constraints), conformance tests (operations), forms (evidence views)
Tertiary Execution and judgment over secondary × primary instances Runs and evidence records, verdicts, evaluations, decisions, certificates, workflow processes, monitors, dataspaces and their usage policies
Cross-cutting Properties of every element on every tier Provenance, time and versioning, the traceability spine, multilinguality, correspondence annotations

A downward reference is a modelling error, and the checker reports one where the dependency is machine-visible ( Annex B ). The tier law has two named consequences: the anchoring rule (secondary models bind subject facts, they never restate them) and the fact/judgment firewall (evidence contains no verdicts).

7.3. Packages and the five package kinds

The unit of publication and reuse is the package : one directory holding a manifest and content files ( 16.3 ). A manifest declares exactly one package kind when it declares one:

Kind Meaning
core The shared kernel: vocabulary, parties, units, value types; composed into other packages, anchored to no Recommendation
module A shared capability package consumed by two or more Recommendation packages
rec A publishable Recommendation: the reference model of one standard
product_reference A manufacturer's product model, related to a Recommendation by mapping only (never a refinement of the standard); carries `manufacturer`, `product`, and `maps_to` facets, and is imported by pinned abstract import (`uses { acme-lc500@2021 }`, C81–C83)
certification_program A scheme operator's certification program: a fourth publisher with the product-reference shape, related to Recommendations (`maps_to`) and to product packages (pinned abstract imports) by mapping only, composed into nothing (C97, C98)
NOTE
The kind facet is optional in the grammar; a manifest without one is an ordinary (rec) package. The five kinds are the closed enumeration the parser accepts ( PackageKind in packages/primmel/src/types/Package.ts ).

7.4. Artifact kinds

A Primmel workspace is made of the following artifact kinds:

Extension Kind Purpose
.prl file A model text: the core artifact. Plain text, UTF-8. The `.mmel` extension is the legacy spelling, accepted everywhere `.prl` is (<<annex-d-lineage,Annex D>>).
package.primmel file The package manifest: the only required file of a package (<<package-manifest,9.3>>).
.prm file A Primmel Map: a JSON mapping between two models, versioned independently of the models it links (<<prm-format,16.4>>).
.prd payload Clause-level extracts of a source standard, consumed by the text-coverage audit as sentence payloads (<<coverage-audits,17.5>>). Consumer-side format; informative here.
.pws/ directory A Primmel Workspace: runtime records produced when a model runs. Consumer-side format; informative here.

7.5. Reference models, implementation models, and mapping

A standards publisher publishes a reference model. An adopting organization publishes an implementation model. A mapping connects the two, element by element, expressed either in-model ( map_profile , 12.8 ) or as a standalone .prm file ( 16.4 ); the two serializations are equivalent and bridge into one model form. The mapping coverage calculus (full, minimal, partial, none) is computed per target namespace, and authored coverage assertions are checked against the computed calculus (C23). A namespace that is imported into a package ( uses ) MUST NOT also be the target of a mapping from that package (C24, import-not-mapping): import is structural inclusion, mapping is the compliance relation, and the two are disjoint.

7.6. Package composition

A package composes other packages structurally with uses (the deprecated single extends is read as a one-element uses ). Composition semantics:

  1. the dependency closure is merged in topological order, dependencies first, the root package last;
  2. a downstream package may reference upstream identifiers but MUST NOT redefine them (C28, no-redefine);
  3. the import graph MUST be acyclic (C29);
  4. every import MUST resolve through the package locator in use (C27);
  5. capability tokens ( provides, requires) are matched across the closure: an unsatisfied requires is an error (C31); a provides that nothing consumes is a warning unless waived (C30);
  6. a version-pinned entry id@edition is an abstract import of a product reference package: located and checked (C83), never content-merged.
  7. a requirement namespace — a requirement class identifier path, e.g. /req/cs — declared by a composed package is OWNED by that package ( requirements-and-conformance): a downstream package may reference its provisions but MUST NOT declare a requirement class or a requirement whose identifier sits at or under an owned namespace (C119, namespace-pin-violation; the exact-identifier case is C28).

Editions are the package lifecycle: a manifest registers its editions , the lifecycle status ( current , preview , superseded , withdrawn ), the validity window, and the supersedes / replaces / superseded_by lineage edges, all checked by the edition rules (C77–C80, C85, C113; 9.3.1 ). The structural model diff between two package states is a toolchain operation ( primmel diff , 17.6 ), not a language construct.

8. Lexical structure

8.1. Characters and whitespace

A Primmel model text is a UTF-8 character sequence. The grammar is whitespace-insensitive: any run of whitespace characters (space, tab, carriage return, line feed, and the other Unicode whitespace) separates tokens and carries no meaning. There are no statement terminators; a construct ends where its block closes.

8.2. Comments

Two comment openers exist, each running to the end of the line:

# a hash comment
// a solidus comment

A comment opener is recognized only at a token boundary. A # or // inside a token is literal text: the identifier StdS#Process5 is one token, not a token followed by a comment. This is why the emission rule of 16.5 quotes any value that begins with a comment opener but emits a value that merely contains one bare.

8.3. Tokens

Three token forms exist:

bare token
a maximal run of non-whitespace characters, unless the first character is ” or {`. Bare tokens carry identifiers, keywords, numbers, and unquoted values.
quoted string
begins with ” and ends at the next unescaped “. A quoted string is one token even when it contains whitespace or newlines. The token's value is the text between the quotes with escapes resolved (8.4).
block
begins with { at a token boundary and ends at its balancing }, nesting to any depth. Blocks are quote-aware: a quoted string inside a block is scanned as a string, so a } or { inside a string does not change the nesting depth. A block token's value is the text between the outer braces. A { that does not begin a token is an ordinary character (see 8.6 for the consequence).

8.4. Escape sequences

Inside a quoted string, a backslash followed by any character denotes that character. The canonical emitter produces exactly two escapes: \" for a quotation mark and \\ for a backslash. A parser MUST resolve \ + c to c for every character c ; a serializer MUST NOT emit any other escape ( 6.5 ), because no other escape round-trips to a distinct meaning.

8.5. Value forms

A facet value is written in one of four forms:

  1. a bare value: one bare token ( C3, 2.2, design-fixed);
  2. a quoted value: one string token ( ” Holds class C6 over the rated range. “ ); a quoted numeric text stays text ( ” 2.1 “ is not the number 2.1);
  3. an embedded expression ( embedded-expressions);
  4. a balanced bracket list ( bracket-lists).

Certain constructs coerce an unquoted numeric value to a number at parse (table cells, instance values); a quoted numeric spelling never coerces. The time value types date , datetime , duration , and period are texts in the ISO 8601 formats specified in 13.6 .

8.6. Embedded expressions

An expression payload is written with the ocl{ ... } marker:

expression "ocl{abs(c_m * t_f / delta_t)}"
conditions ocl{self.temperature >= rated.t_min}

The marker attaches to a brace group that is not at a token boundary, so the tokenizer splits the payload at interior whitespace. A parser recognizes a token starting with ocl{ and re-accumulates tokens until the braces balance; the accumulated text, single-spaced, is the expression. Both the quoted spelling and the inline spelling parse to the same value. The expression language inside the payload is not specified in this document; it is bound to a dialect by the consuming program, and the payload is opaque text to a conforming parser.

8.7. Balanced bracket lists

Certain facets (applicability entries, value enumerations) accept a list spelled with square brackets, [A, B, C] . Brackets are not lexical delimiters; a reader accumulates tokens from a token starting with [ until the accumulated text contains ] . The comma is an ordinary character of the list text.

9. Model structure

9.1. Top-level shape

A model text is a sequence of declarations . A declaration is either a special declaration ( 9.2 ) or a *construct declaration*:

Model          ::= Declaration*
Declaration    ::= SpecialDecl | ConstructDecl
ConstructDecl  ::= KEYWORD IDENTIFIER Block
SpecialDecl    ::= RootDecl | VersionDecl | MetadataDecl | PackageDecl
RootDecl       ::= 'root' BareToken
VersionDecl    ::= 'version' STRING
MetadataDecl   ::= 'metadata' Block
PackageDecl    ::= 'package' Block

Every construct declaration names a keyword registered in Annex A , an identifier, and one block payload. The keywords, their aliases, and the collections they populate are stated normatively in Annex A ; clauses 10 to 15 and clause 19 specify the facet grammar of each construct family.

Declaration order is free. Conventionally a file opens with root , version , and metadata , and a package's manifest is parsed first ( 16.3 ); neither convention is a conformance requirement.

9.2. Special declarations

root <id>
declares the top-level model identity. At most one is meaningful; a later root replaces the earlier.
version "<text>"
declares the model version. The declaration is accepted and not recorded in the model; it carries no conformance force. Packages carry their version in the manifest (9.3).
metadata { ... }
the singleton metadata block. Facets: title, shortname, author, schema, namespace, edition, each a single value.
package { ... }
the package manifest, a singleton block specified in 9.3.

9.3. The package manifest

The manifest is the identity and composition head of a package. Its facets:

Facet Arity Meaning
id 1 The package identifier.
title 0..1 The display title.
version 0..1 The package version text.
editions { ... } 0..1 The edition register, newest first.
baseUrn 0..1 The base URN of the package's document namespace (the `base_urn` spelling is accepted; C85 checks the shape).
extends 0..1 Deprecated single import; read as a one-element uses .
uses { ... } 0..1 Imported packages; an entry is a bare package id or a pinned abstract import `<id>@<edition>` (<<composition-rules,7.6>>). A malformed pin is a parse error.
kind 0..1 One of the five package kinds (<<package-kinds,7.3>>); any other token is a parse error.
manufacturer , product 0..1 each The supply-chain identity of a product reference package (C81).
maps_to { ... } 0..1 The standards-reference packages this model maps to (product reference and certification program packages).
scheme_type 0..1 The ISO/IEC 17067 self-classification of a certification program (the `schemeType` spelling is accepted; C98 reads it).
provides { ... } , requires { ... } , waives { ... } 0..1 each Capability tokens for composition matching (C30, C31).
status 0..1 The edition lifecycle status: `current`, `preview`, `superseded`, or `withdrawn`; any other token is a parse error (C77).
supersedes { ... } , replaces { ... } , superseded_by { ... } 0..1 each The edition lineage URNs (C79; coherence across the closure: C113, <<edition-lineage,9.3.1>>).
validity { from ... to ... } 0..1 The validity window of the edition (C78).
default_spelling 0..1 The ISO 24229 spelling code every inline prose string is authored in (<<multilinguality,clause 15>>; the `defaultSpelling` spelling is accepted).
spellings { ... } 0..1 The declared spelling-coverage set.
description 0..1 Free text.
source { collection ... parts { ... } } 0..1 The source-extract register of the package (the `.prd` payload reference, <<coverage-audits,17.5>>).
ref 0..* Typed relations of the package itself (<<references-and-relations,clause 14>>).

Unknown manifest facets are skipped under the forward-compatibility rule ( 9.5 ).

9.3.1. The edition lineage graph

A package's editions form a declared succession graph, all of it manifest data: the editions register (newest first — the chain), the status and validity window of the packaged edition, and the lineage edges. supersedes and replaces point backward (this edition over those URNs); superseded_by points forward (those URNs over this edition) and exists so a consumer meets the successor edge on the SUPERSEDED package itself — temporal steering and superseded-model demotion read the graph without chasing the successor's manifest. The lineage graph is the union of the declared edges of both directions; replaces is the stronger relation (this edition takes the target's place).

The checker verifies coherence across the composed closure (C113, edition-lineage-coherent ): when two composed packages declare opposite edges between the same pair, the declarations must agree — package A declaring supersedes { B } while B declares superseded_by { C } without A is an error. A missing forward edge is NOT an error: the backward edges remain the authored minimum, and the graph derives without them. The consumption contract, stated informatively: the serializer projects the graph beside the edition semantics of the retrieval export (the publication edition versus the package model_version , primmel-ts#65 ask 2), so "answer from the current edition" and "demote the superseded model" are reads of declared data.

9.4. Identifiers and duplicate detection

An identifier is a bare token. Identifiers are unique within their construct collection across the whole merged input: when a package's files are parsed as one token stream ( 16.3 ), a duplicate identifier in one collection is detected at parse time, wherever it appears. The parse records the issue; the model keeps the later declaration.

9.5. Parse modes and forward compatibility

Two parse modes exist:

lenient (default)
an unrecognized top-level keyword is skipped; an unrecognized facet inside a known construct is skipped together with its value token. The structural exceptions are the multi-token facets: the ref construct, whose skip consumes its predicate, its target, and its optional note block (14.2); the corresponds facet, whose skip consumes its scheme, its concept, and its optional projection block (19.4); the trust_ref form, whose skip consumes the organization identifier and an optional key pair (19.3); and a spelling-tagged alias-family facet of a term, whose skip consumes its spelling code and its list block (13.10.1). In each case a one-token skip would desynchronize the facet walk.
strict
an unrecognized top-level keyword is an error naming the keyword and its source position.

The lenient mode is the forward-compatibility contract: a document written against a later language revision, using constructs or facets this document does not register, parses under an older conforming parser with the unknown material absent and the known material intact. Authors of documents meant for one language version SHOULD validate in strict mode, so that a misspelt keyword is caught rather than skipped.

10. The subject model

10.1. The five entity kinds

The primary tier is the subject chain : five entity kinds that together state what a conforming subject is, has, and does. All five are registered constructs of the language ( Annex A ), and all five are specified here.

10.1.1. instrument: the subject type

One instrument declaration defines the subject type of a Recommendation: its variants, its classification dimensions, its family criteria, and its model groups.

instrument LoadCell {
  extends MeasuringInstrumentModel
  definition "Device that converts a load into an electrical signal."
  variant AnaloguePassiveLoadCell { definition "..." }
  dimension accuracy_class {
    label "Accuracy class"
    scope group
    description "Classification by maximum permissible error."
    values {
      A { description "..." payload { n_lc_limits: "..." } }
      B
      C
      D
    }
  }
  family_criteria { "same material" "same design" }
  family_defaults { dimensions { construction technology } parameters { rated_output } }
  model_group {
    definition "..."
    identical_characteristics { metrological_class n_lc }
    identical_attributes { accuracy_class n_lc }
  }
}

The facets are: extends (parent subject type), definition , variant <id> { ... } (repeatable), dimension <id> { ... } (repeatable; facets label , scope , description , cardinality , label_separator , reference , and values { ... } whose values may carry label , description , a payload { ... } map, implies subsumption links, and term_ref — the dimension grammar is stated once with the top-level form, 10.6 ), family_criteria { ... } , `family_defaults { dimensions { ... } parameters { ... } } , and model_group { ... }` with identical_characteristics and identical_attributes . Typed relations ( ref , clause 14 ) are accepted.

A dimension value's implies declares category subsumption inside one dimension; the applicability engine walks the implication closure, and the graph must be acyclic. A dimension's cardinality is single (default) or set (the subject holds a value set; label_separator joins the rendered labels).

10.1.2. attribute_definition: the schema of a property

attribute_definition e_max {
  symbol "E_max"
  name "Maximum capacity"
  definition "Largest load the instrument is designed to measure."
  quantity_kind mass
  unit kg
  value_type QuantityValue
  origin design-fixed
  scope model
  category metrological
  is_dimension false
  enum humidity_class
  irdi "0172-a/123"
  derived "ocl{...}"
  source { doc "urn:oiml:pub:r:60-1:2021" clause "3.5.5" }
}

The facets are: symbol , name , definition , quantity_kind , unit , value_type , origin , scope , category , is_dimension , enum (bind to an enumeration), irdi (the internationally registrable data identifier), derived (a derivation expression), source / ref provenance, and note . The origin , scope , and category values are program registers: the parser stores them as texts, and their closed vocabularies (for example design-fixed versus declared for the IS/HAS duality of 10.4 ) are enforced by the consuming program's linker, not by this specification.

10.1.3. capability: a composable mixin

capability digital {
  label "Digital"
  description "Digital signal processing capability."
  abstract false
  extends analogue-active
  requires strain-gauge
  has_parameters { output_signal software_identification }
  satisfies_requirements { /req/electronic/software }
  verified_by_tests { /conf/electronic-tests/software }
}

Capabilities compose: extends inherits, requires declares prerequisites, has_parameters names the parameters the capability contributes, and satisfies_requirements / verified_by_tests anchor it to the secondary tier. An abstract true capability is a contract, never applied directly.

10.1.4. behavior: a stimulus-response definition

behavior creep {
  kind temporal
  stimulus force
  response "Indication drift under constant load."
  source { doc "urn:oiml:pub:r:60-1:2021" clause "3.7.1" }
  verified_by { /conf/metrological-tests/creep-test }
}

The facets are kind , stimulus , response , source / ref provenance, and verified_by . A behavior is a process in the semantic model ( 12.1 ): the declaration names it and states its stimulus-response contract, and the process anatomy carries the executable detail.

10.1.5. condition_set: an operating envelope

condition_set load-cell-reference {
  role reference
  entries {
    temperature { value 20 unit degC tolerance 1 }
  }
}

The role facet states the condition tier. The documented tier vocabulary is reference , rated , limiting , and actual (the designed tiers are IS content; the actual tier is HAS content, 10.4 ); the parser stores the role as text and the tier discipline is program-side. Each entry of entries { ... } names a quantity with value , unit , and tolerance .

10.2. The subject construct and the IS/HAS/DOES anatomy

The subject construct is the general form of the primary tier: it organizes the aspect catalog by the three families is , has , and does . The discriminator of the families is the level of being:

Family Level Discriminator
is identity and design Change it and the model describes a different subject; it exists without any test being run.
has observation and instance It varies across units and time without changing identity; it can be measured, logged, or stated per instance.
does function and process It has inputs, steps, and outputs; it can be executed or simulated.
subject LoadCell {
  extends MeasuringInstrumentModel
  is {
    metadata { name "Load cell" }
    provenance { manufacturer "ACME" }
    design_parameters { e_max : mass by design }
    designed_conditions { reference ref-conds rated rated-conds }
    promises {
      c6-envelope {
        target accuracy-class
        level symbolic C6
        conditions ocl{self.temperature >= rated.t_min}
        statement "Holds accuracy class C6 across the rated range."
        verified_by { /req/metrological/mpe /conf/metrological-tests/mpe-test }
      }
    }
    endpoint lc500_api {
      operation get_indication {
        kind query
        serves indication
        payload { quantity_kind mass unit kg timestamp true }
      }
      access { public { get_indication } }
      profile rest_json
    }
  }
  has {
    attributes { d_min : mass test_dependent }
    dimensions { accuracy_class in {A,B,C,D} }
    state OperationalStates
    characteristics { creep }
    environmental_context { "logged 23.4 degC during run 7" }
    artifact_instances { evf-001 }
    serve indication via get_indication { fresh_within 5s }
  }
  does {
    behavior measure
    behavior creep
  }
}

The IS family carries eight aspect kinds: metadata , provenance , structure , design_parameters , designed_conditions , promises , artifacts , and endpoint (the declared API surface of a served subject, 13.11 ). The HAS family carries six aspect kinds plus the serve bindings: attributes , dimensions , state (the operational state machine, 12.6 ), characteristics , environmental_context , artifact_instances , and serve <aspect> via <operation> [{ fresh_within <duration> }] . The DOES family carries one aspect kind, recursively defined: behavior , and a behavior is a process ( 12.1 ).

A promise is a manufacturer claim on a characteristic or a behavior, possibly envelope-shaped or conditional: facets target , level , conditions , statement , verified_by , and provenance. A promise cites no regulator; the manufacturer binds itself, and evaluation verifies. A bare statement-only promise parses but is unverifiable, and the checker flags it (C43); a promise stated as one bare parameter value is an error (C44).

extends merges a subject with its parent at load resolution: maps merge by key with the child winning, lists append with parent entries first, and scalars override with the child's non-empty value. An aspect block placed under the wrong family, or an undeclared aspect kind, does not fail the parse; it is recorded and reported by the checker (C6, anatomy-family).

10.3. The instance construct

An instance declares one member of a subject chain, with its own exhibited values and its upward links:

instance smp-001 {
  of LoadCellSample
  level sample
  model mod-500-2t-c3
  definition_versions { LoadCellSample : "2021" attributes : "1.0.0" }
  has {
    attributes { e_max : 2.2 t }
    dimensions { accuracy_class : C }
    test_context { d_min : 0 kg }
  }
}

The facets are: of (the subject reference), level (the chain level), the upward chain links ( model , family , group , reference ), definition_versions { ... } (the edition pins, resolved against the manifest's edition register by C80), and has { ... } with attributes , dimensions , and test_context value maps. A value entry is key : value [unit] , or the quantity block form carrying the full value contract:

e_max : { value 2.2 unit t kind mass uncertainty 0.001 tolerance 0.5 }

Instance scope discipline (which values a sample-level instance may carry) is a checker matter (C17), not a parse matter.

10.4. The IS/HAS value duality

One quantity may exist at both levels of being: a designed rating (IS, carrying a tolerance) and an exhibited reading (HAS, carrying an uncertainty). The language keeps the levels apart so that a test can say "hold the environmental context within the designed envelope" without confusing which is which. The dual construct ( 13.5 ) declares the relation explicitly; the checker verifies kind coherence between the two sides (C34).

10.5. Artifact definitions and instances

An artifact_definition declares an output a subject must produce, with its content contract and its production rule (the IS artifacts aspect references these):

artifact_definition enforcement-evidence-file {
  name "Enforcement evidence file"
  description "Electronic record the instrument produces per enforcement measurement."
  content_contract {
    fields {
      measured_speed : speed
      measurement_timestamp : datetime
      image_evidence : media optional
    }
    structure "one record per enforcement measurement; images embedded"
    media {
      image_evidence { kinds { jpeg png } role "vehicle identification" }
    }
  }
  produced_when per_measurement
  retention "approx. three months (secure storage)"
  ref derives-from "urn:oiml:pub:r:91-1:2025#clause-6.6"
}

An artifact_instance records one produced output (the HAS artifact_instances aspect references these), with of (the definition), produced_at (a datetime, 13.6 ), by (the producing instance), and content { ... } (the recorded values, in the key : value [unit] entry form of 10.3 ). The checker enforces the contract (C45), instance resolution (C46), and the separation of evidence from judgment (C47).

10.6. The dimension construct (top-level form)

An instrument's inline dimension blocks ( 10.1 ) declare the subject's classification axes. A dimension declared at the TOP LEVEL is a free-standing applicability axis: a named, typed dimension that any applicability { ... } entry ( 11.1 ) may key on, independent of one subject's classification — the axis vocabulary of a domain (an electrical supply axis, an installation-environment axis, a capability axis), declared once and shared.

dimension power-supply-axis {
  label "Power supply"
  description "Power source of the instrument."
  cardinality single
  values {
    ac-mains { label "AC mains" description "Supply from AC mains power." }
    battery { label "Battery" description "Internal battery power." }
    ac-and-battery {
      label "AC mains and battery"
      description "Dual supply."
      implies { ac-mains battery }
    }
  }
  ref derives-from "urn:oiml:pub:r:144-1:2013#clause-4.5.1"
}
dimension capabilities {
  label "Capabilities"
  description "Which declared capabilities of the program a unit carries."
  cardinality set
  values_of capabilities
}

The facet set is exactly the instrument dimension's — one grammar, two placements: label , scope , description , cardinality ( single or set , parse-enforced), label_separator , reference / source provenance, and values { ... } , whose values may carry label , description , a payload { ... } map (nested blocks allowed), implies { ... } subsumption links, and term_ref (the dimension value's terminology anchor). A top-level dimension adds one facet of its own: values_of <register> (0..1) names a program register whose members ARE the value domain — the documented register is capabilities (the merged package's capability register, 10.1 ), so an axis keyed on capabilities never restates the capability list; the register vocabulary is the program's data, stored untyped. A dimension carries either values or values_of , never both.

All three declaration paths — an instrument's inline dimension blocks, an attribute definition with is_dimension true (its bound enum supplies the value set), and the top-level dimension — populate one applicability dimension namespace of the merged package, the resolution domain of every applicability entry ( 11.1.1 ). The checker verifies the shape (C111, dimension-shape ): value identifiers are unique within a dimension, implies targets resolve inside their own dimension and the implication graph is acyclic, values and values_of never combine, and no dimension identifier is declared twice in one namespace.

The consumption contract, stated informatively: applicability entries are the model's filter axes. Because every axis is a typed, declared dimension, a serializer projects an entry into flat retrieval facets directly — dimension identifier to value identifiers — and self-querying retrieval becomes native: "what applies to a class C instrument on battery?" is a filter on two declared dimensions, not a parse of a content blob (the retrieval-facet export is the toolchain's surface, primmel-ts#65 ask 3).

11. Requirements and conformance

11.1. Requirement classes and requirements

A requirement_class groups the requirements of one domain area; its identifier convention is a path ( /req/metrological ), and its facets are name , subject , guidance , and provenance.

A requirement is a constraint over the aspect paths of a subject:

requirement /req/metrological/mpe {
  name "Maximum permissible error"
  statement "The error of indication shall not exceed the mpe."
  guidance "..."
  binds_to { model.parameters.mpe model.classification.accuracy_class }
  subjects {
    subject 1 { entity_id "dimensions.p_LC" label "Apportioning factor" }
  }
  parameters {
    param n_runs: integer { description "..." default 20 range { min 0 max 100 } }
  }
  limit {
    expression "ocl{...}"
    uses { mpe accuracy_class p_lc }
    modality should
    relative_to reference_speed
    notes "..."
    accepts { verdict mdlo_normalized op lte limit "ocl{p_lc}" }
    acceptance { rule guarded guard_band { kind NSFa value 0.5 } }
  }
  applicability { accuracy_class: [A, B, C, D] }
  channel measurand_components
  obligation should
  acceptance_criteria { threshold { operator "<=" value "ocl{...}" } }
  verification { method testing }
  dependencies { /req/other }
  ref derives-from "urn:oiml:pub:r:60-1:2021#clause-5.3.2"
}

The principal facets:

Facet Meaning
statement The normative sentence (the text the committee owns).
binds_to { ... } The aspect paths the requirement constrains. The anchoring rule (<<tier-system,7.2>>): a requirement binds subject facts, it never restates them.
subjects { subject <n> { ... } } The per-subject apportioning of the constraint.
parameters { param <name>: <type> { ... } } Typed parameters — the requirement's instance-parameter schema: `description`, `unit`, `default`, `range { min max }`, `enum_values`, and `bind` (the aspect path the parameter instantiates from; <<instance-parameters,11.1.3>>).
limit { ... } The formal limit: `expression` (an embedded expression, <<embedded-expressions,8.6>>), `uses` (the free variables), `modality`, `relative_to`, `notes`, `quantity { kind unit }` (the derived quantity's typing; <<limit-quantities,11.1.2>>), `accepts` (the verdict binding: `verdict`, `op`, `limit`, an optional `source_discrepancy`), and `acceptance` (<<acceptance-decisions,11.5>>).
applicability { ... } Dimension-keyed applicability entries; a value is a bare id, a quoted text, a bracket list (<<bracket-lists,8.7>>), or a parameter-mapping block. A `match` mode of `any`, `all`, or `exact` is a closed vocabulary, parse-enforced. The dimension namespace and the entry semantics: <<applicability-semantics,11.1.1>>.
channel , obligation The evidence channel and the obligation level (<<modality-vocabulary,11.6>>).
acceptance_criteria { ... } The threshold form of the decision rule.
verification { method ... } The verification method of the requirement.
dependencies { ... } Other requirements this one depends on.
source_discrepancy { ... } The named-difference record against the source text (<<discrepancy-records,14.5>>).

11.1.1. The applicability entry: namespace and match semantics

An applicability { ... } block appears on requirements, requirement classes, conformance classes, conformance tests, forms, and subform references — one grammar everywhere. Each entry is <dimension> : <value> [ match <mode> ] , and the dimension is TYPED: the key resolves into the applicability dimension namespace of the merged package — the union of the instruments' classification dimensions ( 10.1 ), the attribute definitions declared is_dimension true , and the top-level dimension declarations ( 10.6 ) — and each listed value resolves against the dimension's declared value set or its implies subsumption closure (the applicability engine walks the closure, so a subject holding ac-and-battery matches an entry listing battery ). Both resolutions are checker-enforced (C3): an applicability entry keyed on an undeclared axis, or naming a value outside the axis's domain, is an error, not a silent string.

The value forms:

  • a bare id or quoted text: one value of the dimension;
  • a bracket list [A, B, C]: the value set;
  • a mapping block { A: 5, C: 3 }: applicability PLUS per-value parameter resolution — the entry applies where the dimension holds a listed value, and the map carries that value's parameter assignments (the per-classification parameterization the instances facet of a test states explicitly, conformance-test-construct).

The match mode refines matching on set-cardinality dimensions: any (the default; existential — one selected value listed suffices), all (universal — every selected value must be listed), and exact (mutual coverage — the selection equals the listed set, without implies expansion). On a single-cardinality dimension the selection holds at most one value, so all and exact reduce to any ; the checker warns (C3) so the author either sets the dimension's cardinality or drops the mode.

11.1.2. The limit's quantity typing

A machine limit is a quantity, and its typing is data: quantity { kind unit } on the limit block declares the derived quantity's kind and unit — the same contract the verdict construct carries ( 11.3 ) and the calculation signature declares ( 13.7.1 ). When the limit binds a verdict ( accepts ), the two declarations must agree: the checker verifies that the limit's kind and unit match the verdict's declared quantity (C114, limit-quantity-coherence ), so a limit and its acceptance chain can never drift apart in units. A limit with no quantity block remains conforming — the facet is the migration path, and a quantitative limit SHOULD declare it: the block is what makes "limits expressed in mV/V" a unit lookup instead of a string search, and what lets an execution environment normalize the compared quantities (quantity-kind coherence, never string matching, 13.4 ).

11.1.3. The instance-parameter schema

A requirement's parameters { ... } block is the explicit schema for instantiating its check: which parameters a run of the check takes, their types, their units and admissible ranges, and — with bind — WHERE each value comes from:

requirement /req/metrological/mpe {
  name "Maximum permissible error"
  statement "The error of indication shall not exceed the mpe."
  binds_to { model.parameters.mpe model.classification.accuracy_class }
  parameters {
    param n_runs: integer {
      description "Repetitions per test point."
      default 20
      range { min 1 max 100 }
    }
    param e_max: number {
      description "Maximum capacity of the instrument under test."
      unit kg
      bind "model.parameters.e_max"
      range { min 0 }
    }
  }
  limit {
    expression "ocl{...}"
    uses { mpe accuracy_class }
    quantity { kind mass unit kg }
    accepts { verdict mpe_error op lte limit "ocl{...}" }
  }
  applicability { accuracy_class: [A, B, C, D] }
  ref derives-from "urn:oiml:pub:r:60-1:2021#clause-5.3.2"
}

A bind names the aspect path that supplies the parameter's value — into the subject's IS catalog ( model.parameters.e_max ), its classification ( dimensions.accuracy_class ), or the run context — the same path discipline as the form field's bind ( 11.8 ) and the requirement's own binds_to . The instantiation order is fixed and deterministic:

  1. a bind path resolves against the instance under test (its exhibited values, instance-construct);
  2. a parameter with no bind falls back to its default;
  3. a parameter with neither is caller-supplied — derivable from the schema alone, so an execution-side system knows exactly which inputs to ask for.

This is the binding contract an execution environment (the primmel/sst runtime is the estate's) needs to bind a user's instrument instance to the model deterministically — "for THIS instrument instance, does it pass?" becomes a deterministic read, never a guess: the schema says which parameters instantiate the check, bind says their provenance, unit / range say their domain. The checker verifies the schema (C118, requirement-parameter-shape ): a bind path resolves against the subject's aspect catalog, a unit resolves against the merged quantity register (reported as a warning by the reference toolchain during the v3.2 rollout — the estate declares units no register carries yet; the leg tightens to an error when the registers catch up, TODO.primmel/11d), and a range's min never exceeds its max .

11.2. Conformance classes and conformance tests

A conformance_class scopes a family of tests to a requirement class:

conformance_class /conf/metrological-tests {
  title "Metrological tests"
  target /req/metrological
  subject "LoadCell"
  applicability { accuracy_class: [A, B, C, D] }
  guidance "..."
}

A conformance_test is an operation on the subject:

conformance_test /conf/metrological-tests/mpe-test {
  type Testing
  targets { /req/metrological/mpe }
  test_subject { kind model }
  variables {
    variable applied_load { type mass unit kg source measured
      provenance { channel reference_instrument ref eq-013 observed_at t_log } }
    variable e_l { type mass unit kg source derived
      derivation "ocl{indication - applied_load}" }
  }
  observables { observable indication_series { quantity_kind mass unit kg } }
  preconditions {
    precondition warmed-up {
      check "ocl{self.state = #ready}"
      description "An unwarmed run is invalid, never a fail."
      on_violation invalid
    }
  }
  design {
    selection { ... }
    test_points { count 5 }
  }
  instances {
    by accuracy_class
    values { A { n_runs: 5 } C { n_runs: 3 } }
  }
  acceptance_criteria { criterion mpe-check { expression "ocl{...}" } }
  pass_if "ocl{...}"
  acceptance { rule guarded guard_band { kind NSFa value 0.5 } }
  result_forms { mpe-report }
  produces_artifacts { evidence-file }
}

The principal facet groups: type (the test kind), targets (the requirements under test), test_subject , variables (typed test variables; the source vocabulary is declared , measured , derived , computed , lookup , and a measured reference variable carries a provenance probe channel, 11.4 ), observables , preconditions (a gate with check , description , and on_violation ), design (the test design: selection rules, counts, phases, specimens, test points, and the validation facets), instances (per-classification parameter values), acceptance_criteria and pass_if (the decision), acceptance ( 11.5 ), and the output anchors ( result_forms , produces_artifacts , report_rows , reference_materials , required_competence ). inherits_from reuses another test's definition; dependencies orders.

11.3. Verdicts

A verdict is a named, reusable derivation of a judged quantity: derive once, reference everywhere.

verdict drift_error {
  quantity { kind volume-fraction unit "ppm" }
  derive "ocl{indication - cgm_certified_value}"
  inputs { indication cgm_certified_value }
  series_reduction max_abs_over_window
  ref derives-from "urn:oiml:pub:r:144-2:2013#clause-4.8"
}

The facets are symbol , behavior (the behavior the quantity derives from), quantity { kind unit } , derive (the expression), inputs , series_reduction (a closed vocabulary, parse-enforced: none , max , mean , worst_case , max_abs_over_window ), acceptance ( 11.5 ), and provenance. Requirement limits, conformance tests, and form fields reference the verdict identifier instead of restating the derivation.

A verdict's inputs name the free identifiers of its derivation, and an input may name another VERDICT: the acceptance chain is then an explicit graph, not an implicit one — requirement → limit.accepts.verdict → the verdict's inputs (symbols and sub-verdicts) → each verdict's acceptance — and every hop is a declared, citable model element with its own identifier and clause provenance. The checker keeps the graph honest (C116, verdict-inputs-resolve : every input resolves to a declared symbol, a test variable or observable, or another verdict; C117, verdict-chain-acyclic : the verdict graph never cycles). A consumption system can therefore cite "the acceptance chain" precisely — the decision structure is retrievable data — and traverse it: from a verdict to the quantities it derives from, the tests that produce them, and the requirements that bind it.

11.4. Probe channels

A measured test variable may declare the physical channel its reference reading arrives by. The channel vocabulary is closed and checker-enforced (C99): reference_instrument (a traceable reference, cited by equipment register id), observer_attestation (a verification officer reads the physical display into the evidence form), and sim_ground_truth (the acceptance environment only, never a production channel). An observer_attestation channel MUST carry its declared traceability limitation text (C99).

11.5. Acceptance decisions

An acceptance { ... } block states how a limit comparison decides conformity. It attaches to requirement limits, conformance tests, and verdicts. Its vocabularies are closed and parse-enforced:

  • rule: shared_risk or guarded;
  • guard_band { kind value }: kind is NSFa, NSFd, absolute, or ratio;
  • criterion: I/MPE, D/NSFa, D/NSFd, or n/a;
  • uncertainty { max_ratio_to_mpe ... } and statistics { method on_basis_of permits } carry the uncertainty and sampling facets.

11.6. The modality vocabulary

Modality facets carry the RFC 2119 vocabulary, extended with CAN : SHALL , SHOULD , MAY , CAN , MUST . These tokens are model content : they state the obligation of a provision, process, field, or approval of the modelled standard, and they are not conformance keywords of this specification ( 6.1 ). The obligation facet of requirements and tests carries the same vocabulary in lower case ( shall , should , may ) as data; the value is stored as text and program linkers check it.

11.7. Constraints

A constraint declares a subject-intrinsic rule (an invariant of the subject, distinct from a requirement, which is an external obligation):

constraint e-max-bound {
  name "Applied load within capacity"
  stereotype inv
  check "ocl{self.applied_load <= self.e_max}"
  on_violation invalid
  violation_meaning "A run above capacity is not a test result."
  source { doc "urn:oiml:pub:r:60-2:2021" clause "2.3" }
}

The facets are name , stereotype (the constraint kind; inv is the intrinsic-rule stereotype), check (the embedded expression), on_violation , violation_meaning , and provenance. The constraint shape is checker-enforced (C84).

11.8. Forms and subforms

A form is the evidence view of the secondary tier: a declarative data-capture schema that projects the subject graph into a record.

form mpe-report {
  name "MPE test report"
  data_class MpeReport#data
  conformance_process creep_test
  section "E"
  field e_max: number {
    label "Maximum capacity"
    bind "model.parameters.e_max"
    unit kg
    required true
  }
  field runs {
    label "Runs"
    subform_ref run_row { parameters { n: n_runs } }
    min_items 3
  }
  pass_if "ocl{...}"
  calculation_context { mpe_eval }
  applicability { accuracy_class: [A, B, C, D] }
}

The principal facets: name , data_class (the record the form fills), conformance_process , header (a shared header form), section , scope , dimensions , requirements { ... } (the requirements the form serves), references , applicability , instances , constraints , criteria , tables , calculation_context { ... } (the calculations in scope), pass_if / pass_fail / derivation (the form-level judgment derivations), and report_rows { ... } . A field <name>[: <type>] { ... } entry carries label , definition , bind (the aspect path the field records), unit , symbol , verdict (the verdict the field derives, 11.3 ), targets , dimension , enum , pattern , required or required_when (a conditional-requiredness expression), default , values / enum_values , evaluation , calculation_bindings , measurement_method , items / fields (nested structure), min_items / max_items , subform_ref , and provenance.

A subform declares a reusable parameterized row shape: parameters { ... } , type , default , mapping , and the same field entries. The field grammar of forms and subforms is one grammar, shared.

11.9. Provisions

The provision is the v2 compliance primitive, retained: a statement of requirement with RFC 2119 modality, traceable to source clauses.

provision MoistureRecorded {
  condition "Every roast batch shall have its moisture content recorded"
  modality SHALL
  reference {
    BFS-4-2
  }
}

The facets are condition (the requirement text), modality ( 11.6 ), and reference { ... } (the cited reference constructs, 14.3 ). Processes bind provisions with validate_provision { ... } ( 12.1 ). In a v3 model the requirement construct ( 11.1 ) is the binding form: a provision states text and modality, while a requirement additionally binds subject aspect paths, carries a formal limit, and joins the coverage calculus. New modelling SHOULD author requirements; provisions remain for the v2 surface and for standards whose provisions bind nothing.

12. Processes

12.1. The process as a recursive subject

A process is a subject ( 10.2 ): it IS its signature and provenance, it HAS state and registers, it DOES its steps, and the recursion bottoms out in atomic steps. Two definition forms exist: the abstract form (a signature with no body; always valid) and the executable form (a does body of steps and flow).

process creep_test {
  name "Creep test method"
  actor lab
  modality SHALL
  signature {
    in  { applied_load : mass duration : time }
    out { indication_series : mass_series }
  }
  invariants { "ocl{self.applied_load <= self.e_max}" }
  preconditions {
    precondition warmed-up {
      check "ocl{self.state = #ready}"
      description "An unwarmed run is invalid, never a fail."
      on_violation invalid
    }
  }
  executor lab
  registers { conditions_log : text indication_series : mass_series }
  state OperationalStates
  instances {
    by accuracy_class
    values { A { n_runs: 5 } C { n_runs: 3 } }
  }
  does {
    start_event s
    action stabilize  { executor actor role lab_technician capture stab_form write { conditions_log } }
    action apply_load { executor actor role lab_technician read { applied_load } }
    action hold       { executor machine wait duration }
    action record     { executor machine read { applied_load } write { indication_series } }
    end_event e
    flow {
      s -> stabilize -> apply_load -> hold -> record -> e
    }
  }
}

The v2 process facets remain: name , actor , modality , reference_data_registry { ... } (registries read), output { ... } (registries written), validate_provision { ... } , validate_measurement { ... } , subprocess (the canvas page), links , note , table , and figure . The v3 facets add: signature { in { ... } out { ... } } , invariants { ... } , preconditions { ... } , executor , registers { ... } , state (the bound operational state machine), `instances { by <dimension> values { ... } } , does { ... } , child_composition ( all` or gateway , parse-enforced; how children combine for the coverage calculus), activity_kind { ... } ( 12.5 ), and segregation { constraint <id> { ... } } ( 12.5 ).

12.2. Steps and flow

A does body holds step declarations and a flow { ... } block. The step kinds are action , approval , gateway , parallel_gateway , start_event , end_event , timer_event , and signal_event . A step may carry:

  • executor machine or executor actor (a closed vocabulary, parse-enforced), with role <id> for actor steps;
  • read { ... } / write { ... } register access, capture <form>, wait <duration>;
  • fires <transition>: on completion the step takes the named transition of the process's bound state machine (C37, C40);
  • calls <process> { with { in { ... } out { ... } } }: the step invokes a sub-process, binding every callee input to a caller register and every callee output back to a caller register; resolution, completeness, and kind compatibility are checker-enforced (C76), as are input/output type coherence and flow coverage (C74, C75).

Flow edges chain: a -> b -> c . An optional { when "ocl{...}" } body conditions the last hop of a chain; at a gateway the first matching edge in document order wins, and the unconditioned edge is the default.

12.3. Events and gateways

The event constructs are start_event (alias start ), end_event (alias end ), timer_event (alias timer ), and signal_catch_event (alias signalcatch ). A timer event carries type and para ; the documented timer-type vocabulary is WAIT , REPEAT , and DURATION (MN 113), and the checker validates recurrence presence and period format (C14, C15). A signal catch event carries catch . An exclusive_gateway carries label . The process-integrity rules are checker-enforced: one start per process (C10), a terminal end (C11), flow names that resolve (C12), a realized signature (C13), and unique step ids (C16).

12.4. Canvases

A canvas (alias subprocess , the v2 spelling) is a diagram container: a page of process components and edges with layout. A process names its page with the subprocess facet. The canvas is the visual projection of the process graph; the graph is the model.

12.5. Activity archetypes and segregation

An activity_archetype register classifies processes against an activity taxonomy (facets label , definition , parent , clause ), and a process declares its kinds with activity_kind { ... } ; multi-kind is deliberate, classification not inheritance, and the identifiers resolve against the register (C58). A segregation { constraint <id> { ... } } block declares role-segregation constraints as first-class structure (the constraint kinds, for example case_personnel_disjoint , are the program's vocabulary); membership resolution is checker-enforced (C59).

12.6. State machines

A state_machine declares the lifecycle of an entity or the operational states of a subject:

state_machine Application {
  kind lifecycle
  initial DRAFT
  states { DRAFT SUBMITTED UNDER_REVIEW ACCEPTED REJECTED }
  transition submit {
    from { DRAFT }
    to SUBMITTED
    guard "ocl{self.is_complete}"
    cascade {
      set status "under_review"
      create ReviewTask { with { application : self.id } }
      notify ia_officer via email
    }
  }
}

The kind facet is a closed vocabulary, parse-enforced: lifecycle (workflow entities; the default) or operational (a subject's HAS state). The two families are kept separate (C38). A transition carries from , to , action , an optional guard expression, and an optional cascade of side effects: set , where , create , via , and the semantic cascade actions lock , submit , notify , record (a closed vocabulary, parse-enforced). Cascade transition resolution is checker-enforced (C95), and every referenced state must exist (C39).

12.7. Roles and approvals

A role declares an organizational actor ( name , plus optional label and description ). An approval binds a workflow step to an approver role and an approval record registry: facets name , actor , modality , approve_by , approval_record { ... } , and reference { ... } .

12.8. Map profiles

A map_profile declares an in-model mapping to a target namespace:

map_profile OCS {
  description "Mappings into the reference model"
  mapping {
    SourceBeans -> OCS#SourceBeans
    TestBatchMoisture -> OCS#VerifyQuality {
      description "Batch moisture verification fulfils the quality clause."
      justification "The lab runs the moisture test per batch."
      coverage full
    }
  }
  coverage { OCS#SourceBeans full }
}

The facets are description , a mapping { ... } block of source -> target pairs (spaces around the arrow are optional) with an optional per-pair metadata block ( description , justification , coverage ), and a profile-level coverage { ... } block of assertions about reference components. The standalone .prm serialization ( 16.4 ) is equivalent; the checker verifies that mappings resolve (C21), run implementation-to-reference (C22), and agree with the computed coverage calculus (C23), and that a mapping without a description is documented at audit level (C25). A view_profile (alias view ) declares a read-only projection of a model for a set of roles (facets against , roles , visible , description ); views never write (C26).

13. Data, quantities, and time

13.1. Data classes, enumerations, and registries

A class declares a record shape; the #data suffix marks a class as registerable:

class BeanLot#data {
  lot_id:           string   { definition "Supplier lot identifier" modality SHALL }
  received_on:      datetime { definition "Date received" modality SHALL }
  certified_organic: boolean { definition "Certified organic" modality MAY }
}
class RoastProfile {
  target_level: RoastLevel { definition "Target roast level" }
}

A class without #data is a helper class: a reusable complex type that lives as a field inside other classes and cannot be registered. A field declaration is <name>[: <type>][<cardinality>] { <properties> } ; field properties are definition and modality ( 11.6 ) at minimum. The cardinality form is [<min>..<max>] with * for unbounded. An enum declares a closed value set, each value with an optional definition block. A data_registry declares a named store of records of one data class (facets title , data_class ); processes read and write registries through their reference_data_registry and output facets ( 12.1 ).

13.2. Field types

The primitive field types are string , boolean , datetime , and role . A field type may also name an enumeration (one of its values), a helper class (an embedded complex value), or a typed reference reference(<Target>#data) (a foreign key to another registry's record). A field with no type is free-form.

13.3. Measurements

A measurement (legacy alias variable ) declares a value that validation expressions and gateway conditions evaluate:

measurement MoistureReading {
  type DATALIST
  description "Per-batch moisture readings in percent by mass"
}
measurement MaxMoisture {
  type DERIVED
  definition "[MoistureReading].max"
}

The documented measurement-type vocabulary (MN 113) is NUMERIC , DATALIST , DERIVED , TEXT , TABLE_REFERENCE , TRUE/FALSE , and DURATION ; the type is stored as text, and the derivation definition of a DERIVED measurement is the consumer's expression dialect.

13.4. Quantities and units

A quantity_register upgrades bare unit strings into typed register entries: quantity kinds with their dimension vectors and SI coherent units, and units with their symbols, kinds, and SI conversions.

quantity_register si {
  kind mass { dimensions { M 1 } si_unit kg description "Mass." }
  unit kg { symbol "kg" label "kilogram" kind mass factor 1 }
  unit g { symbol "g" label "gram" kind mass factor 0.001 }
  unit degC { symbol "°C" label "degree Celsius" kind temperature factor 1 offset 273.15 }
}

A kind carries dimensions { <base> <exponent> ... } and si_unit ; a unit carries symbol , label , kind , the multiplicative factor , and the affine offset . Comparison coherence is judged on kinds, not unit strings (C33), and a package extends a register without redefining its entries (C32, C33). A series declaration on a symbol or variable states the axes and cell shape of a reading series.

A quantity register is self-contained: its kinds, units, factors, and offsets are Primmel's own content. Where a document-side toolchain carries a unit vocabulary (UnitsML is the document format's register), the binding rides the correspondence annotation of 19.4 — a unit or kind entry declares corresponds unitsml "<unit-identifier>" , a maps-to claim that steers the document projections and never imports the register's semantics ( 19.5 ).

13.5. The value duality: dual

A dual declares the IS/HAS duality ( 10.4 ) of one attribute explicitly:

dual e-max-rating {
  attribute e_max
  designed  { value 2.2 unit t tolerance 0.5 }
  exhibited { value 2.1998 unit t uncertainty 0.0002 }
}

The designed side is the IS role (carrying tolerance ); the exhibited side is the HAS role (carrying uncertainty ). At least one side is present; when both are stated they must be kind-coherent (C34).

13.6. Time primitives

The time value types are texts in ISO 8601 formats, validated with real calendar arithmetic (C35):

  • date: a calendar date, 2026-08-20, with leap-year validation;
  • datetime: a date with a time of day, 2026-09-15T10:14:00Z, with an optional UTC offset;
  • duration: a duration designator, P2Y, PT1H30M, with the designators in order and optional decimal fractions;
  • period: a recurrence period, validated as a duration.

13.7. Tables, symbols, and calculations

A table declares structured tabular data: typed columns { ... } with optional units, data { ... } rows (numeric-looking cells coerce to numbers), profiles { ... } (named row selections), dimension binding, overrides , and binding . A symbol declares a typed, unit-aware formal symbol: type is a closed vocabulary, parse-enforced ( number , integer , string , boolean , enum , collection , array ), with unit , latex , values , series , kind , quantity_kind , origin , and the linking facets attribute , calculation , and profile .

A calculation declares a named computation: inputs { ... } , output { ... } , and one of the calculation forms, expression (an embedded expression), lookup (a table lookup with key ), or profile (a profile reference), with params { param <name>: <type> { ... } } , variable bindings, and provenance.

13.7.1. The calculation signature: typed inputs and outputs

A retrieved calculation is executable exactly when its signature is data: what goes in, in which units, over which ranges, and what comes out. The signature declares it:

calculation mpe_absolute {
  inputs {
    e_l : number { unit kg quantity_kind mass range { min 0 }
      description "Error of indication at the test load." }
    n_lc : integer { range { min 1 max 10000 }
      description "Number of load cell intervals." }
  }
  output : number { unit kg quantity_kind mass range { min 0 }
    name "mpe" description "Maximum permissible error, absolute." }
  expression "ocl{...}"
  ref derives-from "urn:oiml:pub:r:60-1:2021#clause-5.1"
}

An input entry keeps the v2 <name> : <type> { ... } form; the block carries unit and quantity_kind (the typed-unit pair, resolved against the merged quantity register of 13.4 ), `range { min max }` (the admissible domain; either bound may be absent), description , default , and enum_values for enum-typed inputs. The output : <type> { ... } carries the same typing facets with name and description . The checker verifies the signature (C115, calculation-signature-shape ): every unit resolves to a registered unit and every quantity_kind to a registered kind of the merged quantity register (unit resolution is reported as a warning by the reference toolchain during the v3.2 rollout — the estate declares units no register carries yet; the leg tightens to an error when the registers catch up, TODO.primmel/11d), the two agree in kind, a range's min never exceeds its max , and an enum-typed input declares its values. Unit-aware retrieval ("limits expressed in mV/V") and the trustworthy execution of a retrieved calculation both read this signature; they never parse the expression text for units.

13.8. Test point sets and reference materials

A test_point_set declares the points at which a test samples its range: anchor , points (or min_points ), fraction , offset , repetitions_per_point , rule , cardinality , and provenance. A reference_material declares a certified material used by a test: identity_fields , constraints , evidence , override , kind , type , unit , and required facets, with the parse-enforced gate facets ( on_violation , rule ) where the grammar defines them.

13.9. Registry and assurance constructs

Four documentation constructs round out the registry: note (a typed annotation; type is a closed vocabulary, parse-enforced: NOTE , CAUTION , WARNING , EXAMPLE , COMMENTARY ; facets message , reference , and source_discrepancy ), link (a cross-model or external pointer; kind is REPO or URL , parse-enforced, with target , url , path , namespace ), figure ( src , title ), and comment (a review comment: author , message , on , reply_to , resolved , timestamp ).

Three assurance constructs carry the architecture-level records: an invariant declares a named platform invariant with statement , severity , applies_to { ... } , and enforcement (a claims list, or the bare marker aspirational ; the grammar and the marker discipline are checker-enforced, C90, C91). A test_sequence declares a required test ordering: step entries step <n> { test <ref> role <r> depends_on <m> } with the step role ( baseline or follow_up ), sample_applicability , and provenance (shape and integrity: C92, C93). A formulas_used block, keyed by a conformance-test reference, lists the evaluation formulas a test derives (the formulas facet), with provenance (shape: C94).

13.10. Terminology

A term declares one entry of the model's terminology register:

term load-cell {
  label "load cell"
  definition "Force transducer that converts a load into an electrical signal."
  symbol "LC"
  part_of_speech noun
  aliases { "weighing cell" }
  abbreviations { "LC" }
  see_also { force-transducer }
  scope_note "Used for the R 60 sense only."
  ref derives-from "urn:oiml:pub:r:60-1:2021#clause-3.1.3"
}

The facets are label , definition , symbol (the formal symbol of the term), part_of_speech , the alias family ( aliases , colloquial , abbreviations , deprecated ; 13.10.1 ), form_type , language , note , scope_note , section , see_also { ... } , vocab_ref / vocab_term (the external vocabulary binding), overlay (a term-level overlay marker), and provenance. Terms are foundations-tier content: every tier references them and they anchor to no subject.

13.10.1. The alias family

The designation set of a term is data, and it is the bridge a consumption system (a search index, a retrieval plane, an assistant) walks from the vocabulary a user actually types to the defined term the corpus knows. Four facets carry it, each a list of strings (multiword entries quote, per the lexical rules of 8.3 ):

Facet Meaning
aliases { ... } Admitted alternate designations of the same concept: forms the standard's community accepts as naming the term (`alt` is the v2 spelling of this facet; it parses unchanged and folds into the same channel, and a conforming serializer emits the canonical `aliases` form).
abbreviations { ... } Abbreviated forms of the designation.
colloquial { ... } Informal, everyday phrasings users reach for when they do not know the defined term: "why is my scale drifting" belongs on the `durability` term. Colloquial entries are NOT designations: they carry no normative force and a renderer never presents them as the term's name. They exist so the everyday-words → defined-term bridge is authored once, in the model, instead of being hand-rolled per consumer.
deprecated { ... } Deprecated designations: recognized, never proposed.
term durability {
  label "durability"
  definition "Ability of a measuring instrument to maintain its performance characteristics over a period of use."
  aliases { "long-term stability" }
  colloquial { "drifting" "scale keeps drifting" "readings creep over months" }
  colloquial fra-Latn { "dérive" "les lectures dérivent" }
  see_also { stability }
  ref derives-from "urn:oiml:pub:r:60-1:2021#clause-3.1"
  vocab_ref viml-2022
  vocab_term "5.15"
}

An alias-family facet may carry a spelling code between the keyword and the list ( colloquial fra-Latn { ... } ), declaring the list of one alternate spelling inline; the bare form declares the package's default spelling ( clause 15 ). The spelling-tagged form is the language-level construct for language-tagged designation variants: the designation set of one spelling is one list, so a retrieval consumer routes a question to the entries authored in its language. The text block mechanism does not address alias-family lists item by item (a bare string entry has no declared key to key on); the inline tagged form is the whole of the variant mechanism. The serialization of the variant lists (per-unit language tags in a retrieval export) is the toolchain's projection (primmel-ts#65, ask 7).

The checker verifies the family's shape (C110, term-alias-shape ): duplicate entries within one list of one term are errors, and the same string appearing in two different family fields of one term violates one-entry-one-home (reported as a warning by the reference toolchain during the v3.2 rollout — the v2 alt semantics admitted the overlap and the shipped estate carries it; the leg tightens to an error when the estate's terms are re-authored, TODO.primmel/11d); an entry that merely echoes the term's label is a warning (it adds no bridge).

13.11. Twin-era constructs

Three constructs serve a live subject: a connector_profile declares a named protocol profile that endpoints reference (facets protocol , description , reference ); a monitor declares continuous compliance over a subject set, with over { ... } , triggers { ... } ( every <period> , on signal <s> , on change <aspect> ), evaluate selectors ( all , applicable_to(<expr>) , or an explicit id block), emit sinks, and escalate { ... } actions on fail and on invalid (checker rules C65–C70); a passport declares a named, access-classed projection of a product model, with upi { pattern level } (the level is model , batch , or item , C88), carrier { kind payload } , and the access-class content blocks ( public , restricted , authority ), which are fail-closed at parse: an unknown block-valued keyword is a parse error, so a misspelt access class never parses into content no class serves (C86, C87).

14. References and relations

14.1. The problem this clause solves

Every model element of a modelled standard traces to its source: the clause it interprets, the documents it cites, the other elements it implements or supersedes. Primmel v3 expresses all of these as one construct, the typed relation, with the predicate vocabulary declared as data.

14.2. The ref construct

A ref is one typed triple with the enclosing element as subject:

ref <predicate> "<target>"
ref <predicate> "<target>" { note "..." }

The target is a URI: a document anchor (a clause or annex URN, optionally with a fragment) or a model element identifier. A ref facet is accepted on every construct. The predicates are not keywords: they are data, declared in a predicate registry ( 14.4 ), and an undeclared predicate is a checker error (C103, declared-predicate), so a typo is an error, not a silent new relation.

14.3. Provenance: the canonical form and the legacy forms

The citation kinds fold into the legacy channels at parse, so consumers read one model regardless of spelling:

  • ref derives-from "<urn>#clause-<c>[/<fragment>]" is the canonical provenance form; it maps onto the element's provenance channel (its source record with doc, clause, and optional fragment);
  • ref cites "<target>" maps onto the element's citation channel;
  • the citation roles ( requirement, test-procedure, calculation, report-format, method) map onto the role-references channel of forms and fields;
  • every other predicate stays in the element's ref list as a semantic relation.

The legacy spellings keep parsing during the transition: `source { doc "..." clause "..." [fragment "..."] } , references { <role> { ... } }`, and specification_reference "..." . A conforming serializer emits the canonical ref derives-from line for a URN-anchored provenance record and keeps the legacy source block for a free-text one ( 6.5 ). The dedicated hot edges of the model ( targets , dependencies , inherits_from , extends , maps_to , structure ) are facets of their constructs, never ref triples.

The v2 reference construct remains the registry of citable external clauses:

reference BFS-4-2 {
  document "Bean Freshness Standard (fictional)"
  clause "4.2"
  title "Moisture content"
}

A reference declares one external document clause (facets document , clause , title ); provisions and other elements cite it by identifier through their reference { ... } blocks. New modelling SHOULD cite document anchors directly with ref derives-from / ref cites and SHOULD register a reference construct only where a named, reusable citation target earns its keep.

14.4. The predicate registry

A predicate construct declares one relation kind:

predicate derives-from {
  kind citation
  description "The element is the model's interpretation of the target clause."
  subject_kinds { requirement conformance_test form field calculation symbol term package }
  target_kinds { document-anchor }
  resolution must-resolve
  inverse interpreted-by
  transitive false
  symmetric false
}

The facets are kind , description , subject_kinds { ... } , target_kinds { ... } , resolution , inverse , transitive , and symmetric . A program declares its predicate vocabulary in its foundation or core package, and the registry composes through the package includes like any other content.

14.5. Discrepancy records

Modelling a published text sometimes surfaces a difference between the text and the model: a clause the model deliberately does not follow, or a defect the model annotates. Two constructs record this honestly rather than silently:

  • the source_discrepancy { ... } facet, carried by requirements, requirements' limit accepts, tables, and notes, with summary, sources { ... }, resolution, and rationale; the resolution vocabulary is closed and parse-enforced: follows_clause_x or annotated_only;
  • the discrepancy_record construct, the corpus-level form, with summary, sources { ... }, resolution, rationale, and status; the status vocabulary is closed and parse-enforced: open or resolved.

14.6. The trust reference and the correspondence annotation

Two further reference forms extend this layer without joining the predicate registry, and both are specified with the dataspace constructs of clause 19 : the trust reference ( trust_ref , 19.3 ) names a trust-plane identity by addressing only, opaque to the model and resolved by the consumer at runtime; the correspondence annotation ( corresponds , 19.4 ) declares that an element maps to an external concept and steers the expression codecs. Neither is a ref triple: a trust reference has no model-internal target, and a correspondence is maps-to, never is-defined-by.

14.7. Document anchors and the structured reference

A ref target that names a document or a clause of a document is a document anchor , and its shape is normative, because a cross-standard hop is machine-traversable exactly when the anchor composes from declared parts:

DocumentAnchor ::= DocumentURN [ '#' 'clause-' ClauseRef [ '/' Fragment ] ]
DocumentURN    ::= ? a URN naming a document (a package's baseUrn names
                     its own document namespace; C85 checks the shape) ?
ClauseRef      ::= ? the document's own clause numbering, dot-separated ?

The deployed spelling — urn:oiml:pub:r:60-1:2021#clause-5.3.2 — is the canonical form: the URN names the document, the clause- fragment names the clause in the DOCUMENT's own numbering (never a producer-internal anchor such as a UUID; an internal anchor is not citable and a consumer strips it — primmel-ts#65 ask 1 holds the serializer to the same rule). A document without a URN scheme is cited through the structured identity of a reference construct instead.

The v2 reference construct ( 14.3 ) is the registry of such named citation targets, and its facet set grows from display strings to a resolvable identity:

reference iso-iec-17025-7.2 {
  org "ISO/IEC"
  document "ISO/IEC 17025"
  edition "2017"
  clause "7.2"
  title "General requirements for the competence of testing and calibration laboratories"
  urn "urn:iso:std:iso-iec:17025:ed-3"
}

The facets: org (the publishing organization), document (the designation), edition (the publication edition), clause , title , and urn (the resolvable identifier). When urn is present it IS the document's identity and the clause anchor composes as <urn>#clause-<clause> ; when it is absent, the structured quadruple (org / document / edition / clause) is the identity a consumer's locator resolves. Either way the cross-reference carries resolvable identifiers alongside its labels, and the hop from a citing element to the cited standard's clause is data, not display text. The checker verifies the identity (C112, reference-identity ): a malformed urn is an error; a reference carrying neither urn nor the org-and-document pair is a warning (a display-string citation is visible debt, never silently citable).

The composition edge stays where it always was: a manifest's uses entry IS the resolvable identifier of a composed package (the package id through the package locator, with the pinned <id>@<edition> form for abstract imports, 7.6 ), and the reference construct serves the citations that are NOT composition — the standards a model cites without merging their content.

15. Multilinguality

15.1. Spelling codes

Primmel tags every human-readable string with a spelling system code following ISO 24229: an ISO 639-3 alpha-3 language code, an ISO 15924 script code (mandatory; a bare language code is an error), an optional ISO 3166-1 alpha-2 country code, and an optional orthography extension: eng-Latn , uzb-Arab-AF , ind-Latn-pre1972 . A conversion system code names a titular conversion between scripts, for example BGN-PCGN:zho-Hans:Latn:1979 . The code syntax is checker-enforced (C89, spelling-code-wellformed); resolution of a code against the ISO 24229 register is the consuming program's discipline against a pinned register snapshot, and the reference toolchain stays register-free.

15.2. The default spelling and the text construct

A package manifest declares default_spelling (the spelling every inline prose string is authored in; a name "..." facet IS the default spelling's value) and may declare spellings { ... } (the coverage set). Alternate spellings never touch the inline source value; they are authored in text blocks:

text /req/metrological/measuring-range-max.statement {
  spell fra-Latn "La valeur de la plus grande charge ..."
  spell zho-Latn via BGN-PCGN:zho-Hans:Latn:1979 "..."
}

A text block carries spell facets only; any other facet is a parse error. Each spell facet is spell <code> [via <system-code>] "<value>" , and the value MUST be quoted. The block identifier addresses the prose it translates:

  • <element-id>.<field> addresses a prose field of an element;
  • <element-id>.<path...>.<field> addresses nested prose: an intermediate segment names a nested structure, a list item is keyed by its declared key (a field or parameter name, a step's order, a subject's slot), never by a positional index, and the terminal segment is the prose field.

The address grammar is checker-enforced (C89): the element is the longest dot-boundary prefix registered in the package, every path segment resolves, and the terminal prose is authored. Alternate spellings of one field may be split across files; the parser merges the blocks of one address into one content set. Translation files (conventionally l10n/<code>.prl ) therefore never modify the normative source spelling.

The one exception to the text-block mechanism is the term's alias family ( 13.10.1 ): a bare string entry of an aliases , colloquial , abbreviations , or deprecated list has no declared key to address, so the family carries its spelling variants inline, one spelling-tagged list per spelling. Everywhere else, alternate spellings are authored in text blocks and never inline.

16. Serialization

16.1. File types

A Primmel model text is a UTF-8 file with the extension .prl . The extension .mmel is the legacy spelling; a conforming tool accepts both everywhere a file extension matters, and prefers .prl when both exist ( 16.2 ). A mapping file is a JSON text with the extension .prm ( 16.4 ). A package is a directory ( 16.3 ).

16.2. The include directive

A model text may be split across files with the include directive:

include "parts/load-cell"

The directive is resolved by a preprocessor before tokenization, so the parser sees one concatenated text. The rules:

  1. the path resolves relative to the directory of the including file; absolute paths are accepted;
  2. the extension may be omitted; .prl is preferred over .mmel when the omitted form matches neither literally;
  3. includes nest; a cycle is an error naming the chain;
  4. a directive inside a comment line is not expanded (the preprocessor's comment recognition matches the tokenizer's, comments);
  5. a missing include file is an error.

16.3. The package directory

A package is one directory:

oiml-r60/
  package.primmel          the manifest (the only required file)
  model/**/*.prl           the subject chain
  entities/**/*.prl        storable classes
  specification/**/*.prl   requirements, conformance, tables, symbols, calculations
  execution/**/*.prl       forms, subforms, the test report skeleton
  evaluation/**/*.prl      workflow, state machines, processes, roles
  *.prl                    root-level content (terminology, references, notes)
  .primmel-allowlist.prl   the checker allowlist (package metadata, not content)

The load order is deterministic: the manifest first, then the content files of the five convention directories, then the root-level files, each sorted by path. All files of a package are preprocessed and parsed together as one token stream, so cross-file references resolve and duplicate identifiers are detected across files ( 9.4 ). The .primmel-allowlist.prl file is package metadata for the checker ( 17.4 ) and is not content. When the manifest declares uses and a package locator is available, the whole dependency closure composes per 7.6 ; otherwise the directory loads alone.

16.4. The .prm mapping file

A .prm file is the standalone serialization of the mapping primitive ( 7.5 ): a JSON object, versioned independently of the models it links.

{
  "@context": "https://example.org/smart",
  "@type": "Primmel_MAP",
  "id": "OrgO-to-StandardS",
  "mapSet": {
    "StdS": {
      "id": "StdS",
      "mappings": {
        "OpA": {
          "StdS#Process5": {
            "description": "Batch logging fulfils the record requirement.",
            "justification": "The roaster writes the record on completion.",
            "coverage": "full"
          }
        }
      },
      "coverage": { "StdS#Process5": "full" }
    }
  }
}

The rules:

  1. the @type is Primmel_MAP (v3) or MMEL_MAP (the v2 spelling, read as an alias); any other value is an error;
  2. mapSet is keyed by the target (reference) namespace: one implementation may map to many reference models, with coverage computed per target;
  3. a mapping pair carries description, justification, and an optional coverage assertion; the coverage levels are closed and parse-enforced: full, minimal, partial, none;
  4. authored coverage assertions about reference components ride the namespace's coverage object and are checked against the computed calculus (C23);
  5. authored spellings (the @type, bare or Namespace#ElementID targets) round-trip verbatim: loading and re-serializing a .prm file is a fixed point on the normalized model;
  6. @context is emitted only when present; key order is the model's insertion order.

16.5. The canonical form and the round trip

A conforming serializer emits the canonical form: one construct per block, facets in the construct's canonical order, and values spelled by the emission rules:

  1. a value is emitted bare exactly when it contains no whitespace, brace, or quote, does not end in a colon, and does not begin with a comment opener ( # or //); every other value is quoted (the reasons: a bare trailing-colon token re-parses as a key head, and a leading comment opener re-parses as a comment, so both must never emit bare);
  2. inside quotes, ” and \ escape as \ “ and \\ ( escapes), and no other escape is produced;
  3. a URN-anchored provenance record emits as ref derives-from; a free-text one keeps the legacy source block ( provenance-forms);
  4. an admitted-alternate designation list of a term emits in the canonical aliases { ... } spelling, regardless of the authored spelling ( alt folds; term-alias-family);
  5. the dump of a parsed model, re-parsed, yields the same model, and re-serialized, yields the identical text: dump ∘ load ∘ dump is a textual fixed point.

The fixed-point property is not an aspiration: the reference implementation's test suite proves it on the shipped packages, and the round-trip gates of the reference usage fail on any drift.

17. The checker

17.1. primmel check

The checker is the semantic layer of the toolchain: the rules that a syntactically conforming package is judged against. The full rule catalog, C1 to C108 with family, default severity, and level, is normative in Annex B ; this clause specifies the checker's operating semantics.

17.2. Levels

Three invocation levels exist:

default
the normal-level rules run at their catalogued severities; errors fail, warnings print.
strict
every warning promotes to an error, except findings marked KNOWN by the package allowlist (17.4) and budget-covered coverage findings (17.5).
audit
additionally runs the audit-level rules (the coverage audits and the mapping-description rule) and enforces the per-package coverage budgets.

The levels combine: --strict --audit is the strongest discipline. The checker exits non-zero when any error-severity finding survives.

17.3. Rule families

The catalog organizes by family, and each family answers one review question:

Family The question it answers
base Do references resolve, do dimensions use declared enums, are stores unique, do declared reference targets carry resolvable identities, and is the allowlist itself honest?
anatomy Is every aspect under its correct family, and do subject extensions resolve?
process Is every process well-formed: one start, a terminal end, resolving flow, a realized signature, bound subprocess calls?
instantiation Are instance scopes, version pins, and chain links coherent and acyclic?
mapping Do mappings resolve, run the right direction, agree with the coverage calculus, and stay disjoint from imports?
composition Do package imports resolve without redefinition or cycles, do capability tokens match, and do downstream packages stay out of owned requirement namespaces?
quantities Does every quantity carry its unit contract, do duals and maps stay coherent, and do limits and calculation signatures declare their quantity kinds?
state Do fired transitions and referenced states exist, and do the lifecycle and operational families stay separate?
promises Do promises resolve, verify, and stay off bare parameter values?
artifacts Do artifact definitions carry contracts, instances resolve, and evidence stays separate from judgment?
characteristics Does every characteristic have one home, a behavior link, and declared derivation inputs, and do the verdict chains resolve and stay acyclic?
twins Do serve bindings, endpoints, monitors, and composition declarations resolve and stay fresh?
coverage Is every requirement tested, every test evidenced, every form judged, and the normative text covered?
edition Are statuses, validity windows, supersedes chains, and edition pins well-formed, and do the declared lineage edges agree across the closure?
supply-chain Do product mappings resolve, stay pinned, and keep passport access classes leak-free?
dataspace Do the dataspace's policy, compatibility, and content references resolve, are its trust anchors shaped, is its governance cited, do its policies carry well-formed rules, and do correspondence annotations stay unambiguous?

17.4. The allowlist discipline

A package carries its known debt explicitly in .primmel-allowlist.prl . An entry is KNOWN or STALE:

  • a KNOWN entry matches a current finding: the finding prints and never fails, even at the strict level;
  • a STALE entry matches nothing: it is an error (C57), because the data was fixed and the entry must die;
  • a malformed entry is an error (C56).

Two budgets cap the audit-level coverage warnings: coverage_budget for the test-evidence and form-judgment audits (C51, C52, enforced by C55) and text_coverage_budget for the uncovered-normative-sentence audit (C71, enforced by C72). A budget carries an integer and a recorded reason, and it only shrinks: a package at full coverage declares zero, and any regression fails.

17.5. The coverage audits

At the audit level the checker weighs what the package covers: a requirement with no test, a test with no evidence view, an aspect with no constraint (C51, C52, C5), and, for packages that ship .prd sentence payloads, the normative text itself: every normative sentence of the source document is either covered by a model element or carried as a pinned, reasoned allowance, with duplicate sentences adjudicated (C71, C72, C73). The coverage report prints with --coverage .

17.6. Diff and exports

Two further toolchain operations are specified here informatively, because they consume but do not change the language:

  • primmel diff <a> <b> computes the structural model diff between two package states: id-keyed, tier-annotated, classified as added, removed, changed, or moved, with the mapping diff (pairs and the computed coverage delta) and the clause-drift table. With --exit-code the diff is the change-audit gate; with --compare-texts it additionally classifies renumbered clauses as same-text or differed.
  • primmel export reqif and primmel export rdf project a package into the interchange formats ( projections). The projections are lossy by design: they export what the target format can say.
  • primmel export impact projects a package into the impact graph ( impact-graph): the adjacency form of the model's edge-bearing facets, keyed by element id, with the forward and reverse indexes a change-impact read consumes.

17.7. Projections

The ReqIF projection exports the package's requirements as ReqIF XML for requirements-management tools. The RDF projection exports the model graph as RDF (Turtle by default, JSON-LD on request) for linked-data consumers. Both are one-way views over the model; neither is an authoring format, and neither round-trips. Their presence answers the interop question without giving the interchange formats the authoring role ( Annex C ).

17.8. The impact graph

The impact graph is the adjacency form of the model's edge-bearing facets: the answer to the consumer's change-impact question ("if this tolerance changes, which tests, verdicts, and certificates are affected?") as a read over an index instead of a walk over the authored trees. Like the interchange projections it is a toolchain artifact — generated, never authored, never re-imported — and it is specified here informatively; the reference toolchain emits it as `primmel export impact (the impact-graph/1` projection).

17.8.1. The impact edge registry

The registry below is the normative content: it names every facet that contributes edges to the graph, the kind of edge each contributes, and the collection the edge targets. These are the dedicated hot edges of 14.3 — facets of their constructs, never ref triples — plus the typed references the v3.2 set added. An edge's kind is the facet's role; an edge's target collection is where the target id resolves.

Construct Facet Edge kind Target collection
requirement binds_to binding aspect or capability of the subject
requirement dependencies prerequisite requirement
requirement limit.accepts.verdict acceptance verdict
requirement applicability classification dimension (11.1.1)
requirement channel evidence channel channel
conformance_test targets coverage requirement
conformance_test inherits_from specialization conformance_test
conformance_test dependencies prerequisite conformance_test
conformance_test instances.by instantiation instance
conformance_test result_forms evidence view form
conformance_test produces_artifacts issuance artifact
verdict inputs (symbols) derivation symbol
verdict inputs (verdicts) acceptance chain verdict
verdict behavior state transition state machine
calculation inputs derivation symbol
calculation lookup / profile data source table or profile
calculation params parameterization parameter register
form requirements evidence scope requirement
form calculation_context derivation context calculation
form field bind / verdict / targets binding attribute path, verdict, requirement
form formulas_used derivation calculation
dimension value implies subsumption dimension value
instance of / model / family instantiation the instantiated element
manifest uses / maps_to composition package
manifest supersedes / superseded_by edition lineage package edition

17.8.2. The export contract

The export is a versioned, one-way toolchain projection — the same doctrine as the ReqIF and RDF projections of 17.7 , and the same "generated, never authored, never re-imported" rule. The projection carries the version identifier impact-graph/1 and this shape:

  • adjacency keyed by the package-authored element id (the id an author wrote, not a generated surrogate), so a consumer joins the graph to the authored trees without a mapping step;
  • a forward index (element → the edges it declares) and a reverse index (element → the edges that target it), the two views of the same edge set;
  • every edge carrying its kind and its source facet, and the clause provenance ( source { doc clause } / ref derives-from) of the edge-bearing element where the element declares one.

The consumption win is the reverse-index read: the F6 change-impact question ( oimlsmart/rag docs/annealment/F6-impact.md ) — "this tolerance changed; enumerate the tests that target its requirement, the verdicts that chain through those tests, and the certificates those verdicts issue" — is a reverse-index traversal from the changed element, not a walk over the authored trees. Because the projection is one-way, a consumer that needs the authored form reads the package; the graph is the index, never the source.

18. Formal grammar (EBNF)

This clause states the grammar of the language at its load-bearing levels: the lexical structure, the top level, and the shared sub-structures that recur across constructs. The per-construct facet grammars are specified in clauses 10 to 15 , and the construct registry in Annex A states every keyword, its aliases, and its payload shape. The grammar notation is EBNF: [ x ] is optional, { x } repeats zero or more times, and | separates alternatives.

18.1. Lexical grammar

Text        ::= { Whitespace | Comment | Token }
Whitespace  ::= ? any Unicode whitespace character ?
Comment     ::= ( '#' | '//' ) { ? any character except line feed ? } LineEnd
              (* recognized only at a token boundary *)
Token       ::= QuotedString | Block | BareToken
BareToken   ::= NonSpace { NonSpace }
            (* where the first character is neither '"' nor '{' *)
QuotedString::= '"' { EscapedChar | ? any character except unescaped '"' ? } '"'
EscapedChar ::= '\' ? any character ?
Block       ::= '{' Text '}'
            (* balanced; a quoted string inside a block is scanned as a
               string, so braces inside it do not nest *)
LineEnd     ::= ? line feed or end of input ?

18.2. Top-level grammar

Model          ::= { Declaration }
Declaration    ::= SpecialDecl | ConstructDecl
SpecialDecl    ::= 'root' BareToken
                 | 'version' QuotedString
                 | 'metadata' Block
                 | 'package' Block
ConstructDecl  ::= Keyword Identifier Block
Keyword        ::= ? a keyword of Annex A, or an alias ?
Identifier     ::= BareToken

Within a block, the facet grammar of each construct is a sequence of facet entries:

FacetEntry   ::= FacetKeyword FacetValue
FacetKeyword ::= ? a facet keyword of the construct ?
FacetValue   ::= QuotedString | Block | BareToken | Expression | BracketList
Expression   ::= ? a token starting with 'ocl{', re-accumulated until the
                   braces balance ?
BracketList  ::= ? a token starting with '[', re-accumulated until the
                   accumulated text contains ']' ?

18.3. Shared sub-structures

The provenance block, the typed relation, the key-value entry, the quantity block, the trust reference, the correspondence entry, and the alias list entry recur across constructs:

SourceBlock    ::= 'source' '{' 'doc' Value 'clause' Value [ 'fragment' Value ] '}'
RefEntry       ::= 'ref' Predicate Value [ '{' 'note' QuotedString '}' ]
Predicate      ::= BareToken   (* declared in a predicate registry; C103 *)
KeyValueEntry  ::= Key ':' Value [ Unit ]
                 | Key ':' QuantityBlock
QuantityBlock  ::= '{' 'value' Value 'unit' Value
                     [ 'kind' Value ] [ 'uncertainty' Value ]
                     [ 'tolerance' Value ] '}'
Applicability  ::= 'applicability' '{' ApplicabilityEntry { ApplicabilityEntry } '}'
ApplicabilityEntry ::= DimensionKey ':' Value [ 'match' MatchMode ]
            (* Value: a bare id, a quoted text, a bracket list, or a
               parameter-mapping block *)
MatchMode    ::= 'any' | 'all' | 'exact'   (* parse-enforced *)
TrustRef       ::= 'trust_ref' BareToken [ 'key' BareToken ]
            (* the organization identifier, optionally one published key *)
CorrespondsEntry ::= 'corresponds' BareToken QuotedString [ CorrespondsBlock ]
CorrespondsBlock ::= '{' { 'projection' BareToken ProjectionPayload } '}'
ProjectionPayload ::= '{' { BareToken Value } '}'
            (* the codec's steering input, stored untyped *)
AliasListEntry ::= AliasKeyword [ SpellingCode ] '{' { Value } '}'
AliasKeyword   ::= 'aliases' | 'alt' | 'colloquial' | 'abbreviations'
                 | 'deprecated'
            (* the term's alias family; the tagged form carries the
               spelling its list is authored in *)
SpellingCode   ::= BareToken   (* ISO 24229 code; C89 *)

18.4. What the grammar does not decide

Three judgments are deliberately not grammar:

  1. closed vocabularies. Where a facet's value set is closed, the clause that specifies the facet says so, and says whether the set is parse-enforced (a violation is a parse error) or checker-enforced (a rule of Annex B);
  2. reference resolution. Whether an identifier resolves is a checker matter (Annex B), never a parse matter;
  3. scope discipline. Which slot a value may occupy at which chain level is a checker matter (C17), never a parse matter.

19. Dataspaces, policies, trust, and correspondence

19.1. The layer this clause adds

Primmel v3.1 adds the dataspace to the language as model content. Four constructs carry it:

  • the dataspace construct: the dataspace definition as a model object ( dataspace-construct);
  • the policy construct: a usage-policy set in Primmel's own policy grammar ( policy-construct);
  • the trust_ref reference form: a model-level reference to the trust plane ( trust-references);
  • the corresponds annotation: the generalized per-node correspondence declaration and the projection-steering declarations of the expression codecs ( correspondence-annotations).

Two doctrines bound every construct of this clause.

The first is self-containment. The semantics of these constructs are Primmel's own: a reader understands a dataspace, a policy, a trust reference, or a correspondence completely, with zero external standards open. Third-party standards (the dataspace protocols, the policy expression languages, the concept registers) live in the expression layer only: a Primmel policy can be exported as an ODRL document, a dataspace's catalog surface can be expressed over a dataspace protocol, a node can be referenced to an external concept register, and each of those is a projection codec or a correspondence claim, the same shape as the ReqIF and RDF exports of 17.7 . The third-party standard is an output format, never an input dependency; correspondence (maps-to) is not import (is-defined-by), and a correspondence declaration never carries the semantics' source ( 19.5 ).

The second is the concern boundary. The language is data definition and workflow definition: every construct of this clause is a declaration. A dataspace declares its participant classes, artifact classes, policy sets, trust anchors, and governance citations; a policy declares its rules; a trust reference declares addressing. The permission system's realization (roles, grants, tokens, the enforcement machinery) belongs to the runtime platform alone, and no construct of this clause is a permission mechanism. This is the same boundary the workflow layer already holds: a transition declares that an act requires a privilege (its requires ), and the language never realizes the privilege.

19.2. The dataspace construct

A dataspace declares one dataspace as a model object: the participant classes (the kinds of organization that may join), the artifact classes (the kinds of model-defined content the dataspace exchanges), the policy sets that govern the exchanges (by reference, 19.2 ), the trust anchors the exchanges root in ( 19.3 ), and the governance citations (the scheme documents' clauses, in the provenance machinery of clause 14 ).

dataspace bfs-exchange {
  name "Bean freshness exchange"
  description "The scheme-operated dataspace for freshness-evaluation evidence."
  participant_class certification-body {
    label "Certification body"
    description "A body operating a freshness certification program."
  }
  participant_class test-laboratory {
    label "Test laboratory"
    description "A laboratory performing freshness measurements."
  }
  artifact_class evaluation-report {
    label "Evaluation report"
    description "The laboratory's evaluation report on a lot."
    element /art/evaluation-report
    policy restricted-exchange
  }
  artifact_class registry-extract {
    label "Registry extract"
    description "A public extract of the certified-lot registry."
    element LotRegistryEntry
  }
  policies { public-access restricted-exchange }
  default_policy restricted-exchange
  trust_anchor scheme-registry {
    trust_ref bfs-scheme-op key bfs-2026-root
    role registry
    description "The scheme operator's participant registry and root key."
  }
  compatible_with { allied-scheme-exchange }
  ref derives-from "urn:example:bfs:2026#clause-5.1"
}

The facets:

Facet Arity Meaning
name 0..1 The display name.
description 0..1 Free text.
participant_class <id> { ... } 0..* One participant class: a kind of organization the dataspace admits. Facets: `label`, `description`. The class is declared here, in the dataspace, not referenced.
artifact_class <id> { ... } 0..* One artifact class: a kind of content the dataspace exchanges. Facets: `label`, `description`, `element` (0..1, the model element that defines the class's content: an artifact definition, a form, or a data class), and `policy` (0..1, the per-class policy override).
policies { ... } 0..1 The dataspace's policy register: the policy sets (<<policy-construct,19.2>>) the dataspace carries, by identifier.
default_policy 0..1 The standing policy every artifact class inherits. An artifact class's own `policy` facet overrides the default for that class; the negotiation machinery of the platform never widens past the default without the holder's explicit act.
trust_anchor <id> { ... } 0..* One trust anchor: the trust-plane identity the dataspace's exchanges root in. Facets: `trust_ref` (exactly one, <<trust-references,19.3>>), `role` (0..1, the anchor's function, a program register), `description` (0..1).
compatible_with { ... } 0..1 The declared compatibility register: the identifiers of other dataspaces this one declares compatibility with. Compatibility is always explicit; it is never inferred from shared content.
ref , source 0..* The governance citations: the scheme documents' clauses the dataspace's definition interprets, in the canonical provenance form (<<provenance-forms,14.3>>).
corresponds 0..* Correspondence annotations (<<correspondence-annotations,19.4>>).

A dataspace is published, versioned, and composed as part of its package ( 16.3 , 7.6 ): the package is the unit of publication, and the dataspace definition rides it. Another scheme's dataspace declares compatibility with this one through its own compatible_with register; the declaration is model content, checked like any other reference (C104).

The checker verifies the dataspace's references (C104, dataspace-references-resolve ), the shape of its trust anchors (C105, dataspace-trust-anchor-shape ), and the presence of its governance citations (C106, dataspace-governance-provenance , a warning: a dataspace with no governance provenance is an orphan definition).

19.3. The policy construct

A policy declares a usage-policy set as first-class model content, in Primmel's own grammar: rules over the dataspace's artifact classes and the actions on them, with constraints in the language's embedded expression dialect ( 8.6 ). A policy is fully meaningful with zero external references. Export to a policy expression language (ODRL 2.2 is the reference codec) is a projection of the expression layer ( 19.5 ): the wire consumes the expression; the semantics are this clause's.

policy restricted-exchange {
  name "Restricted exchange"
  description "Restricted artifact classes exchange under an active agreement only."
  default_posture true
  governs { evaluation-report }
  rule read-under-agreement {
    kind permission
    action read
    artifact evaluation-report
    constraint "ocl{agreement.state = #active}"
  }
  rule retain-nothing {
    kind prohibition
    action retain
  }
  rule log-every-access {
    kind obligation
    action log
  }
  ref derives-from "urn:example:bfs:2026#clause-6.2"
}

The facets:

Facet Arity Meaning
name 0..1 The display name.
description 0..1 Free text.
governs { ... } 0..1 The artifact classes the policy governs: identifiers of artifact classes declared by the dataspaces of the merged model (C107).
default_posture 0..1 `true` or `false` (a closed vocabulary, parse-enforced). A default-posture policy applies to every artifact class it governs unless the class overrides it; a non-default policy attaches only by explicit reference (an artifact class's `policy` facet, or the platform's negotiation records).
rule <id> { ... } 0..* One rule of the set. Facets below.
ref , source 0..* The clause provenance of the policy.
corresponds 0..* Correspondence annotations (<<correspondence-annotations,19.4>>).

A rule's facets:

Facet Arity Meaning
kind 1 The rule kind, a closed vocabulary, parse-enforced: `permission`, `obligation`, or `prohibition`.
action 1 The governed action: a bare token from the program's action register (`read`, `exchange`, `retain`, `log`, ...). The vocabulary is the program's data; the language carries it untyped, and the expression codecs map it (an ODRL projection maps to the ODRL action vocabulary).
artifact 0..1 The artifact class the rule constrains. When present it names one class of the policy's `governs` register (C107); when absent the rule applies to every governed class.
constraint 0..* A constraint on the rule's applicability, as an embedded expression (<<embedded-expressions,8.6>>); quantities carry the measurement model's unit contract. The language stores the expression; evaluation is the platform's.

The checker verifies the policy's shape: a policy declares at least one rule, every rule carries its action, a rule's artifact stays inside the governs register, the governed classes resolve, and two default-posture policies never govern the same artifact class (C107, policy-shape ).

19.4. Trust references: the trust_ref form

A trust reference names a trust-plane identity from model content: an organization by its registry identifier, optionally with one of the organization's published keys by key id:

trust_ref bfs-scheme-op
trust_ref bfs-scheme-op key bfs-2026-root

The form is a shared sub-structure of the grammar ( 18.3 ), specified here and adopted by the dataspace's trust anchors ( 19.1 ); later revisions may admit it on further constructs.

The resolution contract, stated once:

  1. the reference is opaque to the model. It carries addressing only: the organization identifier and the optional key id. It never carries key material, endpoints, or credentials;
  2. resolution is the consumer's, at runtime. The platform resolves the organization identifier against the trust registry it is configured with, and the key id against that registry's published key set for the organization. The registry's endpoint shape is the platform's contract, never the language's;
  3. a conforming checker never resolves a trust reference. An unresolved (or unresolvable) organization identifier is not a finding: the trust plane's membership at any moment is runtime fact, not model content. The checker verifies the reference's shape only (C105, on the dataspace's trust anchors).

19.5. Correspondence annotations: the corresponds facet

A corresponds facet declares that its element corresponds to an external concept, and optionally steers how the element projects into an expression codec's output:

attribute_definition moisture_limit {
  name "Moisture limit"
  value_type QuantityValue
  unit percent
  irdi "0112/2///61987#ABA001"
  corresponds iec-cdd "0112/2///61987#ABA001"
  corresponds dpp "attr:moistureLimit" {
    projection dpp-attribute { path "identity.moistureLimit" }
  }
}

The entry's shape is corresponds <scheme> "<concept>" [ { ... } ] :

scheme
a bare token naming the external scheme register the concept identifier belongs to (iec-cdd, dpp, vc-claims, ...). The scheme vocabulary is data: the program declares the registers it maps to, and the language carries the token untyped. A scheme declaration never imports the register's semantics; it names the correspondence's target address space.
concept
the concept identifier in the scheme's own addressing (an IRDI, a URI, a claim name), an opaque string. The model asserts correspondence with the concept; it does not restate the concept's definition.
the optional block
the projection-steering declarations: projection <codec-id> { <key> <value> ... } entries, one per expression codec the element projects through. The codec identifier names the codec (the program's register again); the key-value payload is the codec's input, authored once in the model and stored untyped, validated by the codec at export time, never by the language.

Like the ref facet, corresponds is accepted on every construct; the lenient skip consumes its scheme, its concept, and its optional block ( 9.5 ). The irdi facet of an attribute definition ( 10.1 ) remains as the legacy single-scheme spelling of corresponds iec-cdd ; a conforming serializer keeps each spelling as authored, and when both are present on one element they must name the same concept (C108, correspondence-shape , a warning leg). Two entries on one element never name the same scheme: one element has at most one correspondence per scheme, so every bridge reads an unambiguous mapping (C108, an error leg).

19.6. The expression layer: correspondence is not import

The constructs of this clause complete the language's answer to alignment with external standards, and the answer is one-sided by design. A Primmel model is the single source of truth of its own semantics; an external standard enters the model's content only as a correspondence claim (maps-to), and leaves the model only as a projection (an export document). The expression layer, the set of projection codecs a toolchain ships, turns model content into the documents a third-party ecosystem consumes: an ODRL policy document for the dataspace wire, a catalog dataset for a dataspace protocol, an AAS submodel, a verifiable-credential claim set, a concept-register reference. Each codec is an output format of the toolchain, exactly the shape of the ReqIF and RDF projections of 17.7 , and no codec's semantics flow back into the language. A capability the grammar lacks is a language extension; it is never a bolted-on foreign document.

The authored correspondence annotations ( 19.4 ) are the codecs' input: a program declares its correspondences once, in the model, and every bridge consumes them instead of its own hand mapping.

20. Annex A (normative): The construct registry

This annex is the registry of every top-level construct of the language. It reproduces the parser configuration of the reference implementation ( packages/primmel/src/ser-des/config/index.ts of primmel-ts); the two are verified together, and a discrepancy is a defect in one of them.

Every construct declaration has the shape KEYWORD IDENTIFIER { ... } : one keyword, one identifier, one block payload ( 9.1 ). The four special declarations of the first table are the exceptions. The "since" column states the language generation that introduced the construct: v2 (the MN 113 generation, including its extension wave), v3 (this document's first revision), v3.1 (the 2026-08-28 revision's extension set, clause 19 ), or v3.2 (the 2026-09-04 revision's consumption extension set, primmel/spec#18); v3 accepts every v2 construct ( Annex D ), v3.1 changes no v3 construct, and v3.2 changes no v3.1 construct.

20.1. Special declarations

Declaration Payload Meaning
root one bare token The top-level model identity (<<special-declarations,9.2>>).
version one string The model version text; accepted, not recorded (<<special-declarations,9.2>>).
metadata block The singleton metadata block (<<special-declarations,9.2>>).
package block The package manifest, singleton (<<package-manifest,9.3>>).

20.2. The construct registry

Keyword Aliases Collection Since Clause
role roles v2 12
provision provisions v2 11
process processes v2 12
approval approvals v2 12
class data classes v2 13
enum enums v2 13
data_registry registries v2 13
variable variables v2 13
measurement variables v2 13
exclusive_gateway gateways v2 12
start start_event events v2 12
end end_event events v2 12
signalcatch signal_catch_event events v2 12
timer timer_event events v2 12
reference references v2 14
canvas subprocess pages v2 12
note notes v2 13
table tables v2 13
figure figures v2 13
link links v2 13
comment comments v2 13
map_profile map profiles v2 12
view_profile view view profiles v2 12
term terms v2 13
form forms v2 11
subform subforms v2 11
symbol symbols v2 13
calculation calculations v2 13
verdict verdicts v2 11
reference_material reference materials v2 13
test_point_set test point sets v2 13
competence_kind competence kinds v2 11
predicate predicates v2 14
constraint constraints v2 11
discrepancy_record discrepancy records v2 14
state_machine state machines v2 12
requirement requirements v2 11
requirement_class requirement classes v2 11
instrument instruments v2 10
attribute_definition attribute definitions v2 10
capability capabilities v2 10
behavior behaviors v2 10
condition_set condition sets v2 10
conformance_class conformance classes v2 11
conformance_test conformance tests v2 11
subject subjects v3 10
instance instances v3 10
artifact_definition artifact definitions v3 10
artifact_instance artifact instances v3 10
quantity_register quantity registers v3 13
dual duals v3 13
activity_archetype activity archetypes v3 12
connector_profile connector profiles v3 13
monitor monitors v3 13
passport passports v3 13
invariant invariants v3 13
test_sequence test sequences v3 13
formulas_used formulas-used registers v3 13
text texts v3 15
dataspace dataspaces v3.1 19
policy policies v3.1 19
dimension dimensions v3.2 10

20.3. Registry notes

  1. variable and measurement feed the same collection; measurement is the canonical keyword of the MN 113 text and variable the legacy spelling. Both remain registered.
  2. The event constructs register under both the short and the full keyword forms; the two forms of one event kind share one parser.
  3. canvas is the diagram container; subprocess is its v2 spelling. A process's subprocess facet ( process-subject) names the page it renders on; the two uses of the word are one reference.
  4. view is the legacy spelling of view_profile.
  5. The include directive ( include-directive) is a preprocessor directive, not a construct; it never reaches the parser.
  6. The checker allowlist file .primmel-allowlist.prl is package metadata ( allowlist), not content; it is excluded from the package merge.

21. Annex B (normative): The checker rule catalog

This annex is the catalog of every rule the checker runs. It reproduces, without paraphrase, the machine-readable registry of the reference implementation ( packages/primmel/src/check-rules.ts of primmel-ts at version 1.8.2), which primmel check --rules prints. The toolchain registry is the operative form; this annex is its publication. A discrepancy between this annex and the toolchain registry is a defect in one of them, and the verification is one command:

primmel check --rules

Each rule has an identifier (C1 to C119; the numbering is historical and not contiguous by family), a name used in findings, a family, a default severity, and a level:

The detailed semantics of a rule (what exactly it matches) are documented with the rule's implementation and its tests; this catalog fixes the identifiers, names, families, severities, and levels that conformance cites.

Rules introduced by the v3.2 revision (C110 and higher; primmel/spec#18) ship in the reference toolchain at @primmel/primmel 1.9.0 and are part of the operative registry; the conformance suite at version 3.2.0 pins each rule's error-severity leg (clauses CON-01 to CON-09). Three legs report at warning severity in 1.9.0 where the shipped estate trips the error form — C110's cross-field alias overlap, C115's unit resolution, and C118's unit resolution — and tighten to the severities this catalog states when the estate's terms and quantity registers are re-authored (TODO.primmel/11d). Rule C119 (the requirement-namespace pin of 7.6 , mirroring the layering doctrine's layer-owned-namespace guard) ships in the reference toolchain at @primmel/primmel 1.10.0; the conformance suite at version 3.3.0 pins its error leg (clause PKG-06).

21.1. base

Rule Name Default severity Level
C1 attribute-bind-scope error normal
C2 reference-targets-resolve error normal
C3 dimension-enums error normal
C4 store-uniqueness error normal
C5 req-test-coverage warning normal
C56 allowlist-malformed error normal
C57 allowlist-stale error normal
C89 spelling-code-wellformed error normal
C90 invariant-shape error normal
C91 invariant-enforcement-grammar error normal
C92 test-sequence-shape error normal
C93 test-sequence-integrity error normal
C94 formulas-used-shape error normal
C96 duplicate-id error normal
C97 program-maps-resolves error normal
C98 program-surveillance-required warning normal
C103 declared-predicate error normal
C110 term-alias-shape error normal
C111 dimension-shape error normal
C112 reference-identity error normal
C118 requirement-parameter-shape error normal

21.2. anatomy

Rule Name Default severity Level
C6 anatomy-family error normal
C7 anatomy-characteristic-derivation error normal
C8 anatomy-behavior-resolves error normal
C9 subject-extends-resolves warning normal
C84 constraint-shape error normal

21.3. process

Rule Name Default severity Level
C10 process-one-start error normal
C11 process-terminal-end error normal
C12 process-flow-names-resolve error normal
C13 process-signature-realized error normal
C14 process-timer-recurrence error normal
C15 process-timer-period error normal
C16 process-step-ids-unique error normal
C58 activity-kind-resolves error normal
C59 segregation-members-resolve error normal
C74 process-io-type-coherence error normal
C75 process-flow-io-cover error normal
C76 subprocess-signature-bound error normal

21.4. instantiation

Rule Name Default severity Level
C17 instance-scope error normal
C18 instance-version-pin error normal
C19 chain-acyclic error normal
C20 instance-of-resolves error normal

21.5. mapping

Rule Name Default severity Level
C21 mapping-resolves error normal
C22 mapping-direction error normal
C23 mapping-calculus-consistency error normal
C24 import-not-mapping error normal
C25 mapping-description warning audit
C26 view-read-only error normal

21.6. composition

Rule Name Default severity Level
C27 uses-resolves error normal
C28 uses-no-redefine error normal
C29 uses-cycle error normal
C30 provides-consumed-or-waived warning normal
C31 requires-satisfied error normal
C119 namespace-pin-violation error normal

21.7. quantities

Rule Name Default severity Level
C32 inv1-no-bare-quantity error normal
C33 quantity-coherence error normal
C34 duality-coherence error normal
C35 time-format error normal
C36 map-type error normal
C114 limit-quantity-coherence error normal
C115 calculation-signature-shape error normal

21.8. state

Rule Name Default severity Level
C37 state-fires-resolve error normal
C38 state-family-separation error normal
C39 state-machine-states-referenced error normal
C40 anatomy-state-resolves error normal
C41 precondition-on-violation-known warning normal
C95 cascade-transition-resolve error normal
C109 state-machine-initial-present error normal

21.9. promises

Rule Name Default severity Level
C42 promise-target-resolves error normal
C43 promise-verifiable warning normal
C44 promise-not-bare-value error normal

21.10. artifacts

Rule Name Default severity Level
C45 artifact-def-contract error normal
C46 artifact-instance-resolves error normal
C47 artifact-evidence-separation error normal

21.11. characteristics

Rule Name Default severity Level
C48 characteristic-one-home error normal
C49 characteristic-behavior-link error normal
C50 characteristic-derivation-inputs error normal
C116 verdict-inputs-resolve error normal
C117 verdict-chain-acyclic error normal

21.12. twins

Rule Name Default severity Level
C60 serve-targets-resolve error normal
C61 payload-schema-quantity error normal
C62 access-scope-covers-serves error normal
C63 freshness-required-on-live-bindings error normal
C64 endpoint-profile-resolves error normal
C65 monitor-subject-resolves error normal
C66 monitor-trigger-wellformed error normal
C67 monitor-evaluate-resolves error normal
C68 monitor-fail-escalation warning normal
C69 monitor-escalation-resolves error normal
C70 monitor-emit-sinks error normal
C99 variable-provenance-channel error normal
C100 composition-components-resolve error normal
C101 composition-decomposition-covers error normal
C102 composition-state-rule-closed error normal

21.13. coverage

Rule Name Default severity Level
C51 coverage-test-evidence warning audit
C52 coverage-form-judgment warning audit
C53 coverage-uses-bound error normal
C54 coverage-lookup-table-exists error normal
C55 coverage-budget error audit
C71 text-coverage-sentence-uncovered warning audit
C72 text-coverage-budget error audit
C73 text-coverage-config error normal

21.14. edition

Rule Name Default severity Level
C77 edition-status error normal
C78 edition-validity-window error normal
C79 edition-supersedes-resolves error normal
C80 edition-pin-resolves error normal
C85 baseurn-wellformed error normal
C113 edition-lineage-coherent error normal

21.15. supply chain

Rule Name Default severity Level
C81 product-maps-resolves error normal
C82 product-unmapped-promises warning normal
C83 abstract-import-pinned error normal
C86 passport-content-resolves error normal
C87 passport-access-leak error normal
C88 passport-upi-scheme error normal

21.16. dataspace

Rule Name Default severity Level
C104 dataspace-references-resolve error normal
C105 dataspace-trust-anchor-shape error normal
C106 dataspace-governance-provenance warning normal
C107 policy-shape error normal
C108 correspondence-shape error normal

22. Annex C (informative): Why not an existing notation

This annex answers, once, the question every standards modeller asks: why a new language at all. Each candidate notation is named with what it carries well, the semantic class it lacks (the class Primmel exists to carry), and the disposition this program adopted. The dispositions follow the program's standing rule: every external standard this work touches is named, with used, extended, or deliberately not used, and the reason. The deeper comparative analysis, including OPM, EXPRESS, and RDF/OWL, is published in the foundations volume ( primmel/primmel-smart-docs , docs/foundation/08-comparative-analysis.md ).

22.1. ReqIF (with the DIN DKE SPEC 99200 profile)

What it is. The OMG Requirements Interchange Format, and its profile for standards documents, model a standard as rows in a requirements database: a small set of spec-object types (document, heading, provision), one relation (cross-reference), XHTML text with a modality tag. It is an interchange convention between requirements-management tools, and as interchange it is sound.

The missing semantic class: *the subject, and everything anchored to it*. A ReqIF row cannot say what a requirement constrains (it carries a tagged paragraph, not a constraint bound to subject aspect paths), whether something conforms (there are no values, no quantities, no tests), or how conformity is established and judged (there are no operations, no evidence records, no verdicts). Evaluation semantics are outside the format's scope by design.

Disposition: used as an export projection ( primmel export reqif , 17.7 ), so requirements-management tools can consume a package; and deliberately not used as the authoring model, because the authoring model needs exactly the semantic classes the interchange scope excludes.

22.2. UML and SysML

What they are. UML is the general-purpose structure and behavior modelling language; SysML extends it toward systems engineering with requirements and parametrics, and SysML v2 (on KerML) rebuilds the stack on a formal kernel. The kernel/surface architecture of SysML v2 and the one this language rests on are the same architectural move, arrived at independently (foundations volume, chapter 5).

The missing semantic class: the compliance spine . Neither notation carries modality-tagged obligations with clause-level provenance to a published normative text, a sentence-level text-coverage audit against that text, evidence records as a permanent fact tier, or verdicts as re-executable judgments, and neither states the fact/judgment firewall. UML's action semantics stop at the escape hatch (the action body is opaque text); SysML requirements trace model elements to model elements, not model elements to published clauses. Conformity assessment (tests as operations, acceptance decisions with guard bands, verdict chains) is not in either metamodel's scope.

Disposition: deliberately not used as the modelling base. The correspondences are acknowledged (the data pillar plays the role of a class diagram; the kernel/surface split is shared with KerML); the reason against adoption is that retrofitting the compliance spine onto a general-purpose metamodel costs more semantics than it reuses, and the target of this work is a text language with a checker, not a diagramming metamodel.

22.3. BPMN and DMN

What they are. BPMN is the process-flow notation (tasks, events, gateways, token semantics) with wide engine adoption; DMN is the decision notation (decision tables, FEEL expressions).

The missing semantic class: the subject and its quantities . A BPMN task is a labeled shape: it has no typed signature bound to a subject's aspects, no registers, no bound state machine, no quantities with units and uncertainties, and no evidence records. BPMN conformance means behaving like the major engines, not satisfying a rule catalog over models. DMN decides over input data, but its decisions bind nothing to a subject: no measured-value semantics, no acceptance decision with guard bands, no verdict chain. The pair models work and decisions, not conformity.

Disposition: extended . The Primmel process surface (events, gateways, flows, canvases) is the BPMN-shaped subset, extended with signatures, registers, state binding, transition firing, sub-process signature binding, and the process-integrity rules (C10 to C16, C74 to C76). DMN's decision tables correspond to the table and profile lookups of 13.7 plus the acceptance decisions of 11.5 ; the notation was not adopted because a decision in this language must bind to subject quantities and verdicts.

22.4. SBVR

What it is. The Semantics of Business Vocabulary and Business Rules: a formal logic over noun concepts and fact types, with alethic and deontic modalities, for stating business vocabulary and rules precisely.

The missing semantic class: dynamics and measurement . SBVR has no transitions (nothing executes), no exhibited values (nothing is measured), no tests, and no evidence: a rule states what must be true, never how conformity is established or judged. Its deontic obligation is a modality on a proposition, not an operation on a subject.

Disposition: deliberately not used . The vocabulary role is served by the term construct and the predicate registry ( 13.10 , 14.4 ), which carry terminology with clause provenance; the rule role is served by requirements and constraints with embedded expressions ( 11.1 , 11.7 ). The reason against adoption is the one this whole annex states: the language exists to carry the classes SBVR excludes by design.

22.5. ODRL and the dataspace protocol stack

What they are. ODRL 2.2 (the W3C Recommendation) is a policy expression language: permissions, prohibitions, and obligations over assets, for the wire. The dataspace protocols (the Dataspace Protocol family, the IDS reference architecture) define the catalog, contract-negotiation, and transfer machinery of a data exchange.

The missing semantic class: the model . ODRL expresses a policy as a standalone document, but nothing in it says what the policy governs in the model's own terms (the artifact classes, their content contracts, their provenance), and a dataspace protocol carries the exchange without defining the exchange's content. The semantics of "what may move, under whose rules, anchored to which trust" belong to the scheme's model; encoding them in a wire format's terms would invert the dependency.

Disposition: used as codec outputs, never imported. The policy construct of 19.2 is Primmel's own policy grammar, self-contained; an ODRL document is its projection for the wire ( 19.5 ). The dataspace definition of 19.1 is model content; a protocol catalog entry is its expression. Correspondence to external concepts is declared as maps-to annotations ( 19.4 ), and a correspondence never carries the target's semantics into the model.

22.6. The standing answer

Every candidate stops at the document's text or the model's shape. The classes they lack (the subject, measured values, operations, evidence, verdicts, provenance to the published clause) are the classes a SMART standard must carry to be executable. That is the whole answer, and it is given once, here.

23. Annex D (informative): Lineage and compatibility

23.1. The lineage

Primmel is the public successor to the Multi-Modal Modelling Language (MMEL). The lineage in one table:

Generation Document Content
MMEL 0.1 The MMEL schema identifier inside .mmel files The four pillars: data, process, compliance, measurement
Primmel v2 MN 113 (this repository, `sources/spec/`) and its parts MN 113-2 to 113-10 The MMEL 0.1 surface plus the extension wave: forms, subforms, symbols, calculations, state machines, terminology; the package manifest; the requirements and subject-chain constructs
Primmel v3 This document The v2 surface plus the subject anatomy, instantiation, artifacts, quantities and duality, the activity taxonomy, the twin interface, monitors, passports, the assurance constructs, multilinguality, and the checker catalog as a conformance surface
Primmel v3.1 This document, 2026-08-28 revision The v3 surface plus the dataspace constructs: the `dataspace` and `policy` constructs, the `trust_ref` reference form, and the generalized `corresponds` annotations (<<dataspaces-policies-trust,clause 19>>), with their checker rules (C104 to C108)
Primmel v3.2 This document, 2026-09-04 revision The v3.1 surface plus the consumption constructs (primmel/spec#18): the term alias family (<<term-alias-family,13.10.1>>), the top-level `dimension` construct (<<dimension-construct,10.6>>), the document-anchor grammar and the structured `reference` identity (<<document-anchors,14.7>>), the `superseded_by` lineage edge (<<edition-lineage,9.3.1>>), and the units-typed calculation signatures and limit quantities (<<calculation-signatures,13.7.1>>, <<limit-quantities,11.1.2>>), and the verdict chain with the instance-parameter schema (<<verdict-construct,11.3>>, <<instance-parameters,11.1.3>>), and the impact-graph export (the impact edge registry and the `impact-graph/1` projection, <<impact-graph,17.8>>), with their checker rules (C110 to C118)

23.2. The compatibility contract

v3 is a strict extension of v2 at the parse level:

  1. every v2 keyword parses under v3 ( annex-a-constructs), including the alias pairs ( variable / measurement, canvas / subprocess, view / view_profile, the short and full event keywords);
  2. the .mmel file extension is accepted everywhere .prl is ( serialization-file-types);
  3. the MMEL_MAP type spelling of a .prm file is read as an alias of Primmel_MAP, and v2 files keep their v2 spellings across a load/dump cycle ( prm-format);
  4. the legacy citation spellings ( source { doc clause }, references { <role> { ... } }, specification_reference "...") keep parsing and fold into the same model channels as the canonical ref forms ( provenance-forms);
  5. the deprecated manifest facet extends is read as a one-element uses ( package-manifest);
  6. the version declaration line is accepted ( special-declarations).

The forward-compatibility rule of 9.5 runs the other way: a v3 document using constructs a v2 parser does not register parses under that parser with the unknown material skipped. Authors targeting a mixed toolchain estate SHOULD validate in strict mode against the oldest intended generation.

v3.1 is a strict extension of v3 at the parse level, with one boundary stated honestly. A parser of the first v3 revision tolerates the v3.1 top-level constructs ( dataspace , policy ): the unknown keyword, its identifier, and its block are skipped whole. The new multi-token facets are a different case: the skip discipline of the first revision special-cases only ref , so a first-revision parser misparses a corresponds or trust_ref facet inside a construct it knows. The multi-token skip rule of 9.5 covers these facets from this revision on; a document that uses them on the older constructs requires a v3.1 reader, and the version-pinned package composition of 7.6 is where a program states that requirement.

v3.2 is a strict extension of v3.1 at the parse level and changes no v3.1 construct. Its new top-level construct ( dimension ) is skipped whole by a v3.1 parser, and its new facets are block-valued (the alias family of 13.10.1 , the manifest superseded_by register, the signature range and limit quantity blocks) or single-valued ( values_of , the reference identity facets, quantity_kind ), which the one-token skip covers — with one exception stated in 9.5 : the spelling-tagged form (`colloquial fra-Latn { ... }`) is a multi-token facet, so a document using it requires a v3.2 reader.

23.3. Migration guidance

A v2 model becomes a v3 model by declaration, not by rewrite: nothing in a v2 package needs to change. The v3 additions are available when the model earns them: the subject anatomy when aspects need the IS/HAS/DOES split, instances when samples and units enter the model, ref relations when the predicate registry is declared, and the checker catalog as the conformance surface throughout. The canonical migration of the legacy citation spellings to ref is mechanical, and the serializer performs it on every dump ( 14.3 ).

24. Bibliography