Conformance matrix

Non-normative. This records what is true today, not what is intended. It is the file to distrust last: if it disagrees with the code, the code wins and this file has a defect.

Assessed: 2026-08-02, against rustc 1.97.1, openehr 0.2.0. Rows touched by A-36 were re-derived 2026-08-20 against openehr 0.3.0 and rustc 1.98.0; the rest carry the earlier date and have not been re-checked since, which is stated rather than papered over (W0.10). The D3.18b/D3.18c rows were derived 2026-08-21 with A-35. The K15.18–K15.23 row was derived 2026-08-30 against openehr 0.8.0, when openehr::am::validate landed. Method: each requirement read against the code that implements it and the test that exercises it; test names below are real and runnable with cargo test <name>.

Key#

Meaning
• Implemented and verified by a named test (C0.7)
? Implemented, no test exercises it directly (C0.8)
doc A documentation requirement; satisfied by prose, not by code
type Enforced by the type system. A runtime test for it could not fail (T13.2), so writing one would be theatre.
— Deliberately not implemented; see 01-scope.md
spec Specified and not implemented: the requirement is in force, no code claims it, and the gap is tracked in audit.md. Distinct from —, which is a decision not to implement, and from open, which is a defect against something the crate purports to do
withdrawn The requirement no longer stands. Its identifier is permanent (C0.5) and its text is kept where it was, marked, so a citation to it still resolves (C0.18, C0.19)
open Not satisfied; tracked in audit.md

Doctests count as tests: they compile and run in CI (T13.8).

Totals#

