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 semantic basis: the eight terms, LutaML, and MMEL

Primmel's semantics is not invented here and not inherited whole from one language. It has three layers, in this order.

7.2.1. The ground: the eight terms

The semantic ground is the algebra of the foundation volume (the IS–HAS–DOES modelling system , normatively cited): eight terms — three relations and five sorts — with three operations and three closure rules.

Term Meaning
IS individuation: x is the same entity as y , and x is a K — one relation, two grains
HAS attribution: an object has a value along a property
DOES dynamics: an object does a transition
O objects the bearers of all claims
P properties the slots along which objects vary
V values what fills a property slot
T transitions input → transform → output rules
PROCESS = ρ(T) ⊆ O reified transitions: a step is a transition an object does; a process is their composition

The operations are composition ( ∘ , transitions compose when their interfaces match), reification ( ρ , a transition becomes an object), and embedding ( ι , an object can be a value). The closure rules seal the system: kinds are objects (the codomain of IS is O — no TYPE primitive; the power-type discipline of 9.4 is this rule's expression), values hold references (every relation between objects is a property whose value is an object-reference — no RELATION primitive), and process is reified transition (the dynamic axis folds into the static one; one machinery serves both).

The foundation volume proves the system closed (nothing escapes the eight terms), complete (every descriptive claim factors through them), and extensible (it grows by content, never by new primitives). Every construct of this specification is a composition over these terms; a construct that cannot be read as one is a defect of this specification.

7.2.2. The surface and the vocabulary

On that ground, the VOCABULARY comes first from the SMART documentation — the original guides that define what every MMEL primitive MEANS (normatively cited): the seven node types; the process node's fields, whose Input/Output are EVIDENCE; the provision (Actor, Modality, Required evidence); the two data units — the REGISTRY (a data structure that is also the place records are kept) and the DATA CLASS (structure only); the attribute (name, modality, cardinality, type, reference); the measurement variable types; the gateway and edge semantics; connections; mappings; view profiles. A primitive of this specification that the SMART documentation does not define is an EXTENSION, and must say so ( semantic-basis-modes , below). The EVIDENCE LAW is the documentation's own, made normative: a process-test's output is evidence stored as a REGISTRY record, and a provision's fulfilment cites that record — no test output floats free; no evidence exists outside a register.

7.2.3. The documented vocabulary, realized

Every documented primitive, its documented meaning, and the construct that realizes it. (Mode: inherit — the construct IS the primitive; refine — the documented semantics hold with a declared refinement.)

Documented primitive Documented meaning (SMART guides) Primmel realization Mode
Metadata the model's identity; namespace = the global id package refine
Section text kept for document generation text blocks refine
Term term + admitted terms + definition + notes term inherit
Role actor of a process/approval; approver role inherit
Reference a clause citation, attachable to provisions, attributes, approvals reference refine (the URN discipline)
Link URL / repo, with title and description link inherit
Multimedia figure / video / 3D figure inherit
Table 2D array; the class view's schema IS a class definition table refine (the schema bridge)
Enum a type's closed value list enum inherit
Mapping A ⇒ B — fulfilling A satisfies B, not conversely map_profile inherit
Diagram a model has many; a subprocess is another diagram process (the canvas dialect) refine
Node: Process a process fulfilling its provisions process inherit
Node: Approval an approval process approval inherit
Node: Gateway conditional branching; no conditions = any path fulfils exclusive_gateway inherit
Node: Start event one per diagram, no incoming start inherit
Node: End event zero or many; required in an empty gateway path end inherit
Node: Timer event a time period; with self-connection = repeat within N timer inherit
Node: Signal event an external happening that triggers signalcatch inherit (a deprecation candidate — the guides themselves mark it unused)
Process field: Actor the role who runs it process actor → role inherit
Process field: Input/Output relevant EVIDENCE at the input/output phase process inputs/outputs → registry records refine ( 6.2.3 )
Process field: Provision MUST/SHALL/SHOULD/CAN/MAY statements provision inherit
Process field: Note non-requirement statements note inherit
Process field: Tests measurement validation conformance_test refine (the executable programs)
Registry a data structure AND the place records are kept data_registry inherit
Data class a data structure only class inherit
Attribute name, modality, cardinality, type, reference class attributes ( 12.2 ) refine
Measurement variable 7 types; user-defined vs derived variable + the measurement types refine
View profile parameterized view — externally set values view_profile inherit

A construct of this specification that realizes no documented primitive is an EXTENSION (below); the minimalism audit ( TODO.reconfigure file 12) is this table's inverse: every construct accounted for as primitive, composition, extension — or retracted.

7.2.4. The evidence law

A process-test's OUTPUT is EVIDENCE stored as a REGISTRY record, and a provision's fulfilment cites that record. The loop is closed and total: the provision names its required evidence (Actor, Modality, Required evidence — the documented components); the process carries the test; the test's output is written as a record of the registry the evidence requirement names; the fulfilment cites that record with per-value provenance. No test output floats free; no evidence exists outside a register. The conformance clauses (10), the process clauses (11), and the data clauses (12) each carry their leg of this law; the checker enforces it.

On that ground and that vocabulary, Primmel INHERITS two:

  • LutaML (the LutaML Language Reference, the pinned edition — normatively cited in normative-references) is the data-modeling SURFACE — the syntax of the object/property/value axis: packages, classes, attributes (name, type, definition, cardinality, value restriction), enumerations, instances in the assignment form, references and inclusion, value types, and the serialization mappings. Every data-bearing construct in this specification is LutaML's, in one of three declared modes ([[semantic-basis-modes]]): inherited (the construct IS LutaML's; the clause cites the source), refined (LutaML's semantics hold with a declared refinement), or extended (no LutaML counterpart; the rationale is stated).
  • MMEL (the MMEL element semantics — the reconciled parts: data model, forms, symbols, calculations, state machines, process model, measurement, terminology, compliance) is the element basis: forms and their binding, symbols and their units, calculations and their typed inputs/outputs and expression discipline, state machines and their transitions and guards, the process dialect, the measurement types, term entries, and provisions with their modality. The same three modes apply, part by part.

What Primmel ADDS — and the only places where this specification creates vocabulary rather than citing it — is the SMART layer, each piece a named composition over the eight terms: the subject anatomies and the definition/instance discipline, the evidence plane (attribute definitions with scope and origin, provenance, attestations and the certificate lineage), the validation plane (verdicts and their chains, conformance tests and their executable programs), the quantity and duality discipline, the power-type modelling relationships, and the composition and edition machinery of packages. Annex A records, for every construct, its basis and mode; a construct without a stated basis is a defect of this specification.

The discipline the basis imposes: where LutaML or MMEL defines the semantics, this specification cites and conforms — it never restates in different words and never drifts; where Primmel extends, the extension states what it extends and why. The conformance suite carries the same discipline: an inherited construct's fixtures are the basis language's fixtures where they exist.

7.3. 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.4. 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.5. 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.6. 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.7. 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). The scoped form pkg:token names a capability carried by a package OUTSIDE the closure — requires never content-merges: the token resolves against that package's declared provides, from every reading posture (the composed locator and the manifest-only sibling resolution alike), so a package's capability dependencies are explicit in its own manifest without its closure growing;
  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).

7.8. Module-slot overlays

A module package may declare a PARAMETER SURFACE: the manifest lists its slots with `with_slots { <slot> { skeleton "<path>" target "<path>" [each] } … } — skeleton` names the template file the module ships, target the path the bound copy materializes at in the CONSUMING package, and each marks a family slot (a LIST of binding maps, the skeleton materializing once per entry). A slot without a target is the documented parameter surface only: it substitutes inside the module's own content and materializes nothing.

The consumer binds slots ON the uses entry — the binding map rides the entry, keeping the import graph and its parameterization in one place:

uses {
  module-emc-disturbances with {
    forms { severity_a { label "…" clause "…" } … }
    scheme "B"
  }
}

At composition time a bound target slot MATERIALIZES: the skeleton's content is copied to the target path with ${{ <slot> }} placeholders substituted from the binding map (per entry for each slots). The ownership doctrine is the skeleton's: the template owns the pattern interface (field names, types, measurement methods, derivations), while the consumer's overlay at the target path binds the flavored content (units, calculation ids, clause URNs, enum values, labels) — a multilingual-array key is owned by exactly one side per field, and the no-redefine rule (C28) holds between the materialized skeleton and the overlay. Unbound target slots are a composition error, as is a binding naming no declared slot; a target path the consuming package already declares is the no-redefine collision, not a merge.

This construct mirrors the deployed YAML-side doctrine (the module skeletons the smart platform's linker materializes with with: maps and ${{ }} substitution): the spec defines its PRL-shape preservation. The conformance clauses and the corpus scenarios land with the kernel wave that implements the parse, the materialization, and the checks — the grammar here is the decision this section makes: the binding map rides the uses entry (the side-band binds: form is rejected; the parameterization reads with the import it parameterizes, and the no-redefine and namespace-ownership rules keep one owner per path without a second registry to reconcile).

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 has two carriers. The quoted carrier 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 inline carrier (v3.3) is a delimited block — the raw formula, never quoted, parsed at load ( expression { ... } ; the facet's own grammar and its argument scope are specified with the limit, 11.1.4 ). A string value and a block value never mix in one facet: the value's shape is the carrier decision. The quoted payload remains opaque text to a conforming parser, bound by the consuming program; the inline block is the language's own expression syntax and carries the resolution doctrine of 11.1.4 .

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. schema is DECLARATIVE — the qualified bare token schema primmel:0.1 (<registry>:<version>, no quotes); the quoted string form (schema "Primmel 0.1") is retired prose and parses only through the migration window.
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).
language 0..1 The language revision the package is written against (e.g. `v3.4`). Under the strict parse mode the declaration is the gate: a parser older than the declared revision rejects the package instead of silently skipping its newer constructs (C176, `language-declared-wellformed`); the deprecated-carrier ratchet (C178) counts the composed standard against it (<<checker,16>>).
license_key , license_holder 0..1 each The entitlement facet pair: the entitlement-catalog key the platform's license gate reads (`std:<slug>` — C144 checks the shape, C145 the at-most-one rule), and the content's copyright holder (required with the key, C146). A package without a key is public content; a bare holder is attribution without an entitlement claim.
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.

9.6. Namespace declarations

A package may declare namespaces — named uri + prefix pairs, the LML surface (RS 3001 §Namespacing) — in its scope:

namespace CeramicsNs {
  uri "http://example.com/ceramics"
  prefix "cer"
}

Rules:

  1. a namespace block carries uri (1), prefix (0..1), and the default marker (0..1); a prefix-less namespace marked default is the default namespace of the package's wire forms ( wire-mappings);
  2. the metadata block's namespace facet ( special-declarations) remains the model's namespace STRING; when the package also declares namespace blocks, the string MUST name one of them (C162) — the string is the reference, the block is the declaration;
  3. wire mappings reference namespaces by declared name; a mapping that references an undeclared namespace is a checker error (C162).

9.7. The prose quarantine

Primmel is a data modelling language: semantics are carried only by typed, useable fields. Human-language text appears in exactly two places: a label facet (the short display string — itself data, it is what prints) and a description { ... } block (the quarantine). The bare-string spellings definition , summary , statement , criteria , and response are retired surface forms; their content migrates — the structured part into typed fields, the prose part into a description block. Citations are never prose: a reference is a typed ref entry resolving to a urn ( clause 14 ); a free-text reference is a checker error (C158). The deletion test is normative: a package with every description block removed parses, checks, links, runs, and attests identically — if any check result changes, the package smuggles semantics in prose (C158).

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{...}"
  ref derives-from "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 .

A derivation expression names only declared definitions: declared calculations, tables, profiles, symbols, measurements, and attributes of the model. The checker rejects an expression that references anything else and names the missing declaration (C149, the closed-derivation rule). The rule is what makes evaluation total and pure: a calculation's semantics are its declared inputs and body, so two evaluations of one calculation over one input produce one result. 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."
  ref derives-from "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: RETIRED (the typed table is the register)

The condition_set construct is RETIRED: the corpus's fourteen registers migrated to the typed table (§12.1, the wave of 2026-10-19 — each register split by its values' shape), and the construct's parse support is removed. A model still carrying the spelling fails at parse. The typed table is the register; the values bind via table:<id>.<column> .

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 by C80 against the edition register — under composition, the union the closure explicitly declares: every composed package's manifest register joins the root's, so an instance pins against its declaring package's register, and a pin to an edition no composed manifest declares still fails), 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 }

