Authoring · 10 of 17
Modelling Your Lab
In this chapter: the reference/implementation pattern of Volume I, chapter 15 brought down to the laboratory floor, a test laboratory authors its own implementation package: its test SOPs as executable processes, its equipment register with calibration evidence, its own record forms, and a
.prmmapping every step and record to the Recommendation’s required methods, while its structured accreditation scope answers, by cover calculus, which tests the lab may be dispatched at all. The coverage calculus, not prose , then answers “is this lab’s procedure a fulfilment of the method?” The worked example is MTL Rhein Ruhr, a fictional Scheme-A load-cell laboratory mapped to R 60-2, invented for this walkthrough and existing nowhere else.
10.1 Why a lab model
Chapters 1–9 produced the reference side: the Recommendation’s required methods (chapter 4) with their abstract procedure steps, acceptance criteria, run-validity preconditions and required report forms (chapter 5). That is what the standards body publishes. But no laboratory executes an abstract method, every accredited laboratory executes its own SOPs: more granular, quality-system-flavoured, equipment-bound procedures that claim to fulfil the method.
Today that claim lives in accreditation assessments and prose
cross-reference tables. The implementation package makes it computable,
exactly as Volume I, chapter 5’s mapping discipline does for models in
general and the platform’s PD-05 coverage gate (TODO.roadmap/16 ,
browser/build/pd05-coverage.ts, the platform’s certification workflow
mapped to the OIML-CS abstract process model) does for the certification
workflow:
lab's implementation model ──maps──▶ Recommendation (required methods)
The lab is the third publisher of the supply chain (Volume I, §15.1): the package speaks for the lab’s operations, it is authored once, and the calculus answers per required method: full / minimal / partial / no cover, with every fulfilment pair carrying the description and justification an assessor would otherwise have to interview the quality manager for.
10.2 The implementation package
A lab package is a directory data/lab-<name>/ discovered through its
implementation.yaml manifest. It is deliberately neither a standard
nor a composition layer:
- No
standard.yaml, the app build never discovers it; nothing of the lab leaks into a Recommendation’s rendered content. - No
layer.yaml, nothinguses:it; its content never merges into a rec’s effective tree. Layers are content Recommendations share; a lab’s SOPs are its own, a peer of the rec in the supply chain.
The manifest declares the identity, the organization, and, the
decisive act, maps_to: the reference standard and the required
methods this package’s SOPs fulfil:
# data/lab-mtl-rhein/implementation.yaml
id: lab-mtl-rhein
kind: implementation
organization:
name: MTL Rhein Ruhr GmbH
role: test-laboratory
scheme: A
accreditation: "EXAMPLE D-PL-19283-01-00 — ISO/IEC 17025, … (fictional)"
maps_to:
standard: r60
methods:
- /conf/metrological-tests/creep # R 60-2, 2.10.2
- /conf/electronic-tests/esd # R 60-2, 2.10.7.8
structure:
processes: evaluation/processes.yaml # the SOPs
equipment: evaluation/equipment.yaml # the equipment register
forms: execution/forms # the lab's record forms
mapping: lab-to-r60-2.prm # the fulfilment pairs
10.3 SOPs as executable processes
Each SOP is an executable process: ordered steps, each with an executor (the lab’s internal role) and the equipment it uses (register ids). SOPs are more granular than the reference method but compatible: the method’s ten abstract procedure steps become thirteen concrete lab steps, the extra ones being quality-system practice (calibration verification, four-eyes review) that the method never required and the mapping records honestly.
sops:
- id: sop_mtl_012_creep
label: Creep test of load cells (R 60-2, 2.10.2)
document: { number: MTL-SOP-012, revision: "3.2",
approved_by: quality_manager, approved_on: "2026-03-14" }
actor: test_laboratory
obligation: shall
implements: /conf/metrological-tests/creep # documentary only
produces: [mtl_f_042_creep_record] # the evidence output
steps:
- id: apply_dmax_within_envelope
order: 8
action: >
Select D_max within 92–98 % of E_max, record D_max and E_max
on the test record, and apply D_max …
executor: lab_technician
equipment: [mtl_f_001, mtl_daq_101]
exceeds: true
exceeds_note: >
The lab envelope 92–98 % of E_max is stricter than the
required 90–100 % (R 60-2, 2.10.2) — A ⇒ B without B ⇒ A.
Two disciplines live here:
implementsis documentary; the.prmis formal. The single home of the fulfilment relation is the mapping file, never inline fields the calculus cannot check.- Stricter steps are declared, never silent. A step tighter than the
reference practice it fulfils carries
exceeds: true+exceeds_note: the lab’s practice implies the required practice (A ⇒ B) but not the converse, exactly the asymmetry the task demands be explicit. The gate cross-checks that the step’s.prmpair says “stricter” in its description.
10.4 The equipment register
One register per lab; every instrument an SOP step uses, with its ISO/IEC 17025 calibration evidence:
equipment:
- id: mtl_f_001
label: Deadweight force standard machine, 600 kN
inventory_number: MTL-F-001
calibration:
certificate: "Example TL calibration certificate KA-2025-1187"
calibrated_on: "2025-11-03"
due_on: "2026-11-02"
traceable_to: "the national force standard"
Step references must resolve (the gate), and the end-to-end flow check requires calibration to be in date at the moment a step executes , metrological traceability as a runtime assertion, not a binder tab.
10.5 The lab’s record forms
The lab keeps its own forms, that is the point. Implementation
evidence can satisfy reference evidence requirements: each lab form
declares the required report form it fulfils (fulfils_form, an R 60-3
form id from the method’s result_forms), and each field declares the
three links of the evidence chain:
lab_form:
id: mtl_f_042_creep_record
fulfils_form: creep-dr # r60-3/table-6.8
fulfils_conformance_test: /conf/metrological-tests/creep
fields:
- name: dmax_applied
label: Maximum test load D_max applied (92–98 % of E_max)
type: number
unit: kg
recorded_at: apply_dmax_within_envelope # the producing step
maps_to: dmax # the required-form field
variable: d_max # the method's run input
required: true
recorded_at, step ⇒ field: which SOP step records it;maps_to, field ⇒ required field: the dot-path into the required form’s field tree (repeatable-group entries asdirect_application.indication);variable, field ⇒ run input: the method’s variable (chapter 4) the field captures.
Fields without maps_to are lab-internal records beyond the required
form (calibration certificates, the four-eyes sign-off, finer-grained
readings), the lab records more, and the model says so.
When a lab field’s type differs from the required-form field it
satisfies, the reduction is declared on the field, never silent ,
the gate checks scalar compatibility on every maps_to pair (identical
types, or integer ⇒ number widening, pass as-is):
coerce: int-bool, the lab records a count (integer) where the required form asks applied/not-applied (boolean): a positive count coerces to true. The example’scontact_discharge_countandair_discharge_countfulfil the esd form’sdischarge_types.contact_discharges/.air_dischargesbooleans this way.mapping:, a multi-valued or free-text recording reducing to a single-valued required field (an enum, or any scalar): the note states how. The example’spolarity_appliedrecords ‘positive’ / ‘negative’ / ‘both’ (R 60-2 requires both polarities) where the required form models one enum value; the note fixes the transcription convention.
10.6 The .prm, the fulfilment made formal
The mapping file is the standalone .prm serialization of Volume I,
chapter 5 (the same format the platform-to-OIML-CS mapping uses). The
reference tree per mapped method spans the whole meaning of “the
method”:
/conf/metrological-tests/creep ← the method (root)
…/step/<abstract procedure step> ← the procedure
…/criterion/<acceptance criterion> ← the acceptance
/req/<targeted requirement> ← the verdict points
…/precondition/<run-validity precondition> ← the run validity
…/form/<result form> ← the evidence output
Every pair binds a lab element (SOP, step, record form) to a component with description + justification, how the fulfilment works, and why the claim holds:
"sop_mtl_012_creep/apply_dmax_within_envelope": {
"r60#/conf/metrological-tests/creep/precondition/creep-load-envelope": {
"description": "STRICTER THAN REFERENCE: D_max within 92–98 % of
E_max — tighter than the required 90–100 % envelope
(A ⇒ B without B ⇒ A), and the envelope inputs are
always recorded …",
"justification": "… the lab's tighter selection interval is a subset
of the required envelope and the inputs it resolves
are mandatory record fields."
}
}
The coverage block at the end asserts the expected level of every
component, the regression tripwire: computed ≠ asserted fails the gate.
10.7 The coverage gate
npm run validate computes the report (browser/build/lab-coverage.ts)
with the real task-04 engine, @primmel/primmel computeCoverage,
the same calculus the PD-05 gate uses, not a re-implementation, and
enforces seven rules:
- no shared targets, two mapped methods of one package may not
declare the same
/req/*target URN (§10.9 explains why); - every pair carries description and justification;
- no dangling pairs, targets resolve against the method forest, sources are declared SOPs/steps/forms;
- the coverage tripwire, the
.prmasserts every component, computed equals asserted, no stale assertions; - full cover by construction, every component of every declared method is directly mapped. The pair is the fulfilment documentation: an inherited-only cover documents nothing, and inheritance masks dropped pairs (the PD-05 gate’s design rule, tightened, a lab package asserts full cover, so there is no named-gaps escape hatch);
- SOP integrity, unique ids/orders, equipment refs resolve, exactly
one SOP per mapped method,
recorded_at/maps_to/variable/fulfils_formresolve, everymaps_topair is type-compatible (scalar vs scalar: identical orinteger⇒number; anything else only via a declaredcoerce: int-boolormapping:note), and every measured/declared run input of the method is captured by ≥1 lab field across the UNION of the method’s forms (the SOP can produce the required evidence, a form insufficient on its own warns; read standalone it is a partial record of the run); - stricter steps are never silent (
exceeds⇔ a “stricter” pair description).
The example’s report, computed by the calculus:
lab-mtl-rhein (data/lab-mtl-rhein/lab-to-r60-2.prm)
● full /conf/metrological-tests/creep ⇐ sop_mtl_012_creep
● full …/step/check_test_conditions ⇐ …/verify_environment, …/verify_equipment_calibration
… (35 components: 17 creep + root, 16 esd + root)
● full /conf/electronic-tests/esd/form/esd ⇐ …/complete_and_review_record, mtl_f_017_esd_record
Summary — full: 35, minimal: 0, partial: 0, none: 0 (35 components)
Methods at full cover — 2/2 (/conf/metrological-tests/creep, /conf/electronic-tests/esd)
10.8 The competence half: accreditation_scope and the cover calculus ●
The .prm answers “is the procedure a fulfilment?”; a prior question
is “may this lab be dispatched the test at all?” The test side
declares its demands (required_competence, §4.13); the laboratory
side declares its structured scope of accreditation (task 48, ● ,
ISO/IEC 17025 §6.4/§6.6, OIML-CS PD-05 §5.3):
Every TestLaboratory carries accreditation_scope: entries in the same
shape as the test’s requirements, { kind, range?, method_standard?, resolution?, stability? }, with number-only ranges (the parameter-bound
form exists only test-side, resolved against the dispatch context). Two
neighboring concepts stay in their own homes: the accreditation record
(body, certificate number, validity) rides the existing accreditation:
helper (Accreditation.body/number/expiry_date), and the flat
capabilities: strings stay the construction-fixture / operational
vocabulary, the scope supersedes them for competence matching only.
The cover calculus has one home (browser/src/data/competence.ts,
pure):
covers(scopeEntry, required), kind equal; range covers; method_standard equal-or-compatible; resolution/stability at-least-as-good (the lab’s number ≤ the required bound);coverTest, every required entry of the test covered by the scope;planDispatch, over the candidate labs in caller order (the IA’s preference ranking): the first covering lab wins, and a refusal is explicit, with the uncovered entries named, never a silent assignment. A lab that cannot cover the programme’s tests is not a worse choice; it is ineligible, and the record says why (lab-selection.service.ts, thecoverage:ranking). This is the same doctrine as the admissibility gate and the issuance gate: eligibility is computed, and a negative answer names its reason.
Scopes are substantiated, never copied: the seeded scopes (the MTL
990 register and lab-mtl-rhein) reference the per-lab registers via
equipment_refs / personnel_refs, the scope entry says which
equipment and personnel stand behind it, by id, and the registers stay
the single home of the calibration and qualification facts. The
multi-lab reality is proven, not asserted:
competence-dispatch.test.ts dispatches the R 60 programme MTL-first
and watches EMC, power-variation, barometric and software examinations
spill to the NMI whose scope covers them, the preference ranking
deciding only among labs that can.
10.9 The end-to-end flow
Coverage answers “is the procedure a fulfilment?”; the flow test
(browser/src/__tests__/lab-implementation.test.ts) answers “does the
SOP produce the required evidence?”, by executing the SOP model:
- walk the steps in order at the example run date (2026-06-08): every step has its executor; every equipment used is in calibration on that date;
- each step produces the record fields whose
recorded_atnames it; - the produced record must satisfy every required field of the
required R 60-3 form (through
maps_to) and capture every measured/declared variable of the method (throughvariable); - every executed step must be a mapping source, the fulfilment chain is complete end to end.
10.10 Authoring your own lab, the moves
- Inventory the methods. Pick the
/conf/*methods your lab is accredited for; they are themaps_tolist. - Write the SOPs as they are performed, your real steps, executors and instruments, not the method restated. Granularity mismatch is expected; the mapping absorbs it.
- Register the equipment with its calibration evidence.
- Model your record forms and link every field (
recorded_at,maps_to,variable). Fields with no counterpart in the required form stay unmapped-by-maps_to, they are your extra evidence. - Write the
.prmpair by pair, description how, justification why. Where your practice is tighter, declareexceedson the step and say “stricter” in the pair. A method component you cannot map is the finding: either your SOP has a hole, or the pair you are missing is the accreditation discussion you needed to have anyway. - Run the gate.
npm run validatecomputes the cover; the end-to-end test executes the SOPs. Full cover by calculus, evidence by execution, then invite the assessor to read the.prm.
One target, one method. Two methods in one package’s maps_to
may not declare the same /req/* target URN, the gate errors and names
the shared target and both methods. The reason is not bureaucratic:
component ids in the coverage tree are global, so one pair to the shared
target would silently cover both methods’ occurrences of it, and that
is precisely what must not happen. One SOP step may well enforce a
requirement (your ESD generator verifies the disturbances clause every
time it fires), but each method’s judgment of that requirement is
distinct: R 60-2’s esd, bursts, surge, emc-susceptibility and
short-time-power tests all target /req/electronic/disturbances, yet
each method reaches its own verdict on it from its own severity, its own
evidence, its own acceptance criterion. A single pair claiming all five
judgments documents none of them. The example therefore maps each method
as its own coverage; if your lab genuinely covers two such methods,
model them as separate coverages (two packages, one per method
family) or split the SOP so each method’s disturbance verdict has
its own documented pair. (Scoping shared targets per method root inside
one package is the calculus’s planned generalization, until then the
gate keeps the sharing impossible.)
10.11 Validation rules (summary)
-
every
accreditation_scopeentry names a competence-kind-registry kind with resolvable method standards and number-only ranges; everyequipment_refs/personnel_refsentry resolves against the lab’s registers (R29 checks every seeded dispatch assignment for cover); -
dispatch is cover-gated: a lab not covering a test’s
required_competenceis ineligible for it, and aplanDispatchrefusal names the uncovered entries, never a silent assignment; -
an implementation package declares
kind: implementationand amaps_tomethod list; unmapped declared methods fail the gate; -
two mapped methods may not share a
/req/*target URN, one pair cannot fulfil two methods’ distinct judgments of the same requirement; -
every reference component is directly mapped at full cover, the calculus, never prose;
-
exceeds: true⇔ a “stricter” pair description (A ⇒ B without B ⇒ A is never silent); -
lab evidence fields resolve (
recorded_at→ step,maps_to→ required-form field,variable→ method variable); -
a
maps_topair whose field types differ is legal only with the reduction declared,coerce: int-bool(count ⇒ applied/not-applied) or amapping:note (multi-value/free-text ⇒ single value); -
every measured/declared run input of a mapped method is captured by a lab field, across the UNION of the method’s forms (a form insufficient alone warns);
-
equipment calibration is in date when a step executes.
Next: Volume IV, the OIML-CS Scheme and the platform annex, on the Primmel SMART docs site, the OIML-CS certification-scheme reference package and the platform runtime the packages run on.