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:
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:
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 is written with the
ocl{ ... }
marker:
expression "ocl{abs(c_m * t_f / delta_t)}"
conditions ocl{self.temperature >= rated.t_min}
The marker attaches to a brace group that is
not
at a token boundary, so
the tokenizer splits the payload at interior whitespace. A parser
recognizes a token starting with
ocl{
and re-accumulates tokens until the
braces balance; the accumulated text, single-spaced, is the expression.
Both the quoted spelling and the inline spelling parse to the same value.
The expression language inside the payload is not specified in this
document; it is bound to a dialect by the consuming program, and the
payload is opaque text to a conforming parser.
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). |
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.
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{...}"
source { doc "urn:oiml:pub:r:60-1:2021" clause "3.5.5" }
}
The facets are:
symbol
,
name
,
definition
,
quantity_kind
,
unit
,
value_type
,
origin
,
scope
,
category
,
is_dimension
,
enum
(bind
to an enumeration),
irdi
(the internationally registrable data
identifier),
derived
(a derivation expression),
source
/
ref
provenance, and
note
. The
origin
,
scope
, and
category
values are
program registers: the parser stores them as texts, and their closed
vocabularies (for example
design-fixed
versus
declared
for the IS/HAS
duality of
10.4
) are enforced by the consuming
program's linker, not by this specification.
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."
source { doc "urn:oiml:pub:r:60-1:2021" clause "3.7.1" }
verified_by { /conf/metrological-tests/creep-test }
}
The facets are
kind
,
stimulus
,
response
,
source
/
ref
provenance, and
verified_by
. A behavior is a process in the semantic
model (
12.1
): the declaration names it and states its
stimulus-response contract, and the process anatomy carries the executable
detail.
condition_set load-cell-reference {
role reference
entries {
temperature { value 20 unit degC tolerance 1 }
}
}
The
role
facet states the condition tier. The documented tier vocabulary
is
reference
,
rated
,
limiting
, and
actual
(the designed tiers are
IS content; the actual tier is HAS content,
10.4
); the
parser stores the role as text and the tier discipline is program-side.
Each entry of
entries { ... }
names a quantity with
value
,
unit
, and
tolerance
.
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 against the
manifest's edition register by C80), and
has { ... }
with
attributes
,
dimensions
, and
test_context
value maps. A value entry is
key : value [unit]
, or the quantity block form carrying the full value
contract:
e_max : { value 2.2 unit t kind mass uncertainty 0.001 tolerance 0.5 }
Instance scope discipline (which values a sample-level instance may carry) is a checker matter (C17), not a parse matter.
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_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 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 {
expression "ocl{...}"
uses { mpe accuracy_class p_lc }
modality should
relative_to reference_speed
notes "..."
accepts { verdict mdlo_normalized op lte limit "ocl{p_lc}" }
acceptance { rule guarded guard_band { kind NSFa value 0.5 } }
}
applicability { accuracy_class: [A, B, C, D] }
channel measurand_components
obligation should
acceptance_criteria { threshold { operator "<=" value "ocl{...}" } }
verification { method testing }
dependencies { /req/other }
ref derives-from "urn:oiml:pub:r:60-1:2021#clause-5.3.2"
}
The principal facets:
| Facet | Meaning |
|---|---|
statement |
The normative sentence (the text the committee owns). |
binds_to { ... } |
The aspect paths the requirement constrains. The anchoring rule (<<tier-system,7.2>>): a requirement binds subject facts, it never restates them. |
subjects { subject <n> { ... } } |
The per-subject apportioning of the constraint. |
parameters { param <name>: <type> { ... } } |
Typed parameters — the requirement's instance-parameter schema: `description`, `unit`, `default`, `range { min max }`, `enum_values`, and `bind` (the aspect path the parameter instantiates from; <<instance-parameters,11.1.3>>). |
limit { ... } |
The formal limit: `expression` (an embedded expression, <<embedded-expressions,8.6>>), `uses` (the free variables), `modality`, `relative_to`, `notes`, `quantity { kind unit }` (the derived quantity's typing; <<limit-quantities,11.1.2>>), `accepts` (the verdict binding: `verdict`, `op`, `limit`, an optional `source_discrepancy`), and `acceptance` (<<acceptance-decisions,11.5>>). |
applicability { ... } |
Dimension-keyed applicability entries; a value is a bare id, a quoted text, a bracket list (<<bracket-lists,8.7>>), or a parameter-mapping block. A `match` mode of `any`, `all`, or `exact` is a closed vocabulary, parse-enforced. The dimension namespace and the entry semantics: <<applicability-semantics,11.1.1>>. |
channel
,
obligation |
The evidence channel and the obligation level (<<modality-vocabulary,11.6>>). |
acceptance_criteria { ... } |
The threshold form of the decision rule. |
verification { method ... } |
The verification method of the requirement. |
dependencies { ... } |
Other requirements this one depends on. |
source_discrepancy { ... } |
The named-difference record against the source text (<<discrepancy-records,14.5>>). |
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 {
expression "ocl{...}"
uses { mpe accuracy_class }
quantity { kind mass unit kg }
accepts { verdict mpe_error op lte limit "ocl{...}" }
}
applicability { accuracy_class: [A, B, C, D] }
ref derives-from "urn:oiml:pub:r:60-1:2021#clause-5.3.2"
}
A
bind
names the aspect path that supplies the parameter's value —
into the subject's IS catalog (
model.parameters.e_max
), its
classification (
dimensions.accuracy_class
), or the run context — the
same path discipline as the form field's
bind
(
11.8
) and the requirement's own
binds_to
. The
instantiation order is fixed and deterministic:
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
.
A
conformance_class
scopes a family of tests to a requirement class:
conformance_class /conf/metrological-tests {
title "Metrological tests"
target /req/metrological
subject "LoadCell"
applicability { accuracy_class: [A, B, C, D] }
guidance "..."
}
A
conformance_test
is an operation on the subject:
conformance_test /conf/metrological-tests/mpe-test {
type Testing
targets { /req/metrological/mpe }
test_subject { kind model }
variables {
variable applied_load { type mass unit kg source measured
provenance { channel reference_instrument ref eq-013 observed_at t_log } }
variable e_l { type mass unit kg source derived
derivation "ocl{indication - applied_load}" }
}
observables { observable indication_series { quantity_kind mass unit kg } }
preconditions {
precondition warmed-up {
check "ocl{self.state = #ready}"
description "An unwarmed run is invalid, never a fail."
on_violation invalid
}
}
design {
selection { ... }
test_points { count 5 }
}
instances {
by accuracy_class
values { A { n_runs: 5 } C { n_runs: 3 } }
}
acceptance_criteria { criterion mpe-check { expression "ocl{...}" } }
pass_if "ocl{...}"
acceptance { rule guarded guard_band { kind NSFa value 0.5 } }
result_forms { mpe-report }
produces_artifacts { evidence-file }
}
The principal facet groups:
type
(the test kind),
targets
(the
requirements under test),
test_subject
,
variables
(typed test
variables; the
source
vocabulary is
declared
,
measured
,
derived
,
computed
,
lookup
, and a measured reference variable carries a
provenance
probe channel,
11.4
),
observables
,
preconditions
(a gate with
check
,
description
, and
on_violation
),
design
(the test design: selection rules, counts, phases, specimens,
test points, and the validation facets),
instances
(per-classification
parameter values),
acceptance_criteria
and
pass_if
(the decision),
acceptance
(
11.5
), and the output anchors
(
result_forms
,
produces_artifacts
,
report_rows
,
reference_materials
,
required_competence
).
inherits_from
reuses
another test's definition;
dependencies
orders.
A
verdict
is a named, reusable derivation of a judged quantity: derive
once, reference everywhere.
verdict drift_error {
quantity { kind volume-fraction unit "ppm" }
derive "ocl{indication - cgm_certified_value}"
inputs { indication cgm_certified_value }
series_reduction max_abs_over_window
ref derives-from "urn:oiml:pub:r:144-2:2013#clause-4.8"
}
The facets are
symbol
,
behavior
(the behavior the quantity derives
from),
quantity { kind unit }
,
derive
(the expression),
inputs
,
series_reduction
(a closed vocabulary, parse-enforced:
none
,
max
,
mean
,
worst_case
,
max_abs_over_window
),
acceptance
(
11.5
), and provenance. Requirement limits,
conformance tests, and form fields reference the verdict identifier
instead of restating the derivation.
A verdict's
inputs
name the free identifiers of its derivation, and an
input may name another VERDICT: the
acceptance chain is then an explicit graph, not an implicit one —
requirement →
limit.accepts.verdict
→ the verdict's
inputs
(symbols
and sub-verdicts) → each verdict's
acceptance
— and every hop is a
declared, citable model element with its own identifier and clause
provenance. The checker keeps the graph honest (C116,
verdict-inputs-resolve
: every input resolves to a declared symbol, a
test variable or observable, or another verdict; C117,
verdict-chain-acyclic
: the verdict graph never cycles). A consumption
system can therefore cite "the acceptance chain" precisely — the
decision structure is retrievable data — and traverse it: from a
verdict to the quantities it derives from, the tests that produce them,
and the requirements that bind it.
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.
A
constraint
declares a subject-intrinsic rule (an invariant of the
subject, distinct from a requirement, which is an external obligation):
constraint e-max-bound {
name "Applied load within capacity"
stereotype inv
check "ocl{self.applied_load <= self.e_max}"
on_violation invalid
violation_meaning "A run above capacity is not a test result."
source { doc "urn:oiml:pub:r:60-2:2021" clause "2.3" }
}
The facets are
name
,
stereotype
(the constraint kind;
inv
is the
intrinsic-rule stereotype),
check
(the embedded expression),
on_violation
,
violation_meaning
, and provenance. The constraint shape
is checker-enforced (C84).
A
form
is the evidence view of the secondary tier: a declarative
data-capture schema that projects the subject graph into a record.
form mpe-report {
name "MPE test report"
data_class MpeReport#data
conformance_process creep_test
section "E"
field e_max: number {
label "Maximum capacity"
bind "model.parameters.e_max"
unit kg
required true
}
field runs {
label "Runs"
subform_ref run_row { parameters { n: n_runs } }
min_items 3
}
pass_if "ocl{...}"
calculation_context { mpe_eval }
applicability { accuracy_class: [A, B, C, D] }
}
The principal facets:
name
,
data_class
(the record the form fills),
conformance_process
,
header
(a shared header form),
section
,
scope
,
dimensions
,
requirements { ... }
(the requirements the form
serves),
references
,
applicability
,
instances
,
constraints
,
criteria
,
tables
,
calculation_context { ... }
(the calculations in
scope),
pass_if
/
pass_fail
/
derivation
(the form-level judgment
derivations), and
report_rows { ... }
. A
field <name>[: <type>] { ... }
entry carries
label
,
definition
,
bind
(the aspect path the field records),
unit
,
symbol
,
verdict
(the verdict the field derives,
11.3
),
targets
,
dimension
,
enum
,
pattern
,
required
or
required_when
(a
conditional-requiredness expression),
default
,
values
/
enum_values
,
evaluation
,
calculation_bindings
,
measurement_method
,
items
/
fields
(nested structure),
min_items
/
max_items
,
subform_ref
, and provenance.
A
subform
declares a reusable parameterized row shape:
parameters { ... }
,
type
,
default
,
mapping
, and the same
field
entries. The field grammar of forms and subforms is one grammar, shared.
The
provision
is the v2 compliance primitive, retained: a statement of
requirement with RFC 2119 modality, traceable to source clauses.
provision MoistureRecorded {
condition "Every roast batch shall have its moisture content recorded"
modality SHALL
reference {
BFS-4-2
}
}
The facets are
condition
(the requirement text),
modality
(
11.6
), and
reference { ... }
(the cited
reference constructs,
14.3
). Processes bind
provisions with
validate_provision { ... }
(
12.1
). In a v3 model the
requirement
construct
(
11.1
) is the binding form: a provision states
text and modality, while a requirement additionally binds subject aspect
paths, carries a formal limit, and joins the coverage calculus. New
modelling SHOULD author requirements; provisions remain for the v2
surface and for standards whose provisions bind nothing.
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.
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.
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 {
set status "under_review"
create ReviewTask { with { application : self.id } }
notify ia_officer via email
}
}
}
The
kind
facet is a closed vocabulary, parse-enforced:
lifecycle
(workflow entities; the default) or
operational
(a subject's HAS state).
The two families are kept separate (C38). A transition carries
from
,
to
,
action
, an optional
guard
expression, and an optional
cascade
of side effects:
set
,
where
,
create
,
via
, and the semantic cascade
actions
lock
,
submit
,
notify
,
record
(a closed vocabulary,
parse-enforced). Cascade transition resolution is checker-enforced (C95),
and every referenced state must exist (C39).
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).
A
class
declares a record shape; the
#data
suffix marks a class as
registerable:
class BeanLot#data {
lot_id: string { definition "Supplier lot identifier" modality SHALL }
received_on: datetime { definition "Date received" modality SHALL }
certified_organic: boolean { definition "Certified organic" modality MAY }
}
class RoastProfile {
target_level: RoastLevel { definition "Target roast level" }
}
A class without
#data
is a helper class: a reusable complex type that
lives as a field inside other classes and cannot be registered. A field
declaration is
<name>[: <type>][<cardinality>] { <properties> }
; field
properties are
definition
and
modality
(
11.6
)
at minimum. The cardinality form is
[<min>..<max>]
with
*
for
unbounded. An
enum
declares a closed value set, each value with an
optional
definition
block. A
data_registry
declares a named store of
records of one data class (facets
title
,
data_class
); processes read
and write registries through their
reference_data_registry
and
output
facets (
12.1
).
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. 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
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).
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
,
latex
,
values
,
series
,
kind
,
quantity_kind
,
origin
,
and the linking facets
attribute
,
calculation
, and
profile
.
A
calculation
declares a named computation:
inputs { ... }
,
output { ... }
, and one of the calculation forms,
expression
(an embedded expression),
lookup
(a table lookup with
key
), or
profile
(a profile reference), with
params { param <name>: <type> { ... } }
,
variable
bindings, and
provenance.
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.
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
formulas_used
block,
keyed by a conformance-test reference, lists the evaluation formulas a
test derives (the
formulas
facet), with provenance (shape: C94).
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).
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 keep parsing during the transition: `source { doc
"..." clause "..." [fragment "..."] }
,
references { <role> { ... } }`,
and
specification_reference "..."
. A conforming serializer emits the
canonical
ref derives-from
line for a URN-anchored provenance record and
keeps the legacy
source
block for a free-text one
(
6.5
). The dedicated hot edges of the model
(
targets
,
dependencies
,
inherits_from
,
extends
,
maps_to
,
structure
) are facets of their constructs, never
ref
triples.
The v2
reference
construct remains the registry of citable external
clauses:
reference BFS-4-2 {
document "Bean Freshness Standard (fictional)"
clause "4.2"
title "Moisture content"
}
A
reference
declares one external document clause (facets
document
,
clause
,
title
); provisions and other elements cite it by identifier
through their
reference { ... }
blocks. New modelling SHOULD cite
document anchors directly with
ref derives-from
/
ref cites
and SHOULD
register a
reference
construct only where a named, reusable citation
target earns its keep.
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 through the structured identity
of a
reference
construct instead.
The v2
reference
construct (
14.3
) is the registry
of such named citation targets, and its facet set grows from display
strings to a resolvable identity:
reference iso-iec-17025-7.2 {
org "ISO/IEC"
document "ISO/IEC 17025"
edition "2017"
clause "7.2"
title "General requirements for the competence of testing and calibration laboratories"
urn "urn:iso:std:iso-iec:17025:ed-3"
}
The facets:
org
(the publishing organization),
document
(the
designation),
edition
(the publication edition),
clause
,
title
,
and
urn
(the resolvable identifier). When
urn
is present it IS the
document's identity and the clause anchor composes as
<urn>#clause-<clause>
; when it is absent, the structured quadruple
(org / document / edition / clause) is the identity a consumer's locator
resolves. Either way the cross-reference carries resolvable identifiers
alongside its labels, and the hop from a citing element to the cited
standard's clause is data, not display text. The checker verifies the
identity (C112,
reference-identity
): a malformed
urn
is an error;
a reference carrying neither
urn
nor the org-and-document pair is a
warning (a display-string citation is visible debt, never silently
citable).
The composition edge stays where it always was: a manifest's
uses
entry IS the resolvable identifier of a composed package (the package id
through the package locator, with the pinned
<id>@<edition>
form for
abstract imports,
7.6
), and the
reference
construct serves the citations that are NOT composition — the standards
a model cites without merging their content.
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 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, and do limits and calculation signatures declare their quantity kinds? |
| state | Do fired transitions and referenced states exist, and do the lifecycle and operational families stay separate? |
| promises | Do promises resolve, verify, and stay off bare parameter values? |
| artifacts | Do artifact definitions carry contracts, instances resolve, and evidence stays separate from judgment? |
| characteristics | Does every characteristic have one home, a behavior link, and declared derivation inputs, and do the verdict chains resolve and stay acyclic? |
| twins | Do serve bindings, endpoints, monitors, and composition declarations resolve and stay fresh? |
| coverage | Is every requirement tested, every test evidenced, every form judged, and the normative text covered? |
| edition | Are statuses, validity windows, supersedes chains, and edition pins well-formed, and do the declared lineage edges agree across the closure? |
| supply-chain | Do product mappings resolve, stay pinned, and keep passport access classes leak-free? |
| dataspace | Do the dataspace's policy, compatibility, and content references resolve, are its trust anchors shaped, is its governance cited, do its policies carry well-formed rules, and do correspondence annotations stay unambiguous? |
A package carries its known debt explicitly in
.primmel-allowlist.prl
.
An entry is KNOWN or STALE:
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.
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 |
form
|
formulas_used
|
derivation | calculation |
dimension
|
value
implies
|
subsumption | dimension value |
instance
|
of
/
model
/
family
|
instantiation | the instantiated element |
| manifest | uses
/
maps_to
|
composition | package |
| manifest | supersedes
/
superseded_by
|
edition lineage | package edition |
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.
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 | Expression | BracketList
Expression ::= ? a token starting with 'ocl{', re-accumulated until the
braces balance ?
BracketList ::= ? a token starting with '[', re-accumulated until the
accumulated text contains ']' ?
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.
This annex is the registry of every top-level construct of the language.
It reproduces the parser configuration of the reference implementation
(
packages/primmel/src/ser-des/config/index.ts
of primmel-ts); the two
are verified together, and a discrepancy is a defect in one of them.
Every construct declaration has the shape
KEYWORD IDENTIFIER { ... }
:
one keyword, one identifier, one block payload
(
9.1
). The four special declarations of the first
table are the exceptions. The "since" column states the language
generation that introduced the construct: v2 (the MN 113 generation,
including its extension wave), v3 (this document's first revision),
v3.1 (the 2026-08-28 revision's extension set,
clause 19
), or v3.2 (the 2026-09-04
revision's consumption extension set, primmel/spec#18); v3 accepts every
v2 construct
(
Annex D
), v3.1 changes no v3 construct, and v3.2
changes no v3.1 construct.
| 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 |
|---|---|---|---|---|
role
|
roles | v2 | 12 | |
provision
|
provisions | v2 | 11 | |
process
|
processes | v2 | 12 | |
approval
|
approvals | v2 | 12 | |
class
|
data classes | v2 | 13 | |
enum
|
enums | v2 | 13 | |
data_registry
|
registries | v2 | 13 | |
variable
|
variables | v2 | 13 | |
measurement
|
variables | v2 | 13 | |
exclusive_gateway
|
gateways | v2 | 12 | |
start
|
start_event
|
events | v2 | 12 |
end
|
end_event
|
events | v2 | 12 |
signalcatch
|
signal_catch_event
|
events | v2 | 12 |
timer
|
timer_event
|
events | v2 | 12 |
reference
|
references | v2 | 14 | |
canvas
|
subprocess
|
pages | v2 | 12 |
note
|
notes | v2 | 13 | |
table
|
tables | v2 | 13 | |
figure
|
figures | v2 | 13 | |
link
|
links | v2 | 13 | |
comment
|
comments | v2 | 13 | |
map_profile
|
map profiles | v2 | 12 | |
view_profile
|
view
|
view profiles | v2 | 12 |
term
|
terms | v2 | 13 | |
form
|
forms | v2 | 11 | |
subform
|
subforms | v2 | 11 | |
symbol
|
symbols | v2 | 13 | |
calculation
|
calculations | v2 | 13 | |
verdict
|
verdicts | v2 | 11 | |
reference_material
|
reference materials | v2 | 13 | |
test_point_set
|
test point sets | v2 | 13 | |
competence_kind
|
competence kinds | v2 | 11 | |
predicate
|
predicates | v2 | 14 | |
constraint
|
constraints | v2 | 11 | |
discrepancy_record
|
discrepancy records | v2 | 14 | |
state_machine
|
state machines | v2 | 12 | |
requirement
|
requirements | v2 | 11 | |
requirement_class
|
requirement classes | v2 | 11 | |
instrument
|
instruments | v2 | 10 | |
attribute_definition
|
attribute definitions | v2 | 10 | |
capability
|
capabilities | v2 | 10 | |
behavior
|
behaviors | v2 | 10 | |
condition_set
|
condition sets | v2 | 10 | |
conformance_class
|
conformance classes | v2 | 11 | |
conformance_test
|
conformance tests | v2 | 11 | |
subject
|
subjects | v3 | 10 | |
instance
|
instances | v3 | 10 | |
artifact_definition
|
artifact definitions | v3 | 10 | |
artifact_instance
|
artifact instances | v3 | 10 | |
quantity_register
|
quantity registers | v3 | 13 | |
dual
|
duals | v3 | 13 | |
activity_archetype
|
activity archetypes | v3 | 12 | |
connector_profile
|
connector profiles | v3 | 13 | |
monitor
|
monitors | v3 | 13 | |
passport
|
passports | v3 | 13 | |
invariant
|
invariants | v3 | 13 | |
test_sequence
|
test sequences | v3 | 13 | |
formulas_used
|
formulas-used registers | v3 | 13 | |
text
|
texts | v3 | 15 | |
dataspace
|
dataspaces | v3.1 | 19 | |
policy
|
policies | v3.1 | 19 | |
dimension
|
dimensions | v3.2 | 10 |
This annex is the catalog of every rule the checker runs. It reproduces,
without paraphrase, the machine-readable registry of the reference
implementation (
packages/primmel/src/check-rules.ts
of primmel-ts at
version 1.8.2), which
primmel check --rules
prints. The toolchain
registry is the operative form; this annex is its publication. A
discrepancy between this annex and the toolchain registry is a defect in
one of them, and the verification is one command:
primmel check --rules
Each rule has an identifier (C1 to C119; the numbering is historical and not contiguous by family), a name used in findings, a family, a default severity, and a level:
The detailed semantics of a rule (what exactly it matches) are documented with the rule's implementation and its tests; this catalog fixes the identifiers, names, families, severities, and levels that conformance cites.
Rules introduced by the v3.2 revision (C110 and higher; primmel/spec#18)
ship in the reference toolchain at
@primmel/primmel
1.9.0 and are part
of the operative registry; the conformance suite at version 3.2.0 pins
each rule's error-severity leg (clauses CON-01 to CON-09). Three legs
report at warning severity in 1.9.0 where the shipped estate trips the
error form — C110's cross-field alias overlap, C115's unit resolution,
and C118's unit resolution — and tighten to the severities this catalog
states when the estate's terms and quantity registers are re-authored
(TODO.primmel/11d). Rule C119 (the requirement-namespace pin of
7.6
, mirroring the layering doctrine's
layer-owned-namespace guard) ships in the reference toolchain at
@primmel/primmel
1.10.0; the conformance suite at version 3.3.0 pins
its error leg (clause PKG-06).
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C1 | attribute-bind-scope
|
error | normal |
| C2 | reference-targets-resolve
|
error | normal |
| C3 | dimension-enums
|
error | normal |
| C4 | store-uniqueness
|
error | normal |
| C5 | req-test-coverage
|
warning | normal |
| C56 | allowlist-malformed
|
error | normal |
| C57 | allowlist-stale
|
error | normal |
| C89 | spelling-code-wellformed
|
error | normal |
| C90 | invariant-shape
|
error | normal |
| C91 | invariant-enforcement-grammar
|
error | normal |
| C92 | test-sequence-shape
|
error | normal |
| C93 | test-sequence-integrity
|
error | normal |
| C94 | formulas-used-shape
|
error | normal |
| C96 | duplicate-id
|
error | normal |
| C97 | program-maps-resolves
|
error | normal |
| C98 | program-surveillance-required
|
warning | normal |
| C103 | declared-predicate
|
error | normal |
| C110 | term-alias-shape
|
error | normal |
| C111 | dimension-shape
|
error | normal |
| C112 | reference-identity
|
error | normal |
| C118 | requirement-parameter-shape
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C6 | anatomy-family
|
error | normal |
| C7 | anatomy-characteristic-derivation
|
error | normal |
| C8 | anatomy-behavior-resolves
|
error | normal |
| C9 | subject-extends-resolves
|
warning | normal |
| C84 | constraint-shape
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C10 | process-one-start
|
error | normal |
| C11 | process-terminal-end
|
error | normal |
| C12 | process-flow-names-resolve
|
error | normal |
| C13 | process-signature-realized
|
error | normal |
| C14 | process-timer-recurrence
|
error | normal |
| C15 | process-timer-period
|
error | normal |
| C16 | process-step-ids-unique
|
error | normal |
| C58 | activity-kind-resolves
|
error | normal |
| C59 | segregation-members-resolve
|
error | normal |
| C74 | process-io-type-coherence
|
error | normal |
| C75 | process-flow-io-cover
|
error | normal |
| C76 | subprocess-signature-bound
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C17 | instance-scope
|
error | normal |
| C18 | instance-version-pin
|
error | normal |
| C19 | chain-acyclic
|
error | normal |
| C20 | instance-of-resolves
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C21 | mapping-resolves
|
error | normal |
| C22 | mapping-direction
|
error | normal |
| C23 | mapping-calculus-consistency
|
error | normal |
| C24 | import-not-mapping
|
error | normal |
| C25 | mapping-description
|
warning | audit |
| C26 | view-read-only
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C27 | uses-resolves
|
error | normal |
| C28 | uses-no-redefine
|
error | normal |
| C29 | uses-cycle
|
error | normal |
| C30 | provides-consumed-or-waived
|
warning | normal |
| C31 | requires-satisfied
|
error | normal |
| C119 | namespace-pin-violation
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C32 | inv1-no-bare-quantity
|
error | normal |
| C33 | quantity-coherence
|
error | normal |
| C34 | duality-coherence
|
error | normal |
| C35 | time-format
|
error | normal |
| C36 | map-type
|
error | normal |
| C114 | limit-quantity-coherence
|
error | normal |
| C115 | calculation-signature-shape
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C37 | state-fires-resolve
|
error | normal |
| C38 | state-family-separation
|
error | normal |
| C39 | state-machine-states-referenced
|
error | normal |
| C40 | anatomy-state-resolves
|
error | normal |
| C41 | precondition-on-violation-known
|
warning | normal |
| C95 | cascade-transition-resolve
|
error | normal |
| C109 | state-machine-initial-present
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C42 | promise-target-resolves
|
error | normal |
| C43 | promise-verifiable
|
warning | normal |
| C44 | promise-not-bare-value
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C45 | artifact-def-contract
|
error | normal |
| C46 | artifact-instance-resolves
|
error | normal |
| C47 | artifact-evidence-separation
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C48 | characteristic-one-home
|
error | normal |
| C49 | characteristic-behavior-link
|
error | normal |
| C50 | characteristic-derivation-inputs
|
error | normal |
| C116 | verdict-inputs-resolve
|
error | normal |
| C117 | verdict-chain-acyclic
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C60 | serve-targets-resolve
|
error | normal |
| C61 | payload-schema-quantity
|
error | normal |
| C62 | access-scope-covers-serves
|
error | normal |
| C63 | freshness-required-on-live-bindings
|
error | normal |
| C64 | endpoint-profile-resolves
|
error | normal |
| C65 | monitor-subject-resolves
|
error | normal |
| C66 | monitor-trigger-wellformed
|
error | normal |
| C67 | monitor-evaluate-resolves
|
error | normal |
| C68 | monitor-fail-escalation
|
warning | normal |
| C69 | monitor-escalation-resolves
|
error | normal |
| C70 | monitor-emit-sinks
|
error | normal |
| C99 | variable-provenance-channel
|
error | normal |
| C100 | composition-components-resolve
|
error | normal |
| C101 | composition-decomposition-covers
|
error | normal |
| C102 | composition-state-rule-closed
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C51 | coverage-test-evidence
|
warning | audit |
| C52 | coverage-form-judgment
|
warning | audit |
| C53 | coverage-uses-bound
|
error | normal |
| C54 | coverage-lookup-table-exists
|
error | normal |
| C55 | coverage-budget
|
error | audit |
| C71 | text-coverage-sentence-uncovered
|
warning | audit |
| C72 | text-coverage-budget
|
error | audit |
| C73 | text-coverage-config
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C77 | edition-status
|
error | normal |
| C78 | edition-validity-window
|
error | normal |
| C79 | edition-supersedes-resolves
|
error | normal |
| C80 | edition-pin-resolves
|
error | normal |
| C85 | baseurn-wellformed
|
error | normal |
| C113 | edition-lineage-coherent
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C81 | product-maps-resolves
|
error | normal |
| C82 | product-unmapped-promises
|
warning | normal |
| C83 | abstract-import-pinned
|
error | normal |
| C86 | passport-content-resolves
|
error | normal |
| C87 | passport-access-leak
|
error | normal |
| C88 | passport-upi-scheme
|
error | normal |
| Rule | Name | Default severity | Level |
|---|---|---|---|
| C104 | dataspace-references-resolve
|
error | normal |
| C105 | dataspace-trust-anchor-shape
|
error | normal |
| C106 | dataspace-governance-provenance
|
warning | normal |
| C107 | policy-shape
|
error | normal |
| C108 | correspondence-shape
|
error | normal |
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
).
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.