Every value entry — an instance's attributes and test context, an artifact instance's content, a registry record — admits a provenance block. The block states where the value was read from, in the register's own words when the wording differs, and how the value's status came to be:

e_max : {
  value 30 unit t
  provenance {
    source "OIML certificate NMi-3826124-01, page 1"
    page 1
    entry_wording "Max capacity"
    read_at 2026-09-29T10:00:00Z
    declaration app-flintec-2025-0314.characteristics
    verdict /verdicts/mpe
  }
}

The facets are source (the document, test report and run, or system of record), page , entry_wording , read_at (a datetime, clause 13 ), and the lineage references: the declaration the value came from and the verdict that validated it, when the value's status has changed ( clause 19 ). Provenance is optional per entry, and it is carried through serialization. The register-certificate use case — thousands of documents read from scans, each field traceable to its page — is the conformance example the suite carries.

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

10.3.1. Power-type instantiation

The of reference names a subject definition, an instrument, a class, or ANOTHER INSTANCE: every instance can serve as the definition of further instantiation. This is the power-type discipline — the modelling relationship the CDD family of languages is built on, and the interop requirement of the CDDAL projection (a Primmel instance projects as a subclass-of-one): specialization is not a fixed ladder of levels but a recursive pattern, and the subject chain of subject-construct is one profile of it.

instance grp-500 {
  of LoadCellModelFamily
  has { attributes { p_lc : 0.7 } }
}
instance mod-500-2t {
  of grp-500
  has { attributes { e_max : 2.2 t } }
}
instance smp-001 {
  of mod-500-2t
  level sample
  has { test_context { d_min : 0 kg } }
}

Value resolution walks the of chain upward: an instance's own has values override (never replace) those of the instance it instantiates, and the walk terminates at a subject definition (the delegation law INV-10, generalized from the fixed four-level chain). The upward chain links ( model , family , group ) remain the subject-chain profile's idiom and are unchanged; an instance that carries BOTH an of -to-instance reference and an upward chain link is incoherent and is rejected (C20).

The same discipline reaches the definition side: a class 's extends { ... } names a class or an INSTANCE. A class extending an instance specializes that individual — the PROTOTYPE: an instance of the class walks, through the class plane, to the prototype and inherits its exhibited values, so a manufacturer's product line can derive from one characterized prototype without restating it. The delegation walk reads exhibited values only — a class's declared defaults stay outside the walk, exactly as they always were; the prototype hop adds the individual's readings, never the class's contracts.

The walk crosses the class plane exactly once per of to a class: it follows the class's extends chain (class to class, transitively) until it reaches an instance, and continues from there through that instance's own chain. A cycle over these mixed edges — an instance whose continuation through the class plane reaches itself — is rejected (C155).

The typing bridge power-types too: a payload_class reference and an attribute definition's class reference name a class or an INSTANCE — an instance is a class, and its shape is the fields of the class its own chain terminates at (C152).

The checker resolves of against declared subjects AND declared instances (C20), and rejects a cycle in the of chain (an instance that transitively instantiates itself). The four-level scope vocabulary (family, group, model, sample) governs the subject-chain profile; of -to-instance chains outside that profile carry value inheritance without the level law (a package that wants the level law declares its levels, as the R 60 packages do).

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_definition whose content is the projection of declared claims declares contract_from instead of restating fields:

artifact_definition certificate-document {
  contract_from LoadCell.promise_set
  produced_when on_request
  ref derives-from "urn:oiml:pub:r:60-a:2021#annex-b"
}

