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.
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.
This document specifies the Primmel modelling language, version 3. It specifies:
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.
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.
For the purposes of this document, the following terms and definitions apply.
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.
named, typed slot of a subject, belonging to exactly one aspect family (IS, HAS, or DOES)
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
).
one directory holding a Primmel model: a
package.primmel
manifest plus
content files merged by convention (
16.3
)
classification of a package declared in its manifest:
core
,
module
,
rec
,
product_reference
, or
certification_program
model published by a standards publisher that states what a conforming subject is, has, and does
model published by an adopting organization, related to a reference model by a declared mapping
element-by-element declared alignment of an implementation model to a reference model, with a coverage calculus
constraint over the aspect paths of a subject, carrying a statement, an optional formal limit, modality, and provenance
operation on a subject that constrains inputs, environmental context, and state, and observes outcomes against acceptance criteria
named, reusable derivation of a judged quantity from test inputs, referenced by requirements, tests, and forms instead of restated
model element declaring one member of a subject chain, with its own exhibited values and its upward chain links
declared relation kind usable in a
ref
triple; the predicate registry of a
package is data, and an undeclared predicate is an error (C103)
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
one of the five dependency levels of the language: foundations, primary, secondary, tertiary, cross-cutting ( 7.2 )
a dated state of a package in its lifecycle, with a status and a validity window, registered in the package manifest
the per-package
.primmel-allowlist.prl
file that marks known checker
findings as KNOWN debt and fails on STALE entries (
17.4
)
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 )
the single emission spelling a conforming serializer produces for a model, such that loading and re-serializing is a fixed point ( 16.5 )
model object declaring one dataspace: its participant classes, artifact classes, policy sets, trust anchors, compatibility register, and governance citations ( 19.1 )
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 )
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 )
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
)
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
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
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
)
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
)
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
)
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
)
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 )
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.
This document defines five classes of conformance:
A document is a conforming Primmel v3 document if and only if:
A conforming parser:
A conforming parser SHOULD attach source positions (line and column) to its errors and issues, as the reference implementation does.
A conforming serializer (a
dump
implementation):
A conforming checker:
A conforming checker SHOULD print its findings with rule identifiers from Annex B , so that allowlist entries and audit records can cite them.
A package is a conforming Primmel v3 package if and only if:
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.
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:
Primmel's semantics is not invented here and not inherited whole from one language. It has three layers, in this order.
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.
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.
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.
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:
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.
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).
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) |
PackageKind
in
packages/primmel/src/types/Package.ts
).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. |
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.
A package composes other packages structurally with
uses
(the deprecated
single
extends
is read as a one-element
uses
). Composition semantics:
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.
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.
Three token forms exist:
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.
A facet value is written in one of four forms:
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
.
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
.
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.
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.
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 ).
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.
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.
Two parse modes exist:
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.
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:
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).
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.
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).
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.
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.
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.
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>
.
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).
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.
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).
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).
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).
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).
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>>). |
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:
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.
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
).
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:
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
.
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.
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).
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.
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).
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:
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.
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
).
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).
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.
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.
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.
invalid
rides the precondition doctrine (the violated precondition
names itself).
aborted
rides the ABORT TRANSITION (spec#56), which
the language owns end to end:
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).
"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:
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.
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
).
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.
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
).
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
).
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:
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.
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).
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).
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.
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).
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.
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 { ... }
.
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).
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"
}
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
).
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.
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.
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.
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.
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).
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
).
The time value types are texts in ISO 8601 formats, validated with real calendar arithmetic (C35):
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.
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.
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.
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.
Four documentation constructs round out the registry:
note
(a typed
annotation;
type
is a closed vocabulary, parse-enforced:
NOTE
,
CAUTION
,
WARNING
,
EXAMPLE
,
COMMENTARY
; facets
message
,
reference
, and
source_discrepancy
),
link
(a cross-model or external
pointer;
kind
is
REPO
or
URL
, parse-enforced, with
target
,
url
,
path
,
namespace
),
figure
(
src
,
title
), and
comment
(a review
comment:
author
,
message
,
on
,
reply_to
,
resolved
,
timestamp
).
Three assurance constructs carry the architecture-level records: an
invariant
declares a named platform invariant with
statement
,
severity
,
applies_to { ... }
, and
enforcement
(a claims list, or the
bare marker
aspirational
; the grammar and the marker discipline are
checker-enforced, C90, C91). A
test_sequence
declares a required test
ordering: step entries
step <n> { test <ref> role <r> depends_on <m> }
with the step role (
baseline
or
follow_up
),
sample_applicability
,
and provenance (shape and integrity: C92, C93).
A
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.
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).
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).
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:
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.
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.
The citation kinds fold into the legacy channels at parse, so consumers read one model regardless of spelling:
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.
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.
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:
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.
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.
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.
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.
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:
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.
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
).
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:
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.
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:
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:
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.
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:
[fmt]]
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.
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).
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.
Three invocation levels exist:
The levels combine:
--strict --audit
is the strongest discipline. The
checker exits non-zero when any error-severity finding survives.
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)? |
A package carries its known debt explicitly in
.primmel-allowlist.prl
.
An entry is KNOWN or STALE:
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.
A construct retires through a NORMATIVE lifecycle (spec#58; the condition_set precedent, retired at 1.21.20, is the worked example):
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.
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
.
Two further toolchain operations are specified here informatively, because they consume but do not change the language:
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 ).
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).
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 |
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:
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.
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. |
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.
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 ?
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 ']' ?
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.
Three judgments are deliberately not grammar:
Primmel v3.1 adds the dataspace to the language as model content. Four constructs carry it:
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.
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).
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
).
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:
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>" [ { ... } ]
:
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).
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.
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.
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:
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.
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:
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.
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 ).
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.
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.
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
).
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 ).
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.
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.
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).
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.
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 ).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
| 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>>). |
| 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 |
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).
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C42 | promise-target-resolves
|
normal | error |
| C43 | promise-verifiable
|
normal | warning |
| C44 | promise-not-bare-value
|
normal | error |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C150 | attestation-references-resolve
|
normal | error |
| C151 | attestation-claim-lineage
|
normal | error |
| 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 |
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
).
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.
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.
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.
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.
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.
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.
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) |
v3 is a strict extension of v2 at the parse level:
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.
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
).
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 ).
8.2. Comments
Two comment openers exist, each running to the end of the line:
A comment opener is recognized only at a token boundary. A
#or//inside a token is literal text: the identifierStdS#Process5is 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.