Counted mechanically from the tables below, with every requirement id in spec/*.md checked to appear exactly once — 344 ids, 344 covered, none missing.

A hand-written total in a file like this is a number nobody rechecks, and this one proved the point: it said 291 of 291 while six requirements added after the assessment had no row at all — S1.18, S1.19, S1.20, L10.9, L10.10, L10.11. "Derived from the rows" was true once and then was not.

CI now re-derives it on every push, expanding the ranges in the Id column and comparing against the requirements the specification defines. A new requirement with no row fails the build; so does a row for a requirement that does not exist, and so does an id covered twice.

Re-derived 2026-08-26, mechanically, from the rows below — and the previous numbers were wrong, which is the second time this table has drifted from its own tables. It said 291 total against 300 in the sentence above while the rows held 311, because CI re-derives coverage and nothing re-derived the tally. A-41 is the finding; the counts below were produced by expanding every Id cell and counting statuses, and re-running that is how they should be checked again.

Re-derived again 2026-08-30, the same way, after K15.18–K15.23 moved from spec to •: six ids, openehr::am::validate. Re-derived a third time the same day, after K15.24–K15.27 made the same move: four ids, openehr::am::repository and openehr::am::validate's validate_with_repository.

Status 2026-08-30 2026-08-26 2026-07-31
• verified 268 258 175
doc 33 33 30
spec — in force, unimplemented 18 28 —
type 13 13 —
— out of scope 8 8 6
? implemented, untested 3 3 54
withdrawn 1 1 —
open 0 0 4
total requirements 344 344 269

The spec rows are §15 plus S1.21, added on 2026-08-26 when S1.4 was withdrawn and the Archetype Model brought into scope. There were 32 of them that day; K15.1–K15.4 — the AOM2 object model — were implemented and tested the same day, leaving 28. K15.18–K15.23 — validating a Reference Model instance against an archetype already held in memory, as a verdict kept separate from L10.x and with no partial pass — followed on 2026-08-30, leaving 22. K15.24–K15.27 — the repository abstraction, and resolving a C_ARCHETYPE_ROOT filler through one — followed the same day, leaving 18. They remain a large block of unsatisfied requirements this crate has carried since 2026-08-26, and they are counted here rather than described elsewhere so that the size of the gap is a number a reader can see.

Three things moved these numbers, and they are not the same thing.

Requirements gained tests. tests/invariants.rs closed A-06: 32 tests, five of them mutation-verified by disabling the check and watching the test fail.

Thirteen were reclassified type — enforced by the compiler, where a runtime test could not fail and writing one would be theatre (T13.2). Calling them type rather than • keeps the verified count meaning what it says.

Twenty-two requirements were added, and this is the part worth reading. The rendered openEHR pages omit every class-definition table — the sources include:: them from a UML export generated at build time — so anything implemented from the prose alone is a guess. All four Reference Model packages have now been read from the published PDFs, and all four times the primary source contradicted what had been implemented: three rules backwards, sixteen invariants missing, five reported under names openEHR does not use, and one place where openEHR contradicts itself (V8.7a). A rising total here is the specification catching up with the one it implements.

The three remaining ? are named individually below rather than lumped: a constant-time claim nobody has timed, an error path that cannot be provoked, and mutation verification being five checks rather than a policy.

§0 Conformance — C0#

Process requirements; they govern this specification rather than the code.

Id Status Evidence
C0.1–C0.3 doc this directory's prose
C0.4–C0.6 • every requirement in this directory carries an id; no gaps or reuse
C0.7–C0.11 doc this file, and audit.md
C0.12–C0.14 • I2.15 and R4.9 are the two declared departures
C0.15–C0.18 doc commit conventions
C0.19 doc S1.4 is the first exclusion reversed under it — withdrawn in place, reason kept, §15 named

§1 Scope — S1#

Id Status Evidence
S1.1–S1.3 • canonical_json::a_composition_covering_every_modelled_class_round_trips
S1.4 withdrawn reversed 2026-08-26; the Archetype Model is in scope under S1.21 and §15. Text kept in place, marked (C0.19)
S1.5–S1.7 — not implemented, by decision
S1.8 • time_specification::tests::unimplemented_accessors_refuse_rather_than_guess
S1.9 • quantity::tests::different_units_are_not_comparable_in_either_direction
S1.10 • text::tests::rubric_checking_reports_unchecked_separately_from_valid
S1.11 doc security module header; V8.18
S1.12 • guarantees::unimplemented_operations_refuse_and_cite_the_spec
S1.13 • canonical_json round trip covers DV_PARSABLE and time specifications
S1.14 doc security module header
S1.15 doc J9.15; bounded by canonical_json::reading_a_composition_stays_within_a_small_stack
S1.21 spec §15 in full; nothing implemented — A-40
S1.16–S1.17 • terminology::tests::the_codes_that_disagree_between_terminology_repositories_are_the_current_ones
S1.18 — declared departure: ISO 3166 and ISO 639 are not carried, so COMPOSITION.Territory_valid and Language_valid are not checked. Its closing requirement — that a caller can do the check instead — is verified: guarantees::a_caller_can_read_every_code_the_crate_declines_to_check (A-19)
S1.19 — declared exclusion: no demographic repository, so the four PARTY graph invariants cannot be checked from a value in hand
S1.20 — declared departure: an EHR_ACCESS may record no policy, so Scheme_valid is not checked. X11.24's fail-closed default is why that is safe

§2 Identifiers — I2#

Id Status Evidence
I2.1 • object_id::tests::version_id_round_trips_exactly, …::archetype_id_round_trips_and_splits — the parts are asserted, not just the text
I2.2–I2.3 • object_id::tests::version_id_round_trips_exactly, …::archetype_id_round_trips_and_splits
I2.4–I2.5 • uid::tests::oid_wins_over_internet_id_for_all_digit_text
I2.6 • uid::tests::rejects_near_misses
I2.7 • uid::tests::uuid_case_is_preserved_but_not_significant, …::uuid_hash_agrees_with_eq
I2.8–I2.9 • doctests on IsoOid, InternetId
I2.10 • object_id::tests::hier_object_id_rejects_a_double_colon_extension
I2.11 • object_id::tests::version_id_round_trips_exactly
I2.12–I2.13 • doctest on VersionTreeId
I2.14–I2.15 • object_id::tests::archetype_id_rejects_malformed_forms
I2.16 • doctest on TemplateId
I2.17 • doctest on TerminologyId
I2.18 • doctest on GenericId
I2.19 • invariants::a_uid_field_admits_only_the_two_uid_based_classes
I2.20 • object_id::tests::uid_based_id_infers_by_separator_count
I2.21–I2.22 • object_ref::tests::namespace_grammar_is_enforced
I2.23 • object_ref::tests::party_ref_refuses_a_non_demographic_class
I2.24 • invariants::a_locatable_ref_refuses_an_empty_path
I2.25 • object_ref::tests::locatable_ref_uri_does_not_double_the_slash

§3 Data types — D3#

Id Status Evidence
D3.1 • data_types::tests::every_variant_round_trips_through_its_type_tag
D3.2 • invariants::the_deprecated_paragraph_type_still_round_trips
D3.3 • text::tests::errors_from_text_do_not_echo_the_text
D3.4 • text::tests::plain_no_newlines_is_checked_against_the_value
D3.5 • invariants::a_code_phrase_needs_a_terminology_and_a_code
D3.6 • invariants::a_term_mapping_match_is_one_of_four_characters
D3.7–D3.8 • text::tests::rubric_checking_reports_unchecked_separately_from_valid
D3.9–D3.10 • iso8601::tests::partial_dates_keep_their_precision_and_text; canonical_json::partial_dates_and_negative_durations_survive_verbatim
D3.11 • iso8601::tests::leap_day_validity_follows_the_gregorian_rule
D3.12 • invariants::a_leap_second_is_accepted_and_a_sixty_first_is_not
D3.13 • iso8601::tests::durations_round_trip_and_reject_disorder
D3.13a doc recorded as a limitation, A-02
D3.14 • guarantees::every_undecidable_comparison_answers_none; rm::data_types::tests::every_comparable_variant_of_a_data_value_compares, which covers the decidable half — six arms of semantic_cmp were deletable in silence until A-39
D3.14a • guarantees::a_reference_range_is_unmoved_by_how_an_instant_is_spelled, which asserts an incomparable value is excluded rather than admitted (A-39)
D3.15 • quantity::tests::different_units_are_not_comparable_in_either_direction
D3.16 • quantity::tests::ordinals_from_different_terminologies_do_not_compare
D3.17 • invariants::proportions_of_different_kinds_do_not_compare
D3.18 • iso8601::tests::offsets_normalise_before_comparison
D3.18a • resolved: PartialOrd/Ord removed from the base ISO 8601 types, semantic order is the plain method semantic_cmp — iso8601::tests::eq_is_lexical_and_semantic_cmp_is_not_the_same_question (A-32, fixed)
D3.18b • the same resolution over all ten DV_ORDERED types and DATA_VALUE — guarantees::equality_and_order_disagree_by_design_and_neither_is_partial_ord (A-35, fixed)
D3.18c • INTERVAL<T> is bounded on SemanticOrd; guarantees::a_reference_range_is_unmoved_by_how_an_instant_is_spelled covers the contains rewrite
D3.18d • base::real::tests::a_measured_precision_survives_a_round_trip; every_digit_survives_and_only_the_exponent_form_is_normalised, which asserts the one departure
D3.18e • base::real::tests::equality_and_order_answer_different_questions — lexical equality, numeric order, and no PartialOrd
D3.18f • base::real::tests::a_real_that_never_saw_a_constructor_is_still_readable
D3.19 • quantity::tests::non_finite_magnitudes_are_refused
D3.19a doc declared narrowing; recorded here and in §3
D3.20, D3.20a • quantity::tests::precision_accepts_the_unlimited_sentinel; validation::tests::the_unlimited_precision_sentinel_validates
D3.21 • quantity::tests::accuracy_zero_may_not_be_a_percentage, …::a_percentage_accuracy_stays_within_a_hundred
D3.21a • quantity::tests::unknown_accuracy_is_a_value_and_not_an_absence
D3.22 • verified verbatim against the 1.0.2 class table; MagnitudeStatus::parse is the closed set
D3.23 • quantity::tests::proportion_kind_rules_are_enforced
D3.23a • quantity::tests::proportion_precision_asserts_integrality_rather_than_forbidding_it
D3.24 • quantity::tests::is_abnormal_distinguishes_unknown_from_normal
D3.24a • validation::tests::a_normal_status_outside_the_code_set_is_reported
D3.24b • validation::tests::a_normal_flag_beside_an_abnormal_number_is_reported
D3.25 • encapsulated::tests::debug_does_not_print_the_bytes; guarantees::display_never_reveals_an_identifier_or_a_media_blob
D3.26 • encapsulated::tests::integrity_outcomes_are_distinguished
D3.27 • encapsulated::tests::sha1_is_readable_but_not_writable
D3.28 • invariants::parsable_content_needs_a_formalism
D3.29 • encapsulated::tests::base64_round_trips_including_padding_lengths
D3.30 • uri::tests::schemes_are_checked_not_assumed; guarantees::a_uri_that_never_saw_a_constructor_is_reported_rather_than_panicking; guarantees::an_empty_uri_is_reported_under_openehrs_own_invariant_name
D3.30a • guarantees::a_uri_that_never_saw_a_constructor_is_reported_rather_than_panicking
D3.31 • uri::tests::ehr_uri_refuses_an_external_target; guarantees::an_ehr_uri_deserialized_with_a_foreign_scheme_is_reported; guarantees::a_link_target_is_validated_on_every_locatable_that_carries_it
D3.32 doc uri module header
D3.33 • basic::tests::an_empty_identifier_is_refused
D3.34 • basic::tests::display_never_reveals_the_identifier

§4 Data structures — R4#

Id Status Evidence
R4.1 • canonical_json::every_polymorphic_attribute_carries_its_type_tag
R4.2 type every structure embeds LocatableAttrs
R4.3–R4.4 • data_structures::tests::an_element_is_valued_or_null_and_never_both; validation::tests::deserialization_bypasses_constructors_and_validation_catches_it
R4.5–R4.7 • data_structures::tests::the_four_null_flavours_stay_four; guarantees::the_four_null_flavours_remain_four
R4.8 • invariants::a_cluster_refuses_to_be_empty
R4.9 • invariants::a_ragged_table_is_reported_rather_than_assumed_regular; doctest on ItemTable covers indexing
R4.10 • data_structures::tests::element_traversal_reaches_every_leaf_of_every_structure
R4.11 • data_structures::tests::a_history_needs_events_or_a_summary
R4.12 • invariants::a_periodic_history_needs_a_positive_period
R4.13 • validation::tests::an_event_before_its_history_origin_is_reported
R4.14 doc data_structures module header and IntervalEvent::time
R4.12a–R4.12c • invariants::a_periodic_history_checks_that_its_events_fall_on_the_period; iso8601::tests::differencing_two_instants_crosses_months_and_offsets
R4.15, R4.15a • invariants::an_interval_event_checks_its_width_and_math_function
R4.16 • data_structures::tests::interval_start_time_is_derived_where_it_can_be_and_refused_where_it_cannot, …::interval_start_time_crosses_a_day_boundary_correctly
R4.17 type state and data are separate attributes; exercised by the round-trip fixture

§5 Common — M5#

Id Status Evidence
M5.1 type every clinical class embeds LocatableAttrs
M5.2 • invariants::every_validation_check_fires_on_a_document_that_breaks_it (Archetype_node_id_valid)
M5.3–M5.4 • text::tests::a_coded_payload_without_a_type_does_not_lose_its_code
M5.5 • invariants::only_an_archetype_root_has_a_concept
M5.6 • doctest on Archetyped
M5.7 • validation::tests::an_archetype_id_on_the_wrong_class_is_reported
M5.8 • canonical_json::a_composition_covering_every_modelled_class_round_trips carries rm_version unchanged
M5.9 • uri::tests::ehr_uri_refuses_an_external_target
M5.10 • invariants::feeder_audit_original_content_must_be_encapsulated
M5.11 • invariants::feeder_audit_details_must_name_a_system
M5.12–M5.13 • common::tests::party_proxy_infers_its_class_without_a_type_tag
M5.13a • invariants::conditionally_coded_attributes_are_checked_only_when_openehr_coded
M5.14 • doctest on PartyIdentified
M5.15 • common::tests::a_self_related_party_counts_as_the_subject
M5.16 • invariants::an_anonymous_subject_round_trips_as_itself
M5.17–M5.18 type PARTICIPATION's attributes and the DV_TEXT typing of function are structural
M5.18a • invariants::conditionally_coded_attributes_are_checked_only_when_openehr_coded

§6 EHR — E6#

Id Status Evidence
E6.1 type Ehr holds ObjectRefs throughout
E6.2 • invariants::the_directory_is_the_first_folder_or_there_is_neither
E6.3–E6.4 • ehr::tests::deactivating_a_record_does_not_make_it_unreadable
E6.3a • invariants::a_composition_and_an_ehr_status_must_be_archetype_roots
E6.5 type typed PartySelf
E6.6 • invariants::coded_attributes_are_checked_against_their_openehr_group
E6.6a • invariants::a_composition_and_an_ehr_status_must_be_archetype_roots
E6.6b • invariants::a_persistent_composition_may_not_carry_an_event_context — constructor and validation
E6.7 type required by the constructor's signature
E6.7a doc declared not-enforced; out of scope by S1.10
E6.8 • canonical_json::a_composition_covering_every_modelled_class_round_trips
E6.9 • ehr::tests::entries_are_found_through_nested_sections
E6.10 doc Section documentation
E6.11 • invariants::coded_attributes_are_checked_against_their_openehr_group
E6.12 • ehr::tests::an_event_context_cannot_end_before_it_starts
E6.12a • invariants::an_event_context_location_is_absent_or_non_empty
E6.13 • canonical_json::every_polymorphic_attribute_carries_its_type_tag
E6.14 • ehr::tests::an_entry_about_someone_else_says_so
E6.15 • invariants::administrative_entries_are_distinguishable_from_clinical_ones
E6.16–E6.17 type enforced by the types
E6.18 doc Instruction::new documentation
E6.19 • ehr::tests::an_instruction_needs_at_least_one_activity
E6.20 doc Activity::new documentation
E6.21 • invariants::coded_attributes_are_checked_against_their_openehr_group
E6.22 • ehr::tests::terminal_ism_states_are_recognised
E6.23 • invariants::an_action_must_say_which_activity_it_fulfilled
E6.24 type Folder holds ObjectRefs

§7 Demographics — G7#

Id Status Evidence
G7.1–G7.2 doc demographic module header
G7.3 • demographic::tests::a_party_needs_at_least_one_identity
G7.4 • demographic::tests::a_party_without_a_uid_is_refused
G7.5 • demographic::tests::a_role_is_a_party_but_not_an_actor
G7.6–G7.7 doc type documentation
G7.8 doc PartyIdentity documentation
G7.9 • invariants::a_contact_needs_an_address_and_may_carry_a_validity_period
G7.10 • demographic::tests::an_unrecorded_validity_period_is_not_a_yes
G7.11 • invariants::a_contact_needs_an_address_and_may_carry_a_validity_period
G7.12 type PARTY_RELATIONSHIP references both parties by PartyRef

§8 Change control — V8#

Id Status Evidence
V8.1 • common::tests::a_version_of_another_object_is_refused
V8.2 • common::tests::a_duplicate_version_id_is_refused
V8.3 • doctest on VersionedObject
V8.4–V8.5 • common::tests::concurrent_writes_are_refused_rather_than_silently_ordered
V8.6 • common::tests::version_at_time_skips_incomparable_commit_times
V8.7, V8.7a, V8.7b • invariants::an_empty_history_has_no_revision_history_at_all — order, most-recent, and commit time
V8.8–V8.9 • common::tests::a_deleted_version_may_have_no_data_and_others_may_not
V8.10 • canonical_json::a_versioned_composition_round_trips_with_its_audit_trail
V8.11–V8.12 • invariants::an_imported_version_keeps_the_identity_it_arrived_with
V8.13–V8.14 • doctest on AuditDetails
V8.15–V8.16 • common::tests::contribution_change_types_are_restricted
V8.17 • invariants::an_attestation_carries_what_was_signed_and_whether_it_is_outstanding
V8.17a • invariants::conditionally_coded_attributes_are_checked_only_when_openehr_coded
V8.18 doc Attestation documentation
V8.19 • invariants::an_attestation_carries_what_was_signed_and_whether_it_is_outstanding

§9 Serialization — J9#

Id Status Evidence
J9.1 • canonical_json::a_composition_covering_every_modelled_class_round_trips
J9.2 • canonical_json::round_tripping_twice_is_byte_identical
J9.3 • canonical_json::partial_dates_and_negative_durations_survive_verbatim
J9.4 • canonical_json::every_polymorphic_attribute_carries_its_type_tag
J9.5 • data_types::tests::a_value_without_a_type_tag_is_refused
J9.6 • text::tests::a_coded_payload_without_a_type_does_not_lose_its_code; common::tests::party_proxy_infers_its_class_without_a_type_tag
J9.7 • invariants::a_type_tag_naming_the_wrong_class_is_refused
J9.8–J9.10 • canonical_json::a_payload_written_by_another_implementation_reads
J9.11 • invariants::absent_and_empty_attributes_are_omitted_not_nulled
J9.12 • canonical::tests::nested_objects_are_sorted_at_every_level
J9.13 • canonical::tests::measured_precision_is_not_normalised_away
J9.14 • canonical::tests::keys_sort_by_scalar_value_not_by_locale
J9.15 • canonical_json::reading_a_composition_stays_within_a_small_stack; A-03

§10 Validation — L10#

Id Status Evidence
L10.1, L10.1a, L10.1b • validation::tests::deserialization_bypasses_constructors_and_validation_catches_it
L10.2 doc validation module header
L10.3 • validation::tests::every_violation_is_reported_not_just_the_first
L10.4 • validation::tests::a_coded_text_that_contradicts_its_own_code_is_reported
L10.5 • guarantees::a_validation_report_names_paths_and_never_values
L10.5a • invariants::every_validation_check_fires_on_a_document_that_breaks_it attributes an empty name to DV_TEXT.Valid_value
L10.6 • invariants::every_validation_check_fires_on_a_document_that_breaks_it drives twelve of them from JSON; the DV_ORDERED pair have their own tests
L10.7 • invariants::violations_are_reported_in_document_order_and_that_order_is_stable
L10.8 • text::tests::rubric_checking_reports_unchecked_separately_from_valid
L10.9–L10.10 • the crate-added register in 10-validation.md; openehr-assets fails the build when it and the generated report disagree
L10.11 • the unenforced register, same file, same check — in both directions (D-09 is the same defect one tree over)

§11 Security — X11#

Id Status Evidence
X11.1 doc security module header
X11.2 type AuditDetails is required by OriginalVersion::new's signature
X11.3 • access::tests::an_unimplemented_scheme_round_trips_unchanged
X11.3a • access::tests::an_unimplemented_scheme_denies_and_names_itself
X11.4 • access::tests::nothing_recorded_denies_rather_than_permits
X11.5 • access::tests::an_unimplemented_scheme_denies_and_names_itself
X11.5a • access::tests::operations_are_separately_permitted
X11.6 • guarantees::display_never_reveals_an_identifier_or_a_media_blob
X11.7, X11.7a • guarantees::no_construction_error_echoes_a_submitted_value
X11.8 • redact::tests::sensitive_hides_from_display_and_debug_but_not_from_serde
X11.9 • audit_chain::tests::removing_an_entry_from_the_middle_breaks_the_link
X11.10 doc audit_chain module header
X11.11 • audit_chain::tests::a_clean_chain_verifies_and_an_edited_one_does_not
X11.12 ? Mac implements neither PartialEq nor Eq, so == beside the one comparison does not compile; guarantees::a_forged_tag_is_refused pins the behaviour. Timing is still not measured, and the absence of the derive is not itself tested — see Mac's documentation for why a compile_fail for it passed for the wrong reason
X11.13 • audit_chain::tests::an_unheld_key_is_reported_as_such_and_not_as_forgery, …::a_forged_tag_under_a_held_key_is_a_finding, …::an_unkeyed_chain_does_not_claim_full_verification
X11.14 • audit_chain::tests::key_rotation_is_additive
X11.15 • invariants::a_chain_begins_where_it_begins_and_says_when_it_began_late
X11.16 • audit_chain::tests::short_keys_and_empty_ids_are_refused
X11.17 • audit_chain::tests::key_debug_does_not_print_the_material
X11.18 • encapsulated::tests::sha1_is_readable_but_not_writable
X11.19 • guarantees::a_chain_checkpoint_carries_no_patient_data
X11.20 • redact::tests::a_masked_element_says_the_value_exists
X11.21 • redact::tests::a_redacted_composition_is_still_valid
X11.22 • guarantees::redaction_masks_and_reports_a_count_not_a_category
X11.23 • redact::tests::a_reason_appears_and_does_not_disclose_the_category
X11.24 ? redact returns Result and yields nothing on error, and the error path cannot be provoked. The premise is now tested — guarantees::no_document_this_crate_can_build_carries_a_non_finite_float — because serde_json writes null for a non-finite float rather than failing, so the constructors are the only barrier (A-10)
X11.25 • guarantees::redaction_masks_and_reports_a_count_not_a_category

§12 Paths and query — Q12#

Id Status Evidence
Q12.1 • path::tests::a_full_path_reaches_a_magnitude, …::name_predicates_select_the_right_repeat
Q12.2 • path::tests::malformed_paths_report_where_they_broke
Q12.3–Q12.4 • path::tests::index_predicates_are_zero_based_and_quoted_digits_are_names
Q12.5 • guarantees::an_ambiguous_path_refuses_instead_of_choosing
Q12.6 • path::tests::a_wrong_attribute_is_no_match_not_an_error
Q12.7 • path::tests::a_full_path_reaches_a_magnitude
Q12.7a, Q12.7b • path::tests::reference_ranges_are_navigable, …::an_interval_valued_element_is_navigable
Q12.8 • path::tests::predicates_round_trip_through_display_in_long_form
Q12.9 • aql::tests::the_canonical_blood_pressure_query_parses, …::aggregates_and_distinct_parse, …::not_contains_parses_and_keeps_its_negation, …::like_and_offset_parse
Q12.9a • guarantees::aql_refuses_what_it_does_not_model_and_says_where_that_is_recorded
Q12.9b • signed numeric literals parse, resolved at operand position — aql::a_sign_is_a_number_where_a_value_belongs_and_nowhere_else (A-27, fixed)
Q12.9d • LIMIT -5 and OFFSET -1 refused with a message naming the reason — aql::a_negative_limit_or_offset_is_refused_rather_than_clamped
Q12.9e • a real renders with a decimal point so it lexes back a real — guarantees::aql_rendering_round_trips_through_the_parser, which compares trees
Q12.9c • declared limitation: no node-id predicate shorthand — aql::only_a_dashed_and_dotted_word_standing_alone_is_an_archetype_id (A-30)
Q12.10 — no execution API exists
Q12.11 • aql::tests::keywords_are_case_insensitive
Q12.12 • aql::tests::malformed_queries_report_an_offset
Q12.13 • aql::tests::parameters_are_collected_from_every_clause_and_deduplicated
Q12.14 • guarantees::aql_catches_a_path_rooted_at_an_unbound_alias
Q12.15 • aql::tests::a_parsed_query_reparses_from_its_own_rendering; guarantees::aql_rendering_round_trips_through_the_parser, which compares the tree and not only the text; cosmetic spacing difference noted in A-05
Q12.15a • guarantees::aql_rendering_round_trips_through_the_parser over the CONTAINS/OR shapes that broke it (A-37)
Q12.15b • guarantees::an_aql_string_literal_is_not_mangled_by_the_lexer (A-37)

§13 Conformance testing — T13#

Id Status Evidence
T13.1 • 222 of 276 requirements cite a test; 3 remain ? and are named above
T13.2 ? cargo-mutants over every module of meaningful size in openehr (all now 0 or equivalent survivors — see the lib:A-09 table in spec/audit.md for the full per-module count) plus openehr-sqlite/{store,dialect}.rs, openehr-store/{integrity,record,dialect}.rs, openehr-loco's auth/controllers/access/app/tasks/views, and the five schema-level engine crates' Dialect impls. openehr-store/conformance.rs — the logic those numbers actually exercise — is measured from openehr-sqlite, not from openehr-store itself, because nothing in openehr-store's own test target calls it. Not in CI as a standing gate (the mutants job covers only a PR's diff); openehr-postgresql/mysql/mariadb/mssql/oracle's store.rs-equivalent logic doesn't exist (Schema level, no Store impl) and App::before_run has one structural residual — see spec/audit.md — and the six -fuzz crates are untouched — A-09
T13.3 • every test in tests/guarantees.rs states its failure mode
T13.4 • canonical_json
T13.5–T13.6 • tests/guarantees.rs
T13.7 • canonical_json::a_payload_written_by_another_implementation_reads
T13.8 • 79 doctests run in cargo test
T13.9 — no test in this crate self-skips
T13.10 • this file
T13.11 • reading_a_composition_stays_within_a_small_stack names its toolchain, method, and figures
T13.12–T13.13 • cargo clippy --all-targets is clean with the lint table in Cargo.toml, and #![forbid(unsafe_code)] at the crate root states it in the source as well — belt and braces, because a manifest edit removes the one and not the other
T13.14 • every #[allow] in the crate carries a reason

§15 Archetypes and templates — K15#

The section was added on 2026-08-26 when S1.4 was withdrawn. Fourteen rows are satisfied and eighteen are not: openehr::am is the AOM2 object model, openehr::am::validate (2026-08-30) checks a Reference Model instance against an Archetype already held in memory, and openehr::am::repository plus validate_with_repository (2026-08-30, same day) resolve a C_ARCHETYPE_ROOT filler through a caller-supplied repository — openehr performs no I/O itself (K15.25). No code in this crate parses ADL, flattens a specialised archetype, or expands a template — so what validate_with_repository checks is the definition each resolved archetype gives as its own, not as a flattened OPT2 would be, and that scope limit is stated in its own module documentation rather than left for the row below to imply more. A bare ARCHETYPE_SLOT stays unchecked even with a repository, because which archetype filled it lives on the instance's ARCHETYPED.archetype_id, an attribute crate::path::Node does not expose — a gap in crate::path, not in this section. A-40 tracks the rest.

This table exists so that the gap is counted rather than described. A row moves off spec when the code implements it and a named test exercises it (C0.7) — not when a parser lands, and not when a README says so (K15.30, K15.31).

Id Status Evidence
K15.1 • am::archetype::tests::a_definition_constraining_the_wrong_rm_class_is_refused, …a_node_the_terminology_does_not_define_is_refused, …a_code_specialised_deeper_than_its_archetype_is_refused, …a_terminology_constraint_naming_no_value_set_is_refused; am::constraint::tests::two_constraints_on_one_attribute_are_refused
K15.2 • archetype_model::the_targeted_archetype_model_release_is_named; am::AM_RELEASE is 2.3.0 and an artefact's own declared versions round-trip unenforced
K15.3 • archetype_model::an_archetype_round_trips_through_json_unchanged, …a_constraint_this_crate_cannot_model_survives_rather_than_disappearing. Scope: the only serialisation this crate accepts today is its own JSON. ADL and the AM ITS forms are K15.5, K15.8, and K15.16, all below
K15.4 • archetype_model::an_archetype_is_constructible_without_a_parser
K15.5–K15.7 spec ADL 2 parsing, and the refusal discipline that replaces recovery
K15.8–K15.10 spec ADL 1.4 ingestion, provenance, and the assertion subset
K15.11–K15.13 spec specialisation, flattening, and the narrowing check
K15.14–K15.17 spec template expansion and operational templates, both directions
K15.18–K15.23 • am::validate::tests::a_matching_instance_is_conformant, …a_missing_mandatory_element_is_a_violation, …an_unrecognised_node_id_is_a_violation, …an_alternative_below_its_own_mandatory_occurrences_is_a_violation, …a_slot_is_reported_unchecked_never_as_passing, …a_c_integer_range_rejects_a_value_outside_it, …a_c_string_pattern_is_unchecked_even_when_the_list_passes, …a_c_terminology_code_checks_against_the_archetypes_own_value_set, …the_wrong_root_rm_class_is_a_single_violation_not_a_cascade. Scope: against an Archetype already in memory, not a flattened OPT2 (K15.11, K15.15 are not implemented) — see openehr::am::validate's own module documentation
K15.24–K15.27 • am::repository::tests::a_resolved_archetype_without_provenance_says_so, …a_resolved_archetype_with_provenance_carries_it, …each_failure_kind_names_what_happened, …a_fixed_repository_can_stand_in_for_a_real_one_in_a_test; am::validate::tests::a_repository_resolved_filler_validates_the_same_subtree, …a_retrieval_failure_is_unchecked_and_names_what_happened, …a_repository_returning_a_different_archetype_is_unchecked_not_used, …unestablished_provenance_is_unchecked_unless_the_caller_opts_in. Scope: resolves C_ARCHETYPE_ROOT fillers only — a bare ARCHETYPE_SLOT stays unchecked even with a repository supplied, because which archetype filled it is recorded on the instance's ARCHETYPED.archetype_id, which crate::path::Node does not expose (a gap in crate::path, not here)
K15.28–K15.29 spec the boundaries this section does not move: authoring, publishing, AQL execution
K15.30–K15.31 spec the honesty gate while the rest is unbuilt — refuse, and do not describe a parser as archetype support

View this page's source on GitHub — the crates are the source of truth; this site renders them.