The contract's field list is then the referenced set's print projection, never a restatement of it; the checker requires the reference to resolve and rejects a definition that declares both contract_from and inline contract fields (C153).

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 dimension value's payload is typed by declaring payload_class with the reference of a class ( clause 13 ); the payload's fields then conform to that class's fields, and the checker validates them (C152). The typed payload is the form the language prefers: a context-specific definition — an accuracy class with its intervals and error tiers — is a value carrying a typed instance of its definition, not a symbol pointing at a table and not an untyped string map. The untyped payload { ... } map remains accepted for compatibility; a package that declares payload_class retires it (C152 flags a value that carries both). 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 {
    args {
      mpe   <- symbol:mpe
      p_lc  <- symbol:p_lc
    }
    expression {
      abs(p_lc * n_runs) <= mpe
    }
    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` (a quoted OCL payload, <<embedded-expressions,8.6>>, or the inline block, <<limit-expressions,11.1.4>>), `args` (the inline body's scope, <<limit-expressions,11.1.4>>), `uses` (the legacy form's 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 {
    args {
      mpe    <- symbol:mpe
      e_max  <- attribute:e_max
    }
    expression {
      abs(served - nominal) <= mpe
    }
    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.1.4. The limit: the expression body and its arguments

The limit's machine-executable predicate has two carriers. The legacy carrier is a quoted OCL payload, expression "ocl{...}" , whose free variables ride the uses { ... } list. The inline carrier (v3.3) is a delimited block, expression { ... } : the raw formula, never quoted, parsed at load. The legacy string and the inline block never mix in one limit — the value's shape (a string or a block) is the entire decision.

The inline limit is a function : it takes declarative arguments, and the scope is passed into it. An args { ... } block binds each local name to its declaration — one binding per line, name <- source :

limit {
  args {
    e_vehicle  <- symbol:e_vehicle
    mpe_class  <- table:vehicle_mpe.mpe_initial
    d          <- attribute:scale_interval
    n_axles    <- observable:n_axles
  }
  expression {
    abs(e_vehicle) <= max(mpe_class, 1 * d * n_axles)
  }
  notes "Table 2 percentage of the conventional value (6.7)."
}

The block order is canonical: args , the expression, then the documentation and acceptance facets ( modality , relative_to , notes , quantity , accepts , acceptance , source_discrepancy ).

The binding sources:

Form Binds to
symbol:<id> , table:<id> , datum:<id> , verdict:<id> , attribute:<id> A declaration of this package's own registries. A dotted id rides the declared object (`table:vehicle_mpe.mpe_initial` names a column of the declared table).
observable:<id> The dispatch context — the readings the run observes.
self.<path> The subject aspect the requirement constrains.
process:<p>.<step>[.<io>] The step plane (register 19): a declared process, one of its steps, or one of the step's declared signature inputs or outputs. The dot descends the declared structure; the leaf names a declared io (C173, `step-plane-refs-resolve`, checks the descent).
<package>:<kind>:<id> A declaration across a uses edge; the composed closure resolves it.

The resolution doctrine : the body's identifiers are the args and nothing else — an identifier naming no argument is air inside the formula and is rejected (C171, expression-identifiers-resolve ). Three exceptions are context, never ambient scope: a path rooted at self ; a path rooted at one of the dispatch-context roots ( sample , family , group , model , instrument , specimen ) — the pronouns of the dispatch, allowed as path roots, never as bare scalars; and the closed function vocabulary — abs , max , min , floor , ceil , round , sqrt , exp , ln , log , mod , sum , count , exists , forall . A call whose target is a dotted path ( table.permits(a, b) ) or a bound argument (a table lookup) rides its declared object — the root is the check, the method is the object's own interface.

The expression grammar binds the usual precedence, loosest first: implies , then or , xor , and , the comparisons ( <= >= < > = ≠ ), the additive operators, the multiplicative operators, unary - and not , then the primaries — the bound names, the calls, the numeric literals, the single-quoted string literals, and the parenthesised groups. The collection navigation (register 24) rides the postfix position: collection -> forAll(v | body) , -> exists(v | body) , -> select(v | body) — the lambda parameter scopes the body exactly like an argument binds the limit's, and the iteration predicate's truth is the navigation's value. The serialized form is canonical: the dumper re-emits the parsed tree's canonical source, and the canonical source reparses to the same tree.

A requirement_class 's law ( class-law ) carries the inline form with the args OPTIONAL: the class's containment is itself the declared scope, and the law's identifiers are the members. An explicit args block on the law adds bindings, each of which must name a contained member (C172, requirement-algebra-resolves ).

The inline carrier is the language's for every machine-executable payload: the verdict's canonical derivation ( derive { … } ), the test variable's derivation ( derivation { … } ), the run-validity check of a precondition and the constraint's invariant ( check { … } ) — the same string-vs-block carrier decision, the same canonical serialization, the same AST-exact resolution (C171). A comparison inside an inline expression sets operands of the same declared quantity kind (C177, kind-coherence ): the kinds come from the args' declared sources, an operand of unknown kind or a bare number never conflicts, and the unit algebra stays with the evaluation plane — the checker names the kinds, it does not compute them.

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 {
        indication - applied_load
      } }
  }
  observables { observable indication_series { quantity_kind mass unit kg } }
  preconditions {
    precondition warmed-up {
      check {
        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), procedure (the execution skeleton — see below), 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.

The declared procedure carries the test's execution skeleton. The named typed step is the one form, and the declaration order is the sequence:

procedure {
  step check_test_conditions "Review the test conditions" inputs { reference_conditions }
  step apply_test_load_points "Apply the test loads" outputs { e_l e_r }
  step record_indications
}

A step carries its id, optional prose, inputs and outputs . The ids are unique within the procedure (C147). The legacy spellings — the numbered facet ( 1 "action" ) and the bare procedure_steps id list — are DEPRECATED: the checker warns (C154) until the realized models migrate, then both refuse at parse — the ids live on the steps themselves. The executable programs ride beside the skeleton: preparation { … } (the ordered warm-up/preload/zero-check discipline) and stimulus { … } (the ordered measurement program with drive, hold, freshness, and per-point acceptance).

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 {
    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.

15.8. The uncertainty budget

An uncertainty_budget declares the GUM components of one measured quantity's standard uncertainty and the coverage factor of its expansion. The budget is a DECLARATION — the propagation law (u c = √(Σ uᵢ²), U = k·u c) is the GUM's, computed by the evaluation plane; the model names the components, their kinds, and their provenance, never the arithmetic:

uncertainty_budget u_creep_reference {
  target twin_creep_drift_indication
  coverage k 2
  component u_std {
    type B
    distribution rectangular
    half_width 0.25
    unit "kg"
    ref derives-from "urn:oiml:pub:cs:twin-1:2027#clause-6"
    description "The force standard's certificate half-width."
  }
  component u_pair {
    type A
    series reference_pair_error
  }
}

The facets are target (the declared verdict whose measured quantity the budget backs), coverage k (the expansion factor; the GUM's default 2 applies when omitted), and the component entries. A Type A component evaluates the statistics of a captured series — its series names a declared test variable carrying the series shape. A Type B component states a distribution (rectangular, triangular, normal, ushape — the GUM's input models; the divisor rule is the evaluation plane's) around its half_width . The checker verifies the resolution: the target is a declared verdict, a Type A series is a declared captured series, a Type B component states its distribution and half-width, and the coverage factor is a positive number ( C179 ).

11.8. 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 {
    self.applied_load <= self.e_max
  }
  on_violation invalid
  violation_meaning "A run above capacity is not a test result."
  ref derives-from "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.9. 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 , min / max (the scalar numeric range — the entry-validation facet for measured fields: each bound a numeric literal or a template, and a numerically bounded pair is ordered min ≤ max (C184 field-range-order)), 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.10. 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
  ref cites "urn:fmv:std:bfs:4:2022#clause-4.2"
}

The facets are condition (the requirement text), modality ( 11.6 ), and ref cites "<document anchor>" ( 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.

11.11. The run: input, output, store

A conformance test's execution is a first-class shape — the run — and the chain it closes is input ⇒ output ⇒ store: the run is the INPUT (who performs it, against which trajectory), the test's result forms are the OUTPUT records collected from the run, and the store names the data registry those records land in. The form is never the run: it is the record the run produces.

conformance_test /conf/x {
  name "X"
  type Testing
  targets { /req/x }
  result_forms { probe }
  run {
    performed_by laboratory
    store probe-records
    trajectory run_states
  }
}

The facets: performed_by is a closed vocabulary, parse-enforced ( laboratory | manufacturer ); store names a declared data_registry ; trajectory names a declared operational state_machine whose fired transitions the run folds. The run's lifecycle is the language's own closed vocabulary — performed , completed , invalid , aborted — not a facet: invalid is the run whose setup was wrong (the precondition doctrine), never a fail.

11.11.1. The abort transition

invalid rides the precondition doctrine (the violated precondition names itself). aborted rides the ABORT TRANSITION (spec#56), which the language owns end to end:

  • Who may fire it — the run's own performer (its performed_by actor: the laboratory's or manufacturer's operational stop) or the scheme operator (the DIRECTED stop). The two carry different evidential weight: the performer's stop is an operational event, while a scheme-directed stop is itself a candidate finding the SCHEME records — it is never a laboratory failure.
  • The evidence — the transition is mandatory-evidenced: the actor (the closed vocabulary laboratory | manufacturer | scheme) and a NON-EMPTY reason. An abort without its reason is an unrecorded stop; invalid names its violated precondition, and aborted names its reason in the same breath. The transition, when the consumer's clock is authoritative, carries its at stamp (ISO 8601).
  • The transition fires once — an already-aborted run refuses a second abort: the first reason is the run's record.
  • The store doctrine — an aborted run's captured records LAND in the declared registry (the record is the record): the store holds what was captured, marked by the run's terminal state; the abort deletes nothing.

The runtime contract carries the same shape (the Run record's abort { by, reason, at? } terminal, abortRun 's validation): a conforming implementation cannot record an abort without its actor and reason, cannot fire the transition twice, and cannot unstore the partial records.

The run also carries the applied-input contract: inputs names the test's declared-source variables whose values the run applies — the scheme-set corners, dwell loads, classes, and factors the laboratory sets before it probes. A measured variable is an OUTPUT of the run — it can never be an applied input. The run consumes what the author declares and produces what the instruments measure.

run {
  performed_by laboratory
  store probe-records
  trajectory run_states
  inputs { envelope_temperature_min envelope_temperature_max }
}

The checker resolves the plane (C180 run-plane-shape , C181 run-store-binds ): the store names a declared registry, the trajectory names a declared machine, the applied inputs name the test's declared-source variables, and the registry's data class is the record forms' data class — the output records and the store hold the same shape.

The record form binds its applied-input slice DECLARATIVELY (spec#57): run_inputs names the owning test's run.inputs members the record reflects as fields — the record reflects the run by construction, not by convention, and the consuming application renders the applied inputs off the form alone:

form probe {
  data_class ProbeRecord#data
  conformance_process /conf/x
  run_inputs { envelope_temperature_min envelope_temperature_max }
}

The slice is machine-checked end to end (C183 record-run-inputs ): every name must appear in the owning test's run.inputs — the form's conformance_process binding resolves the owner (each of conformance_process_ids when the form covers several tests) — AND be a field of the form's data class: a record cannot carry what the run does not apply, and the record's slice must be the record's shape. A form declaring no slice is untouched (the linkage is opt-in; the per-store projection spelling was rejected because the per-form binding composes with subforms).

11.12. The check primitive

"Evaluate an expression and compare" is ONE construct. The surface spellings — a requirement's limit { expression { ... } } , an acceptance_criteria triple (type/operator/threshold), a process's validate_measurement "…" string, a form's pass_fail { pass_if … } , and the constraint construct — are consolidated surface forms over the one check :

check within_mpe_check {
  inputs { errors : number { unit v } mpe : number { unit v } }
  expression {
    errors -> forAll(e | e <= mpe)
  }
  operator lte
  threshold { symbol mpe }
  ref derives-from "urn:oiml:pub:r:60-1:2021#clause-5.3.2"
}

Rules:

  1. a check declares named inputs (C160: signature coverage — every consumed name is produced by a binding source), a boolean expression in the OCL dialect (logic, not math — calculations are declared math, calculation-math), and optionally an operator + threshold pair (the acceptance-criteria sugar);
  2. every identifier in the expression is a declared input, an output of a bound step or calculation, or a declared symbol (C158, C165);
  3. the retired spellings parse unchanged and fold into the checks collection; a conforming serializer emits the canonical check form;
  4. verdicts and acceptance decisions ( verdicts, acceptance-decisions) READ checks; they never re-evaluate prose.

12. Limits

Every requirement and requirement class carries its obligation as one or more limits . A limit is a function: it declares its arguments — each bound to the qualified declaration it reads — and states an assertion over those arguments. The scope is passed into the limit; nothing is ambient.

12.1. Argument bindings

requirement /req/metrological/mpe-vehicle-mass {
  limit {
    args {
      e_vehicle <- oiml-r134:symbol:e_vehicle
      mpe_class <- oiml-r134:table:two_axle_mpe.mpe
      d         <- oiml-r134:symbol:d
      n_axles   <- oiml-r134:symbol:n_axles
    }
    abs(e_vehicle) <= max(mpe_class, d * n_axles)
  }
}

A binding's source is qualified by its kind : kind:id names a declaration of this package's own registries; package:kind:id — the three-part scoped-reference grammar of the composition's capability references, extended with the kind disambiguator and the optional .column tail for tabular sources — crosses a uses edge, and the composed closure resolves it. There is no ambient scope: an identifier outside the args is air, whatever the binding's reach. The member bindings of a class law ( member:<id> ) are the one special form — the containment itself is the scope declaration. An identifier in the assertion that is not a declared argument is a parse error: the formula is the executable core, and nothing in it is air ( C171 ).

12.2. Multiple limits and applicability guards

A requirement carries a LIST of limits. Real standards bound different sub-domains differently; each limit states the sub-domain it applies to as an applicability guard over its own arguments:

requirement /req/metrological/mpe-ranges {
  limit "mpe-low-range" {
    args {
      e  <- oiml-r134:observable:e
      mu <- oiml-r134:table:vehicle_mpe.mpe_low
    }
    when {
      e <= 5000
    }
    expression {
      abs(e) <= mu
    }
  }
  limit "mpe-high-range" {
    args {
      e  <- oiml-r134:observable:e
      mu <- oiml-r134:table:vehicle_mpe.mpe_high
    }
    when {
      e > 5000
    }
    expression {
      abs(e) <= mu
    }
  }
}

Limits are named. The name is the limit's public handle: a conformance test targets it, the coverage and the retrieval projection emit it ( /req/metrological/mpe-ranges:limit/mpe-low-range ), the diff and the edition mappings cite it, and the checker's diagnostics name the failing limit. A name may be a quoted string ( ” mpe-low-range “ — the natural hyphenated form) or a bare identifier; the quoted form is unambiguous in every context, and the split stays clean: the NAME carries the natural wording, while the assertion's argument bindings carry the expression-safe local names ( e , mu ) — a hyphen never has to survive inside an expression. Multiple limits are a conjunction: every limit whose guard holds must hold. A limit with no guard applies over the whole domain; a single unnamed limit is accepted as the one-element shorthand. When the bound arguments declare their domains, the checker verifies that the guards partition the domain — no gap where no limit applies, no overlap where the assertion is ambiguous.

12.3. The class law: containment is conjunction

A requirement class contains its member requirements; containment is conjunction — the class's obligation is its members'. The class may state a law : a limit whose expression composes the members with the logical connectives the source document states in prose (and, or, either-or, neither-nor, unless):

requirement_class /req/electronic/software-protection {
  limit {
    expression {
      sealing or signalling
    }
  }
  requirement sealing { ... }
  requirement signalling { ... }
}

The containment is the law's scope: the law's identifiers are the contained members, and the body takes the inline form ( limit-expressions ). An explicit args block binds additional names, each of which must name a contained member. A class with no law is pure conjunction. The checker verifies the algebra: the law's identifiers name contained members only, every member appears in the law or the class carries no law, and a member's modality is never weaker than the class's ( C172 ).

13. Processes

13.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 ).

13.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.

13.2.1. Step signatures and the executor dissolution

Every step carries a SIGNATURE: named inputs in, named outputs computed. The output signature is the step's declared product; later steps and calculations consume only named outputs, through the binding edge (a named source — a step output, a reading field, or a declared parameter — to a named input). The process is a typed dataflow, not a checklist. A step that never ran has no outputs; a run missing a step's outputs is INADMISSIBLE (checker C160, signature coverage).

Step flavors are one primitive distinguished by what the outputs ARE: conditioning (named setpoints in, condition assertions out, the completion checked over the conditions log; downstream steps require the assertions), measurement (readings), computation (derived values), decision (branches).

The executor machine | actor vocabulary is RETIRED. The step names its role — the accountable actor, which never disappears (a rig's verdict is the laboratory's verdict; a service's signature is the issuing authority's). The RUN binds an executor that acts-AS the role, carrying qualification evidence appropriate to its kind: a person carries accreditation, a rig its calibration certificate (the TestRunEquipment cal-cert precedent), an API a service qualification, and a DIGITAL TWIN its twin-validation attestation — a twin is itself a subject of clause 10 whose claims are validated against the physical rig, so the loop applies to its own executors. The datum origin axis (declared, design-fixed, observed, computed) is epistemic and unaffected: a twin-measured reading is still observed.

A binding { from <named-source> to <named-input> } entry is the one dataflow edge; it dissolves the form-level calculation_bindings rename maps. The calls { with { in ... out ... } } form is its process-call projection (C74, C75, C76 unchanged).

13.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).

13.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.

The LAYOUT is part of the model's data: every flow node carries its position — x <number> and y <number> , the node's diagram coordinates in the page's space (the editor's placement, the serializer's round-trip). There are no waypoints: an edge's geometry is derived by the renderer, never declared.

13.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).

13.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 ReviewTask {
      create { application : self.id }
    }
    cascade AuditEvent {
      action record
      with { entity_type: "applications" }
    }
  }
}

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 optional cascade blocks of side effects — each naming its target entity and carrying 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).

States and transitions each carry two spellings: the states may be a states { … } list or bare state <name> declarations (a machine mixing the two canonicalizes to declarations on the first dump), and a transition may be the named form shown above or the arrow form transition <From> -> <To> [action <n>] { … } — multi-source lists ( from { A B } , [A, B] ) fan out to one transition per source in both. Each machine's authored spelling is what the serializer replays: the round trip is a fixpoint.

13.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 { ... } .

13.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).

14. Data, quantities, and time

14.1. Data classes, enumerations, and registries

The attribute surface is LUTAML'S ( 6.2 , the data-modeling surface of the object/property/value axis): an attribute is attribute <name>, <Type> { <facets> } — the name and the type separated by a comma, the facets in a block — with the facets LutaML defines plus the two the SMART documentation adds to the attribute (modality and reference):

class LoadCellSample {
  attribute serial_number, String { definition "The manufacturer's serial number" modality SHALL }
  attribute e_max, Mass { definition "Maximum capacity" cardinality 1..1 }
  attribute accuracy_class, AccuracyClass { values AccuracyClass }
}
enum AccuracyClass {
  value "A" { definition "the special class" }
  value "B"
  value "C"
  value "D"
}

The facets: definition (LutaML), cardinality <min>..<max> with * for unbounded (LutaML), values — a closed value set inline or an enum reference (LutaML), modality (the SMART documentation: the attribute carries requirement modality), and reference (the SMART documentation: a clause citation). An attribute of an enumeration type renders as the closed set; a quantity-valued attribute names its unit and kind through the quantity register.

A class declares a record shape; the #data suffix marks a class as registerable. A class without #data is a helper class: a reusable complex type that lives as an attribute inside other classes and cannot be registered — the SMART documentation's DATA CLASS. A class marked #data , or referenced by a data_registry , is the documentation's REGISTRY data structure: a structure AND the place records are kept ( 6.2 ).

An instance states values in LUTAML'S ASSIGNMENT FORM — `attribute

riding their declared facets:

instance smp-001 {
  of LoadCellSample
  serial_number = "HBK-001"
  e_max = 2.2 t
  accuracy_class = "C"
}
NOTE

The v2 field dialect <name>: <type> [<cardinality>] { … } migrates mechanically to the LutaML form ( name: T [0..1] { … } → attribute name, T { cardinality 0..1 … } ); the migration tooling ships with the kernel (phase 2 of the full flow), and the v2 dialect is rejected after the migration window.

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 ).

15. 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.

15.1. 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.

15.2. 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. The IDENTIFIER SPELLING of a kind or unit is the register's, verbatim: a condition-set entry key, a quantity_kind reference, or a unit reference names the register's declared identifier exactly — the language carries no underscore/hyphen equivalence, and the checker (C33) treats a spelling mismatch as incoherence. 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 ).

A unit may also pin the UnitsDB release directly — `unitsdb { nist "<id>" unitsml "<id>" }` — the machine-readable register the corpus's stem blocks carry UnitsML markup for. The DB is authoritative for the decomposition STRUCTURE (the si derived bases walk to the SI bases, the prefixes, the measurement scale); the register keeps its authored conversion numbers, and the checker verifies the pin against a supplied UnitsDB snapshot (C185 unitsdb-binding-coherent) — the interval-scale offsets (degree Celsius's 273.15) live in the register, never in the DB. The retrieval export carries the pin's identifiers and the resolved SI structure onto the emitted units when the export is given the snapshot.

15.3. Numeric semantics

A surface numeric literal is an EXACT DECIMAL. The authored form of a quantity value — its value, its uncertainty, its tolerance — is part of the value: 2.10 kg is a precision statement (two decimals — significant figures), never a misspelling of 2.1 kg . A quantity value whose authored literal carries information the number alone loses (trailing zeros, an explicit exponent form) preserves that literal verbatim on the wire; the conforming serializer emits it unchanged, and the round trip is the proof. Auxiliary numerics — register factors, offsets, column defs, orders — have no precision statement to carry and canonicalize to the number. An expression literal carries the same precision statement: the canonicalizer replays the authored number token verbatim — 2.10 stays 2.10 , .5 stays .5 , an exponent form stays the exponent form — while the evaluator reads every accepted spelling (integer, fraction, leading dot, exponent) as the number it denotes. Comparisons (limits, checks, acceptance decisions) evaluate in binary double precision (IEEE 754); the corpus's acceptance bounds are coarse by doctrine (MPE apportioning, class tables), never parked at a representable edge, and a scheme that needs exact-decimal adjudication declares it in the check rather than in the number.

15.4. 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).

15.5. The datum

A datum declares one typed, unit-bearing value of the model's data registry — the declared facts a formula, a limit, or an evaluation reads. The datum is the duality's registry face: where dual relates a quantity's two roles, the datum is the declared record a role's value resolves against.

datum reference_volume : number {
  label "Reference volume"
  unit "m3"
  quantity_kind volume
  origin observed
  scope type_evaluation
  description "The reference volume of the test run (3.2.1)."
  render latex "V_{\mathrm{a}}"
}

The facets: the type rides the head ( datum <id> : <type> ); label and description carry the human surface (the description may be a block); unit and quantity_kind declare the measure — a value stated for a declared quantity carries its unit (C32); origin is a closed vocabulary, parse-enforced: declared , design-fixed , observed , computed ; scope names the regime the datum belongs to; and each render pair declares the datum's presentation in a declared math notation. Declared datums join the checker's identifier registries: an expression argument may bind datum:<id> ( 11.1.4 ).

15.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.

15.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 , values , series , kind , quantity_kind , origin , and the linking facets attribute , calculation , and profile . The latex facet is RETIRED: a per-symbol glyph is derived presentation the math engine renders, never model semantics.

A calculation declares a named computation: inputs { ... } , output { ... } , and one of the calculation forms, math (declared math in a declared notation, 13.x — the form all new calculations use), expression (the retired OCL surface, read-only for the migration window), lookup (a table lookup with key ), or profile (a profile reference), with params { param <name>: <type> { ... } } , variable bindings, and provenance.

15.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.

15.8. The uncertainty budget

An uncertainty_budget declares one measured quantity's uncertainty components and the coverage factor of the expansion — the language side of the GUM plane. The budget is a DECLARATION: the propagation law is the GUM's, computed by the evaluation plane; the model names the components, their kinds, and their provenance, never the arithmetic.

uncertainty_budget u_ref {
  target mpe_error
  coverage k 2
  component u_cal {
    type B
    distribution rectangular
    half_width 0.5
    unit "kg"
    ref derives-from "urn:example:cert:2026#clause-5.1"
    description "The force standard's certificate half-width."
  }
  component u_rep {
    type A
    series reference_pair_error
  }
}

The facets: target names the verdict whose quantity the budget expands; coverage k states the coverage factor of the expansion; each component carries an id and its kind — type A evaluates the statistics of a captured series (a conformance test's declared series variable), type B states a distribution ( rectangular , triangular , normal , ushape — a closed vocabulary, parse-enforced) with its half_width and unit . Components and the budget itself take source provenance and description . One component per uncertainty contribution; Type A and Type B components mix freely in one budget.

The checker resolves the budget's references (C179, uncertainty-budget-resolves ): the target names a declared verdict, every Type A series names a captured series variable, and every Type B component states both its distribution and its half-width.

15.9. 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.

15.10. 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).

15.11. 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.

15.11.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).

15.12. 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).

15.12.1. The calculation math: declared notation, declared symbols

Math is NOT in OCL. A calculation's formula is DECLARED MATH, in a declared notation, bound to declared symbols. The expression "ocl{…}" form is retired for calculations (checks keep OCL — boolean logic is logic, not math):

symbol load_v {
  type number
  quantity_kind si:dimensionless
  unit si:1
  render latex "L_{v}"
  render asciimath "L_v"
}
symbol mpe {
  type number
  quantity_kind si:dimensionless
  unit si:1
  render latex "mathrm{MPE}"
}
calculation mpe_tiers {
  math {
    notation asciimath
    formula "p_LC * factor(load_v)"
  }
  inputs {
    load_v : number { unit si:1 }
    p_lc : number { unit si:1 }
  }
  output : number { unit si:1 symbol mpe }
  ref derives-from "urn:oiml:pub:r:60-1:2021#clause-5.3.2"
}

Rules:

  1. the math block carries notation — one of asciimath, latex, unicodemath, mathml, omml (parse-enforced) — and formula (a quoted string; for the XML notations, a block payload). The formula MUST parse in its declared notation under a Plurimath-conformant engine; the reference engine is plurimath-ts (C166).
  2. every identifier appearing in the formula is a DECLARED symbol bound to the calculation — an input, the output, or a declared constant (C165). The formula's symbols resolve by name; an undeclared identifier in math is a checker error, never a silent variable.
  3. inputs and output bind the symbol registry: a signature entry's symbol facet names a declared symbol construct (which carries the latex glyph, quantity_kind, unit); the entry's own typing facets must agree with the symbol's (C115). One output, exactly one; inputs one or more.
  4. the OUTPUT SYMBOL is the result's address: downstream calculations bind it as an input, steps bind it through the binding edge ( process-steps), forms bind it into fields, evidence records carry it. Results are read out, reused, and linked BY SYMBOL — the symbol is the link.
  5. units and quantity kinds reference AUTHORITATIVE registries only: unit unitsdb:degC, quantity_kind si:mass — the namespace prefix is a CLOSED, parse-enforced vocabulary: si (the digital SI framework / D-SI) and unitsdb (NIST UnitsDB). No package invents a unit namespace; a quantity the registries do not carry is expressed as a combination of the units they do (a ratio of masses carries quantity_kind si:dimensionless, unit si:1), and a genuinely novel unit is contributed to the authoritative registry, never declared locally (C167). The package-level quantity register no longer DECLARES units; it carries the usage set and the correspondences ( correspondence-annotations).
  6. rendering HINTS are declared, per notation, with the notation NAMED: render latex "L_{v}", render asciimath "L_v" — zero or more per symbol, the notation parse-enforced. The hint is presentation data (it names its notation, so it is useable), consumed by the math engine when it renders the formula in that notation; absent hints fall to the engine's default rendering of the identifier. The retired bare latex facet carried no notation and no form — that is why it was a primitive smell; the render statement is neither.
  7. the declared notation of a formula is authoritative and round-trips verbatim; MathML is the interchange pivot.
  8. the lookup and profile calculation forms are unchanged; a lookup's tier data and a profile's selections bind symbols the same way.

16. References and relations

16.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.

16.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.

16.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 (`source { doc "..." clause "..." [fragment "..."] } , the reference { doc ... clause ... }` role-channel blocks, references { <role> { ... } } , and specification_reference "..." ) are RETIRED: since 1.21.21 they are parse errors. A conforming serializer emits the canonical ref derives-from line for every provenance record (a free-text citation rides the target whole, `ref derives-from "PD-05 §4.1 “ ) ( 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 — the registry of named citation targets — retired with the legacy spellings: cite the document anchor directly.

16.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.

16.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.

16.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.

16.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 by its free-text designation whole ( ref derives-from "PD-05 §4.1" ): the display citation stays data, without pretending to a resolvable identity. The v2 reference construct (the registry of named citation targets) and its identity rule (C112) retired with the legacy spellings; cite the document anchor directly.

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 ref cites serves the citations that are NOT composition — the standards a model cites without merging their content.

17. The separator grammar

The name grammar crosses boundaries with three separators, each with exactly one meaning:

The document citations ( urn:… targets) are quoted strings — the URN's own syntax inside the citation, not this name grammar.

18. Multilinguality

18.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.

18.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.

19. Serialization

19.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 ).

19.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.

19.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.

19.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.

19.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.

19.6. Declared wire mappings

The wire form of a class — which element, attribute, or key carries each attribute when the model serializes to a data format — is DECLARED, never implicit and never hand-coded. The mapping construct (LML RS 3001 §Serialization, RS 3010 §Mapping blocks; Annex A ) binds a class to its wire form per format:

class Ceramic {
  datum location : String
  datum kiln : String
}
mapping xml Ceramic {
  element "ceramic"
  namespace CeramicsNs
  map_element "location" location
  map_attribute "kiln" kiln
  map_content notes
}
mapping yaml Ceramic {
  map "full-name" location
  render_nil false
}

Rules:

  1. Format is one of xml, yaml, json, toml, csv; one mapping per class per format; a second mapping for the same pair is a checker error (C164).
  2. the xml facets are the LML surface: element, namespace (a declared namespace name, namespace-declarations), map_element, map_attribute, map_content, mixed_content; the key-value formats use map (wire name to attribute), map_key to_instance, map_value to_instance/as_attribute, and render_nil.
  3. the compiler lowers declared mappings into the serialization profile the kernel executes; serialization NEVER consults hand-written conversion code — the declared mapping is the only wire contract (checker rule C164: a class serialized by a format with no declared mapping, or by conversion code the checker can see, is an error).
  4. this plane is distinct from the model-mapping artifact: a .prm file ( prm-format) maps one MODEL to another (the mapper plane); a mapping block declares one CLASS's wire form (the serialization plane). The shared word carries two jobs; the artifacts never mix.

[fmt]]

20. The grammar-backed string formats

A string_format registers a named string form for one artifact class — the grammar-backed serialization an artifact's textual projection uses. The format name carries the colon sigil of LML's registered formats; a bare name is a parse error.

string_format :pubid_iso {
  artifact "pubid"
  root "international-standard"
}

The facets: artifact names the artifact class the format renders, and root names the root element of the format's grammar. The registration is the contract between the model and the serializer: the wire mappings ( wire-mappings ) declare which elements ride which format, and the format declares the artifact and root the rendered document carries.

20.1. The canonical form as a command

primmel fmt [--check] <package-dir> is the canonical serialization as a command: each content file loads, dumps, and — when the bytes differ — is written back in the canon. The fixpoint is the safety: the canonical text re-loads to itself, verified before any write, and a file that does not round-trip byte-stably is reported, never rewritten. The author's facet order yields to the canonical order — the canon is the save-back contract: the kernel owns the serialized form, and an editor's save rides the same dump. --check reports and exits non-zero without writing (the CI gate).

21. The checker

21.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.

21.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.

21.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, do limits and calculation signatures declare their quantity kinds, and does every derivation reference only declared definitions (C149, the closed-derivation rule) and every typed payload conform to its declared class (C152)?
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, evidence stays separate from judgment, and a derived contract reference resolve without restating fields (C153)?
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?
attestation Do the attestation's subject, promise set, basis, and authority references resolve (C150), and does every mandatory claim the certificate prints carry its declaration and verdict lineage (C151)?

21.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).

The entry grammar — every field required, every entry one of the registry's rule identifiers:

allowlist_entry {
  rule C169
  match "provenance binding urn:oiml:pub:r:60-3:2021#table-* names no fragment or sentence of the shipped document*"
  reason "Bare subpart citations of the r60-3 tables: the clause-only harvest cannot verify table-level fragments."
  audit_ref "TODO.external-refs — the r60-3 re-harvest queue"
}

rule names the rule id; match is a glob over the finding message (anchored whole-string — a pattern not starting with * matches only from the message's head); reason states the debt in one sentence; audit_ref names the register entry that tracks the queue the entry declares. A missing field is C56; a rule-inactive entry matching nothing is C57.

Two OPTIONAL facets carry the deprecation ladder's audit vocabulary (spec#58) — present means well-formed, absent means the entry is not ladder debt:

allowlist_entry {
  rule C5
  match "requirement /req/no-such-req:*"
  reason "Fixed already."
  audit_ref "TODO.roadmap/17"
  migrated_by "model-library#84"
  retires_with "1.21.22"
}

migrated_by names the PR or wave that carries the fix the entry dates; retires_with names the release or wave whose landing lets the entry die. Tooling reads both to audit the debt uniformly and to flag entries whose stated wave has landed; a STALE entry that declared retires_with names it in the C57 message, closing the loop between the plan and the ratchet. An empty facet is 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.

21.5. The deprecation ladder

A construct retires through a NORMATIVE lifecycle (spec#58; the condition_set precedent, retired at 1.21.20, is the worked example):

  1. Arm — the construct's definition carries a migration note: the TARGET construct and the binding form, in one sentence (`deprecated: use table with column bindings`). The parser warns on every use of the construct (C154 construct-deprecated), and primmel check --rules renders every armed note, so an adopter reading a C154 warning finds the migration path without hunting this spec. The warning is the arm, never the gate: a package still on the construct checks clean at every level.
  2. Dated debt — packages that cannot move in the arm window record allowlist entries ( allowlist) with the ladder vocabulary: reason, audit_ref, and — where the debt is a planned wave — migrated_by and retires_with. The entries are the wave's receipt.
  3. Parse-refuse — a later release REMOVES the parse support: the legacy spelling becomes a parse error, the construct leaves the Annex A registry, and the checker rule burns down to empty (C154 stays in the catalog, idle, as the next retirement's hook). The removal release's corpus gate is green first: no adopter crosses the refusal by surprise, because the arm already named the path and the wave.

The ladder's steps are RELEASES, not flags: there is no compat shim, no dual read, no transition window beyond the arm. A construct that jumps the ladder (refusal without an armed note) is a spec violation of this section.

21.6. 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 .

21.7. 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.

21.8. 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 ).

21.9. 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).

21.9.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
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

21.9.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 (the canonical ref derives-from line) 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.

21.10. The validation contract and the new-rule register

The checker's mandate is specified, not incidental. Its rule families map the LML validation families and exceed them (the exceeded set is Primmel's strictly-greater side): attribute constraints (types, cardinalities, defaults, nil policy — plus units and quantity kinds), class constraints (record shapes, #data pairing), reference validity (urn resolution, binding closure) — plus provisions, coverage, composition, and the loop's own rules. The LML-alignment revision adds:

Rule Statement
C158 `prose-quarantine`: no bare string field outside a `label`, a `description` block, an expression, or a urn; a free-text reference is an error; the DELETION TEST is normative (all descriptions removed ⇒ identical check, link, run, and attest results).
C159 `urn-only-refs`: every reference resolves to a declared urn; the retired `source { doc …, clause "" }` spelling no longer parses — a free-text citation is a canonical `ref derives-from` whose target is the designation whole.
C160 `signature-closure`: every name an expression, check, step, or calculation consumes is produced by a declared output, reading, or parameter — across the composed closure.
C161 `check-surface`: the retired boolean spellings fold into the `checks` collection; the serializer emits the canonical form.
C162 `namespace-coherence`: the metadata namespace string names a declared namespace block; wire mappings reference declared namespaces.
C163 `change-levels`: model-diff output classifies releases — removal or rename (class, attribute, enum value), type or cardinality change: major; additive: minor; fixtures and documentation: patch — and the co-release rule: package version, conformance corpus, and grammar artifact agree before a release.
C164 `wire-mapping-coverage`: a class serialized to a format carries a declared mapping for that format; serialization never consults hand-written conversion.
C165 `math-symbols-declared`: every identifier in a calculation's formula is a bound symbol (input, output, or declared constant).
C166 `math-parses`: the formula parses in its declared notation under a Plurimath-conformant engine (the reference: plurimath-ts).
C167 `qualified-unit-reference`: a unit or quantity_kind reference uses the closed authoritative namespaces `si:` (the digital SI framework) or `unitsdb:` (NIST UnitsDB); anything else is a parse error. A render hint names its notation.
C168 `method-ref-resolves`: a conformance test's `method_ref` names THE PROCESS the test realizes — a bare id declared in the same package, or a scoped `<package>:<id>` reference resolved read-only through the locator (the target's own content validates the id; nothing merges). A bibliographic standard id is not a resolution target: the method is a process to be realized, not a document to cite. Locator-gated: without a locator the scoped leg stays silent. The process bodies' `calls` edges ride the same resolution.
C169 `provenance-clause-resolves`: a provenance binding citing a SHIPPED document lands on real content of that document — exact, subtree, or enclosure. A clause-qualified citation landing nowhere is air (an error); a bare subpart citation is unverifiable debt (a warning).
C170 `edition-lineage-declared`: a package declaring more than one edition carries `edition-mapping.json` (`primmel-edition-mapping/1`) naming both edition states — the cross-edition linkage is an artifact, never implicit.
C171 `expression-identifiers-resolve`: every identifier inside a limit expression, a derivation, or a derive resolves to a declared name in scope — the expression's uses list, the test's declared variables, or the package's symbols, datums, verdicts, tables, attribute definitions, and calculations. Dotted paths are whole references; a method call rides its declared object; the series aggregations (`max_of`, `min_of`, `mean_of`, `stddev_of`, `count_of`) are the type-A vocabulary the evaluation plane computes. The inline carriers (the `expression { … }`, `derive { … }`, `derivation { … }`, `check { … }` blocks) walk the parsed AST — the exact resolution; the legacy strings keep the registry walk. The formula is the executable core: nothing in it is air.
C177 `kind-coherence`: a comparison in an inline expression sets operands of the same declared quantity kind — the kinds come from the args' declared sources (`symbol:`/`attribute:` quantity kinds, verdict quantities, table column types). An operand of unknown kind or a bare number never conflicts: the check is exact where the declarations are, silent everywhere else. The kind rides the arithmetic and the kinded calls; the unit algebra is the evaluation plane's (<<embedded-expressions,8.6>>), never the checker's.
C178 `legacy-carrier-deprecated`: a package declaring the deprecation generation carries no quoted `ocl{…}` expression carriers — the inline blocks (<<limit-expressions,11.1.4>>) are the only forward form. The generation constant is inert until the release declares it; the ratchet activates on the bump.
C179 `uncertainty-budget-resolves`: an uncertainty budget targets a declared verdict; a Type A component names a declared captured series, a Type B component states its distribution and half-width; the coverage factor is a positive number. The propagation law is the evaluation plane's — the checker resolves the declaration, it never computes the uncertainty.

22. 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.

22.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 ?

22.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 | ExpressionCarrier | BracketList
ExpressionCarrier ::= ExpressionString | ExpressionBlock
ExpressionString  ::= ? a token starting with 'ocl{', re-accumulated until the
                   braces balance ? (* the legacy carrier; C178 deprecates *)
ExpressionBlock   ::= 'expression' Block | 'derive' Block | 'derivation' Block
                    | 'check' Block
(* The block's body is FIRST-CLASS EXPRESSION SYNTAX (<<expression-language>>):
   the precedence grammar below, harvested by the checker's identifier
   resolution — never a quoted string. The grammar applies no expression
   tokenization to the block: the body is the raw span the expression
   parser reads. *)
BracketList  ::= ? a token starting with '[', re-accumulated until the
                   accumulated text contains ']' ?

22.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. The LML-aligned revision adds the description block, the mapping block, the member-fields block, the string-format block, and the namespace block:

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 *)
DescriptionBlock ::= 'description' Block
            (* THE PROSE QUARANTINE (C158): human-language text appears
               only here, or in a label facet. Every other string field
               of every construct is useable data — a name, a unit, a
               code, an expression, or a urn. The deletion test: a
               package with all description blocks removed parses,
               checks, links, runs, and attests identically. *)
ExpressionCarrier ::= CarrierKeyword Block
CarrierKeyword ::= 'expression' | 'derive' | 'derivation' | 'check'
                 | 'args'
            (* the v3.3 expression carriers (11.1.4): inside any
               construct's item stream, a carrier keyword followed by a
               block is the key's block value — the explicit exception
               to the construct reservation; a carrier keyword followed
               by a bare token still heads a construct *)
MappingBlock   ::= 'mapping' Format Identifier Block
Format         ::= 'xml' | 'yaml' | 'json' | 'toml' | 'csv'
            (* the declared wire form of one class for one format;
               clause 16. The block's facets are the LML surface:
               element, namespace, map_element, map_attribute,
               map_content, mixed_content, map, map_key, map_value,
               render_nil. *)
MemberFieldsBlock ::= 'member_fields' '{' { FieldDecl } '}'
            (* the typed payload schema shared by an enum's members;
               clause 10.14 *)
StringFormatBlock ::= 'string_format' ':' BareToken Block
            (* the grammar-backed string form of a class: artifact +
               root; clause 10.15 *)
NamespaceBlock ::= 'namespace' Identifier Block
            (* uri + prefix (+ optional default marker); clause 9.6 *)

EOF

22.4. The expression grammar

The inline carriers' body is the language's own expression syntax — a precedence grammar over the tokens of lexical-grammar , one expression per carrier, harvested by the checker's identifier resolution ( 16 ):

Expression   ::= ImpliesExpr
ImpliesExpr  ::= OrExpr { 'implies' OrExpr }
OrExpr       ::= XorExpr { 'or' XorExpr }
XorExpr      ::= AndExpr { 'xor' AndExpr }
AndExpr      ::= CompareExpr { 'and' CompareExpr }
CompareExpr  ::= AddExpr { ('=' | '!=' | '≠' | '<' | '>' | '<=' | '>=') AddExpr }
AddExpr      ::= MulExpr { ('+' | '-') MulExpr }
MulExpr      ::= Unary { ('*' | '/') Unary }
Unary        ::= [ 'not' | '-' ] Primary
Primary      ::= Number | String | 'true' | 'false'
               | '(' Expression ')'
               | PathOrCall
               | Navigation
               | Conditional
PathOrCall   ::= DottedPath [ '(' [ Expression { ',' Expression } ] ')' ]
DottedPath   ::= BareToken { '.' BareToken }
Navigation   ::= DottedPath '->' Iterator '(' BareToken '|' Expression ')'
Iterator     ::= 'forAll' | 'exists' | 'select' | 'collect'
Conditional  ::= 'if' Expression 'then' Expression 'else' Expression 'endif'

The precedence is the listing's order: implies binds loosest, the arithmetic tightest; every binary level is left-associative. The conditional's endif delimits, so a branch needs no parentheses at any precedence — a branch may itself be a conditional. The navigation's lambda parameter scopes its body exactly like a limit's args binding ( 11.1.4 ). The iterators' values: forAll and exists yield booleans; select yields a subcollection of its source; collect projects — a collection of the body's element kind. The boolean literals are kindless leaves. The same grammar is implemented a second time, independently, by the conformance suite's second checker (conformance/checker2-rules.json): the two implementations agreeing is the grammar's conformance proof.

22.5. 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.

23. Dataspaces, policies, trust, and correspondence

23.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.

23.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).

23.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 ).

23.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).

23.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).

23.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.

24. The attestation

An attestation states that a third party has verified a subject against a declared promise set. The construct exists because the certificate's substance is exactly that claim, and no other construct of the language carries it: an artifact projection records output, a verdict records a decision, and neither names the verifying authority, the evidence basis, and the attested promise set together.

24.1. The attestation construct

attestation r60-certificate-claim {
  subject    ssm-ssb7
  promises   LoadCell.promise_set
  basis      test_report "NMi-3826124-01"
  authority  IssuingAuthority "NMi Certin B.V."
  statement  "This Certificate attests the conformity of the above
              identified Type with the requirements of OIML R 60-1:2021"
  limits     { scope   "the metrological and technical characteristics of
                        the type"
               legal    "does not bestow any form of legal international
                        approval"
               quotation "partial quotation is not permitted; reproduction
                          in full is permitted" }
}

The facets are:

  • subject (exactly one) — the certified instance, an instance id of the subject chain. The attestation never restates the instance's values; it references them.
  • promises (exactly one) — the attested promise set, by reference in Subject.promise_set_id form. The attestation re-uses the promise set; it never restates a promise.
  • basis (0..*) — the evidence the attestation rests on: an evidence kind (a report kind, an evaluation report, an audit) and the evidence's identifier. Every basis entry is a typed reference the checker resolves (C150).
  • authority (exactly one) — the verifying party: a role reference (the package's role register) and the authority's name as stated.
  • statement (exactly one) — the attestation's own wording, verbatim.
  • limits (0..1) — the attestation's stated limits as labelled texts. The labels are free vocabulary of the attestation itself; the checker requires a label and a text per entry (C150).

24.2. The certificate as the attestation's document form

The certificate is the document form of an attestation plus the actors and the register lifecycle. The language models the document surface with the existing artifact constructs: the certificate's print projection is an artifact_definition whose content contract is derived from the attested promise set, and one issued certificate is an artifact_instance of that definition whose entries carry per-entry provenance . The certificate's behaviors (attest, revise, withdraw) are the attesting subject's declared behaviors, and the register's snapshot semantics are the withdrawal behavior's input: the certificate instance carries the register's status and year as provenance-attributed facts.

The certificate_template construct ( 15.x ) remains the print projection's rendering contract: its characteristic rows resolve by name against the attested promise set, and its obligation vocabulary (mandatory rows carry values; conditional rows print only when declared) is a closed register.

24.3. The data lineage: declared, validated, carried forward

A value whose status changes across the modelling lifecycle keeps its lineage as typed references (the kernel's lineage rule). The certificate's characteristics are the worked example: the applicant declares the characteristics in the application, the promise set formalizes what evaluation verifies, the procedures produce the evidence, the verdicts record what held, and the attestation carries the same data as validated claims. Nothing is re-entered, re-extracted, or re-typed between the stages.

The lineage has three stages:

  1. Declaration — the applicant's sought classification, entered in the application, with attribute origin declared.
  2. Validation — the promise set's verified_by links name the requirements, conformance tests, and procedures; the evidence is produced; the verdicts record what held.
  3. Carry-forward — the attestation's claim instantiations reference the application's declared values and the evaluation's verdicts.

The carry-forward stage is declared per promise with a claims block. Each claim names the promise, the declaration instance whose value is carried, and the verdict that validated it:

attestation r60-certificate-claim {
  subject    ssm-ssb7
  promises   LoadCell.promise_set
  basis      test_report "NMi-3826124-01"
  authority  IssuingAuthority "NMi Certin B.V."
  statement  "This Certificate attests the conformity of the above
              identified Type with the requirements of OIML R 60-1:2021"
  claims {
    accuracy_class {
      declared     app-flintec-2025-0314.characteristics
      validated_by /verdicts/mpe
    }
    e_max {
      declared     app-flintec-2025-0314.characteristics
      validated_by /verdicts/mpe
    }
  }
}

The checker requires every claim's declaration reference and verdict reference to resolve (C150), and requires a claim per promise the certificate prints as mandatory (C151): a mandatory characteristic on the certificate must be carried by a lineage, not restated.

For legacy certificates whose only source is a scanned document, the document is the source and per-entry provenance applies: each carried value names the document and the page it was read from. A born-digital certificate threads the lineage above; a legacy certificate carries document provenance; both are the same attestation over the same promise set.

25. Platform modules

This clause specifies the platform-module constructs: the registers and contracts the SMART platform's methodology defines and that any programme on the platform may use. Each construct below was first carried by the reference implementation; this clause is its normative home (the layering contract of Annex A states that the kernel knows nothing about legal metrology, and each of these constructs is motivated by a standard's structure in general, not by one programme's document). The constructs form one lineage: the application declaration carries the certificate's declaration stage, the test-report and evaluation modules carry the validation stage, and the certificate module prints validated claims by reference ( clause 19 ).

25.1. promise_set — the promise register

A promise set is a subject's manufacturer-claim register: first-class claims on characteristics and behaviors — possibly envelope-shaped, possibly conditional — that evaluation verifies and the certificate prints as promises-as-verified.

promise_set LoadCell {
  promise accuracy_class {
    target accuracy_class
    statement "The type holds its declared accuracy class."
    verified_by { /req/metrological/mpe /conf/metrological-tests/measurement-error }
    ref derives-from "urn:oiml:pub:r:60-1:2021#clause-5.1.1"
    certificate { dimension accuracy_class type string label "Accuracy class" obligation mandatory }
  }
}

The promise facets are those of the subject's own promise entries ( clause 10 ): target , level , conditions , statement , verified_by , source , and the certificate print projection. The certificate projection's obligation vocabulary ( mandatory , optional ) is parse-enforced; its characteristic type vocabulary and every reference resolution are check-enforced (C131, C42–C44). A promise set is referenced by an attestation ( clause 19 ) and by an artifact definition's contract_from facet (C153); the certificate's characteristic list generates from the set, never as a restatement.

25.2. identity_slot — the marking register

An identity slot is one marking of the instrument's documentary identity: the name or trade mark, the type designation, the serial number, the year of production, the approval mark. Each slot declares its label, its type, its presentation surfaces ( marked-on-instrument , accompanying-document ), its definition, its provenance, and whether it is optional. The register feeds the markings examination's evidence surface: the examiner records what the sample carries against the declared slots.

25.3. application_declaration — the application register

An application declaration is the applicant-facing register of an application for type evaluation: the declaration form the applicant signs and the documentation checklist the application must carry. Each document entry declares a name, a description, an obligation from the closed register ( shall , should , may — parse-enforced), and its provenance. The register carries the certificate lineage's declaration stage: the instrument characteristics the applicant declares are data, and the attestation's claims reference them ( clause 19 ).

25.4. common_test_condition — the shared test condition register

A common test condition is one condition that recurs across test procedures: the test equipment's calibration and traceability, the ambient conditions' stability, the loading discipline, the measuring range limits. Each entry declares a title, a description, and a reference to the clause that grounds it. A procedure's steps reference these entries instead of restating them; the entries are the shared vocabulary of the condition anatomy ( clause 10 ).

25.5. test_report_skeleton — the evidence contract

A test report skeleton names the sections of a test report and binds each section to the forms that fill it. Each section declares a number, a title, a description, and its form entries; each form entry names the form construct, the file that authors it, its title, and when it is required. The skeleton is the evidence contract the laboratory procedures fill (the separation of evidence from judgment, clause 11 ): its sections carry typed content with provenance, and a step that records a measurement never records the verdict on a requirement.

25.6. test_report_checklist — the report completeness register

A test report checklist states the checks a compiled test report must pass before it is submitted: one entry per check, with its question and its binding. The checklist is the report skeleton's completeness rule as data, and the coverage machinery reports an unchecked entry rather than silently passing it.

25.7. certificate_template — the print projection contract

A certificate template is the rendering contract of the certificate document: the number format of the certificate's identifier, the label pattern of the classification designation, and the rendering of additional sections. The characteristic list is NOT restated here — it generates from the attested promise set (each promise's certificate print projection). The label pattern's placeholders resolve against the declared classification dimensions (C136).

25.8. aspect — the aspect register

An aspect is a named facet of the instrument's documentary surface: the markings, the accompanying document, the application documentation, the sealing, the software, the construction. Each aspect declares its kind, its definition, the identity slots or attributes it contains, and its provenance. Aspects are the qualitative counterpart of the attribute register: an aspect states what must be marked, documented, or sealed; an attribute states the typed value a characteristic carries.

25.9. evaluation_profile — the evaluation preset register

An evaluation profile is a named combination of classification dimension values that selects the applicable requirements, tests, and calculations: one profile per evaluation preset. Each entry declares its dimension bindings and a description. The profiles are the evaluation-side applicability axis (the model-side axis is the dimension itself, clause 10 ).

25.10. evaluation_dimensions — the evaluation axis register

An evaluation dimensions register declares the classification axes the evaluation keys on and the bindings each axis resolves through, per channel of the evaluation. It is the evaluation profile's resolution domain: a profile's dimension values resolve through this register to the evaluation's applicability decisions.

25.11. sample_selection_rule — the specimen selection register

A sample selection rule is one rule of the specimen-selection discipline: the step it realizes, the rule's own wording, its rationale, its applicability, its selector expression, and the test kind it triggers. The register states the family/group selection discipline as data; the procedure references the rules instead of restating them.

25.12. lab_selection_criterion — the laboratory matching register

A laboratory selection criterion is one parameter-to-capability match rule the test-design linker resolves: its weight ( required , preferred ), the parameter type, its label and description, the applicability condition, and the match operator with its required capability. The register is how a dispatching process chooses between accredited laboratories — data, not prose.

25.13. specimen_governance_rule — the specimen governance register

A specimen governance rule states one rule of the specimen's custody during evaluation: receipt, identification, storage, return. Each entry declares its weight, its label, its description, and its provenance.

25.14. calculation_context — the evaluation binding register

A calculation context declares the variable bindings the evaluation's calculations resolve through: each field names a computation input and the source it reads from — a classification dimension, a parameter, or a computed expression. A computed source carries its expression facet; every free identifier of the expression is checked (C133), and the closed-derivation rule (C149) applies to the expressions the context declares.

25.15. part_annex — the annex accountability register

A part annex is one annex of a multi-part publication, accounted for: its letter, its title, its obligation ( normative or informative ), its summary, its realization (how the model realizes the annex's content), and its provenance. The register is the coverage machinery's checklist for annex content: every annex is modelled or excluded with a stated reason.

25.16. formula_note — the formula annotation register

A formula note is an annotation on one evaluation formula: the condition it applies to, its note text, and its provenance. The register carries the R 60-3 worked-formula annotations as data beside the symbols they annotate.

25.17. framework_document — the governing-document register

A framework document is one document of a certification framework's governing hierarchy: its rank in the hierarchy, its document designation, its title, the governance organ that approves it, and the clause of the framework publication that grounds it. The register states the hierarchy as data; the precedence rule ( 20.19 ) keys on it.

25.18. document_precedence — the precedence rule register

A document precedence entry states the conflict rule of a governing hierarchy: which document prevails when the provisions of two conflict. One entry per rule, with its clause and provenance; the hierarchy of 20.17 is what the rule orders.

25.19. document_module — the document namespace register

A document module names one publication of a framework's document series and claims the requirement namespace its provisions own: the document designation, its title, edition, year, and the namespace pin. The pin is what the composition machinery enforces ( clause 16 's namespace discipline): a downstream package references the module's provisions but never declares requirements under its owned namespace.

25.20. declaration_kind — the declaration register

A declaration kind is one kind of formal declaration a scheme's participants sign: its label, the participant kind that holds it, the clause and procedure that ground it, and its definition. The declaration machinery of the scheme (the signing gate of 20.22 ) keys on the register.

25.21. declaration_gate — the signing-gate register

A declaration gate states the scheme's signing invariant as data: the participant kind that must hold a signed declaration, the declaration kind required, the grounding clause, the statement, and the abstract processes the gate blocks until the declaration is signed. The gate is the governance constraint the process model references instead of restating.

25.22. governance_organ — the governance register

A governance organ is one organ of a scheme's governance: its label, its clause, and its mandate — the decisions it takes, stated as data. The decision rules of 20.29 name the organ that takes them.

25.23. participant_kind — the participation register

A participant kind is one kind of scheme participant: its label, its terminology anchor, the clause that defines it, its definition, the member kind it belongs to, and its competence requirements. The participation pipeline's processes and the declaration register key on the kinds.

25.24. scheme_definition — the scheme register

A scheme definition is one certification scheme of a framework: its label, its terminology anchor, its clause, its definition, and its demonstration method. The scheme lifecycle ( 20.25 ) moves a category between the registered schemes.

25.25. scheme_lifecycle — the lifecycle register

A scheme lifecycle states how an instrument category moves through the schemes: the category it applies to, the initial scheme, and the entry, transition, and withdrawal actions with their clauses and their automatic or decided character. The lifecycle is data; the governance organ that decides a non-automatic transition names it.

25.26. scheme_type — the certification-scheme-type register

A scheme type is one product-certification scheme type of ISO/IEC 17067: its label, clause, description, the sampling statement, the attestation object, and the determination activities it comprises. The register is the vocabulary a certification programme's self-classification declares.

25.27. scheme_activity_kind — the activity register

A scheme activity kind is one activity family of ISO/IEC 17067's activity grid (selection, determination, surveillance and attestation activities): its family, its grid row, and its label. The scheme types' determination blocks reference the kinds instead of restating them.

25.28. decision_rule — the governance-decision register

A decision rule states one decision of the scheme's governance as data: the organ that takes it, the decision kind, the organ whose recommendation it acts on, and the grounding clause. The register is the governance processes' decision vocabulary.

25.29. verification_pathway — the verification register

A verification pathway is one pathway of a Recommendation's verification regime (initial, in-service, special): its kind, label, description, the visual-inspection discipline, and the conformance tests it comprises. The register is how the regime's pathways are data; a procedure references the pathway it realizes.

25.30. informative_annex — the informative annex register

An informative annex is one informative annex of a framework document, accounted for like a normative annex: the document it belongs to, its letter, its title, its summary, and its provenance. The register is the coverage machinery's checklist for informative annex content: every annex is modelled or excluded with a stated reason.

26. 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.

The annex is generated from the parser configuration by the parity tooling of the kernel repository ( packages/primmel/scripts/annex-a.mts of primmel-ts, --check in continuous integration); it is never hand-edited, and a hand edit is a defect in this annex.

During the reconfiguration whose plan of record is the Primmel reconfiguration roadmap, a construction freeze applies to the parser: no new construct joins the parser configuration. A construct lands only together with the specification section this annex points at, in one change, with the parity check green in both repositories' continuous integration; the unregistered constructs carry their triage dispositions in the kernel's visible registry-triage.prl list, and the list only shrinks.

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.

26.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>>).

26.2. The construct registry

Keyword Aliases Collection Since Clause Basis (mode)
activity_archetype activity archetypes v3 12 extension: The OIML-CS framework (clause 20)
application_declaration applicationDeclarations v3.3 20.3 extension: The scheme application documents
approval approvals v2 12 inherit: MMEL approval node
artifact_definition artifact definitions v3 10 composition: Rho of a produced output-kind
artifact_instance artifact instances v3 10 composition: A produced V-bundle
aspect aspects v3.3 20.8 composition: Named HAS grouping
attestation attestations v3.3 19 extension: The certificate lineage
attribute_definition attribute definitions v2 10 extension: The evidence discipline over properties
behavior behaviors v2 10 composition: Named DOES grouping
calculation calculations v2 13 composition: T over values
calculation_context calculationContexts v3.3 20.14 extension: Clause 20 register
canvas subprocess pages v2 12 inherit: MMEL diagram
capability capabilities v2 10 composition: Named IS grouping
certificate_template certificateTemplates v3.3 20.7 extension: The print projection contract
check checks v3.3 11.10 composition: Evaluate-and-compare — the consolidated surface forms over one construct
class data classes v2 13 inherit: LutaML class / MMEL data class
comment comments v2 13 composition: Attached prose
common_test_condition commonTestConditions v3.3 20.4 extension: Clause 20 register
competence_kind competence kinds v2 11 extension: The OIML-CS framework (clause 20)
conformance_class conformance classes v2 11 composition: Provision grouping
conformance_test conformance tests v2 11 inherit: MMEL measurement validation test
connector_profile connector profiles v3 13 composition: External interface binding to P/V
constraint constraints v2 11 composition: Predicate over P/V
data_registry registries v2 13 inherit: MMEL registry
dataspace dataspaces v3.1 19 composition: Guard over rho(T)
datum datums v3.3 15.4 composition: The duality's registry face — one typed, unit-bearing declared value
decision_rule decisionRules v3.3 20.30 extension: The OIML-CS framework (clause 20)
declaration_gate declarationGates v3.3 20.23 extension: The OIML-CS framework (clause 20)
declaration_kind declarationKinds v3.3 20.21 extension: The OIML-CS framework (clause 20)
dimension dimensions v3.2 10 composition: P with closed V-set (applicability)
discrepancy_record discrepancy records v2 14 composition: A fact about a fact
document_module documentModules v3.3 20.20 extension: The OIML-CS framework (clause 20)
document_precedence documentPrecedences v3.3 20.19 extension: The edition lineage (which document governs)
dual duals v3 13 extension: Designed/exhibited duality
uncertainty_budget uncertainty budgets v3.3 11.9a extension: The GUM plane's declaration — the components and the coverage, never the arithmetic
end end_event events v2 12 inherit: MMEL end event
enum enums v2 13 inherit: LutaML enum
evaluation_dimensions evaluationDimensions v3.3 20.10 extension: Clause 20 register
evaluation_profile evaluationProfiles v3.3 20.9 extension: Clause 20 register
exclusive_gateway gateways v2 12 inherit: MMEL gateway node
figure figures v2 13 inherit: MMEL multimedia
form forms v2 11 composition: MMEL form (element semantics)
formula_note formulaNotes v3.3 20.16 extension: Clause 20 register
framework_document frameworkDocuments v3.3 20.17 extension: The OIML-CS framework (clause 20)
governance_organ governanceOrgans v3.3 20.24 extension: The OIML-CS framework (clause 20)
identity_slot identitySlots v3.3 20.2 extension: Scheme identity properties
informative_annex informativeAnnexes v3.3 20.32 extension: The OIML-CS framework (clause 20)
instance instances v3 10 composition: O with HAS (LutaML assignment form)
instrument instruments v2 10 composition: Subject (v2 spelling)
invariant invariants v3 13 composition: Always-holds predicate
lab_selection_criterion labSelectionCriteria v3.3 20.12 extension: Clause 20 register
link links v2 13 inherit: MMEL link
map_profile map profiles v2 12 inherit: MMEL mapping
mapping wireMappings v3.2 19.6 composition: The LML mapping surface — one class's declared wire form per format
measurement variables v2 13 composition: V with provenance
monitor monitors v3 13 composition: Standing predicate over a trajectory
namespace namespaces v3.2 9.6 composition: The LML namespacing surface — named uri + prefix pairs
note notes v2 13 inherit: MMEL note
part_annex partAnnexes v3.3 20.15 extension: The OIML-CS framework (clause 20)
participant_kind participantKinds v3.3 20.25 extension: The OIML-CS framework (clause 20)
passport passports v3 13 composition: The attestation bundle projection
policy policies v3.1 19 composition: Guard rules over rho(T)
predicate predicates v2 14 composition: Predicate
process processes v2 12 inherit: MMEL process node
promise_set promiseSets v3.3 20.1 extension: The claim set the certificate projects
provision provisions v2 11 inherit: MMEL provision
quantity_register quantity registers v3 13 extension: The quantity type system
reference_material reference materials v2 13 inherit: MMEL reference / table (the reference-material carrier)
requirement requirements v2 11 composition: MMEL provision + scope
requirement_class requirement classes v2 11 composition: MMEL provision scope
role roles v2 12 inherit: MMEL role
sample_selection_rule sampleSelectionRules v3.3 20.11 extension: Clause 20 register
scheme_activity_kind schemeActivityKinds v3.3 20.29 extension: The OIML-CS framework (clause 20)
scheme_definition schemeDefinitions v3.3 20.26 extension: The OIML-CS framework (clause 20)
scheme_lifecycle schemeLifecycles v3.3 20.27 extension: The OIML-CS framework (clause 20)
scheme_type schemeTypes v3.3 20.28 extension: The OIML-CS framework (clause 20)
signalcatch signal_catch_event events v2 12 inherit: MMEL signal event (docs: deprecation candidate)
specimen_governance_rule specimenGovernanceRules v3.3 20.13 extension: Clause 20 register
start start_event events v2 12 inherit: MMEL start event
state_machine state machines v2 12 composition: T-decomposition of dynamics
string_format stringFormats v3.2 20 composition: The grammar-backed string form of one artifact class
subform subforms v2 11 composition: MMEL subform
subject subjects v3 10 inherit: IS/HAS/DOES over O
symbol symbols v2 13 composition: P with unit/kind
table tables v2 13 inherit: MMEL table
term terms v2 13 inherit: MMEL term
test_point_set test point sets v2 13 composition: V-set of operating points
test_report_checklist testReportChecklists v3.3 20.6 extension: Clause 20 register
test_report_skeleton testReportSkeletons v3.3 20.5 extension: Clause 20 register
test_sequence test sequences v3 13 composition: Rho(T)-ordering
text texts v3 15 inherit: MMEL section
timer timer_event events v2 12 inherit: MMEL timer event
variable variables v2 13 inherit: MMEL measurement variable
verdict verdicts v2 11 composition: Re-runnable function over facts
verification_pathway verificationPathways v3.3 20.31 extension: Clause 20 register
view_profile view view profiles v2 12 inherit: MMEL view profile

26.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.

27. 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.21.19), 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 C182, with reserved gaps; 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).

27.1. base

Rule Name Default severity Level
C1 attribute-bind-scope normal error
C2 reference-targets-resolve normal error
C3 dimension-enums normal error
C4 store-uniqueness normal error
C5 req-test-coverage normal warning
C56 allowlist-malformed normal error
C57 allowlist-stale normal error
C158 prose-quarantine normal error
C159 urn-only-refs normal error
C160 signature-closure normal error
C161 check-surface normal error
C162 namespace-coherence normal error
C163 change-levels normal error
C164 wire-mapping-coverage normal error
C169 provenance-clause-resolves normal error
C170 edition-lineage-declared normal error
C171 expression-identifiers-resolve normal error
C172 requirement-algebra-resolves normal error
C176 language-declared-wellformed normal error
C179 uncertainty-budget-resolves normal error
C177 kind-coherence normal error
C178 legacy-carrier-deprecated normal warning
C124 pair-list-shape normal error
C125 formula-variant-shape normal error
C127 common-test-condition-shape normal error
C128 part-annex-shape normal error
C89 spelling-code-wellformed normal error
C90 invariant-shape normal error
C91 invariant-enforcement-grammar normal error
C92 test-sequence-shape normal error
C93 test-sequence-integrity normal error
C147 test-program-shape normal error
C148 test-program-duration normal error
C154 construct-deprecated normal warning
C94 formulas-used-shape normal error
C96 duplicate-id normal error
C97 program-maps-resolves normal error
C98 program-surveillance-required normal warning
C103 declared-predicate normal error
C110 term-alias-shape normal error
C111 dimension-shape normal error
C118 requirement-parameter-shape normal error

27.2. anatomy

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

27.3. process

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

27.4. instantiation

Rule Name Default severity Level
C17 instance-scope normal error
C18 instance-version-pin normal error
C19 chain-acyclic normal error
C20 instance-of-resolves normal error
C155 power-type-chain normal error
C156 class-extends-resolves normal warning
C157 registry-dataclass-resolves normal error

27.5. mapping

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

27.6. composition

Rule Name Default severity Level
C27 uses-resolves normal error
C28 uses-no-redefine normal error
C29 uses-cycle normal error
C30 provides-consumed-or-waived normal warning
C31 requires-satisfied normal error
C119 namespace-pin-violation normal error
C120 framework-references-resolve normal error
C122 scheme-type-resolves normal error
C123 document-module-references-resolve normal error
C126 formula-note-targets-resolve normal error
C130 identity-and-aspect-references normal error
C131 promise-certificate-projection normal error
C132 application-declaration-references normal error
C133 calculation-context-references normal error
C134 evaluation-dimension-references normal error
C135 evaluation-profile-coherence normal error
C136 certificate-template-references normal error
C137 workflow-config-references normal error
C138 verification-pathway-references normal error
C139 selection-rule-references normal error
C140 test-report-skeleton-references normal error
C141 checklist-entry-shape normal error
C142 gateway-edges-resolve normal error
C143 approval-references-resolve normal error

27.7. quantities

Rule Name Default severity Level
C165 math-symbols-declared normal warning
C166 math-parses normal warning
C167 qualified-unit-reference normal error
C32 inv1-no-bare-quantity normal error
C33 quantity-coherence normal error
C34 duality-coherence normal error
C35 time-format normal error
C36 map-type normal error
C149 closed-derivation-references-declared normal error
C152 payload-typed-by-class normal error
C114 limit-quantity-coherence normal error
C115 calculation-signature-shape normal error

27.8. state

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

27.9. promises

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

27.10. artifacts

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

27.11. characteristics

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

27.12. twins

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

27.13. coverage

Rule Name Default severity Level
C180 run-plane-shape normal error
C181 run-store-binds normal error
C182 series-record-fields normal error
C183 record-run-inputs normal error
C184 field-range-order normal error
C185 unitsdb-binding-coherent quantities error
C51 coverage-test-evidence audit warning
C52 coverage-form-judgment audit warning
C53 coverage-uses-bound normal error
C54 coverage-lookup-table-exists normal error
C55 coverage-budget audit error
C71 text-coverage-sentence-uncovered audit warning
C72 text-coverage-budget audit error
C73 text-coverage-config normal error

27.14. edition

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

27.15. dataspace

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

27.16. attestation

Rule Name Default severity Level
C150 attestation-references-resolve normal error
C151 attestation-claim-lineage normal error

27.17. supply chain

Rule Name Default severity Level
C81 product-maps-resolves normal error
C82 product-unmapped-promises normal warning
C83 abstract-import-pinned normal error
C144 license-key-shape normal error
C145 license-key-unique normal error
C146 license-holder-required normal error
C86 passport-content-resolves normal error
C87 passport-access-leak normal error
C88 passport-upi-scheme normal error

28. 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 ).

28.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.

28.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.

28.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.

28.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.

28.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.

28.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.

29. Annex D (informative): Lineage and compatibility

29.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)

29.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 }, the reference { doc clause } blocks, references { <role> { ... } }, specification_reference "...") are retired — they no longer parse ( 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.

29.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 ).

30. Annex F (normative): The LML alignment — the strict-superset proof

Primmel's data-modelling substrate IS the LutaML Model Language (LML, RS 3001 + RS 3010); Primmel strictly extends it with the metrology loop's higher constructs. This annex is the proof table: every LML construct, its Primmel counterpart, and its status. A status of declined carries its reason and is final until this annex is revised. The registry's Basis column ( Annex A ) carries the same alignment per construct.

LML construct LML ref Primmel counterpart Status
package / models scope lang §Package package ( 9 ) aligned (v2)
class lang §Class class aligned (v2; inherit)
attribute (+ cardinality) lang §Attribute datum ( 10.2 ) unified this revision (was attribute definition/identity slot/parameter/field)
attribute default + nil policy decl §Attribute defaults datum facets default , render_nil adopted this revision
instance-type override isa decl §isa instance of + declared override adopted this revision (the of slot accepts an override entry)
value types lang §Value value_types plane aligned (v3)
enums lang §Enums enum aligned (v2; inherit)
enum member payloads member_fields decl §Enum member payloads enum member_fields + inline member payloads adopted this revision
table-driven enums from_table decl §Table-driven enums enum from_table (compile-time expansion from a grammar artifact's tables) adopted this revision
derived fields derive decl §Derived fields DERIVED variables + calculation binding aligned (the computation is a declared calculation — Primmel binds it in-language)
grammar-backed string formats string_format decl §String formats string_format (thin artifact reference) adopted this revision
serialization mappings (xml) lang §Serialization mapping xml ( 16.2 ) adopted this revision (compiles to the kernel profile)
serialization mappings (key-value) decl §Mapping blocks mapping yaml/json/toml/csv with map / map_key / map_value adopted this revision
namespaces (uri + prefix) lang §Namespacing namespace blocks ( 9.6 ) adopted this revision
external file inclusion lang §Reuse the include directive ( 16.2 ) + package merge aligned (v2)
internal references lang §Reuse urn references + LutaML Path (below) aligned + extended this revision
LutaML Path expressions lutaml-path path expressions over the package (intra-model addressing; urns remain the external citation address) adopted this revision
model register model-register the package register (registration, model paths, registrable classes, mixed-version composition rules) adopted this revision
model/value transforms model-transform the declared mapping artifact (source/target packages, accepted pairs, value transforms) adopted this revision
model collections model-collection package composition ( uses / requires ) + collections aligned (v2)
source-declared versions + change levels model-evolution package version (in source) + change-level rules over model-diff ( C163 ) adopted this revision
validation rules lang §Validation the checker ( clause 17 ; Annex B) exceeds — Primmel adds provisions, coverage, composition, the prose quarantine
operations (UML round-trip metadata) decl §Operations — DECLINED: Primmel models subjects-and-loops, not class diagrams; XMI-sourced behavior converts to behaviors/processes at adoption time. Revisit only if a corpus source demands the round-trip.

The higher constructs — subjects, promises, requirements, processes with step signatures, conformance tests and runs, evidence, forms, attestation — have NO LML counterpart: they are the strictly-greater side of the relationship ( clauses 11 to 20 ).

31. Bibliography