Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Comparison with upstream EHRbase

FerroEHR is measured against upstream EHRbase (Java) — the project it succeeds — on the same instruments it applies to itself: the CNF 2.0 conformance runner executes the same committed catalogue against both servers’ official deployments, and the benchmark harness drives both with byte-identical clinical workloads on the same host. Both directions are always published; a result that favours upstream is reported exactly like one that favours us.

Each side runs with its own committed party set — an ixit describing the reachable instances (upstream’s Basic auth has no read-only principal, so its ixit declares none) and a statement (the ICS) declaring the capabilities and ambiguity-register options that party actually claims. A capability a party does not claim reads not claimed and never gates its verdicts; a test whose ground cannot exist on a party’s topology or technology profile reads not applicable with a machine citation, never fail. That is how the comparison stays fair without ever weakening a case.

Every number and every curve on this page is generated at build time from the committed run artifacts (docs/conformance/*/results.json + verdicts.json + stress.json) — nothing here is hand-typed, and the CI stale-numbers gate rejects any attempt to hand-type it. To reproduce either side yourself, see Conformance and Benchmarks.

Conformance

Systems under test

ferroehrupstream (Java)
Productferroehr 3.15.0ehrbase-java 2.34.0
Run date2026-07-312026-07-31
Party statementtools/cnf-runner/party/ferroehr/tools/cnf-runner/party/ehrbase-java/
Stackroot compose, built from the current sourcesdocker/sut-ehrbase-java.yml (official images)

Methodology

Both systems execute the same committed CNF 2.0 catalogue (863 case-by-format executions) through the same reference runner (tools/cnf-runner), each on fresh volumes with its own committed party set: the ixit names the reachable instances (upstream declares no readonly principal), and the statement (the ICS) declares the claimed capabilities, spec versions, and ambiguity-register options — ISO/IEC 9646-style test selection excuses undeclared option branches, unclaimed capabilities, and release-dated behaviour outside the declared versions as N/A with a citation, never as silent skips. Verdicts are pure functions of (statement, results, catalogue, capability matrix).

The declared-version delta matters and is stated, not hidden: ferroehr declares ITS-REST 1.1.0 while upstream EHRbase declares ITS-REST 1.0.3 — the catalogue realizes 1.1.0, so every Release-1.1.0-dated behaviour (the Demographic API, ITEM_TAGs, Simplified Formats on the wire, the admin EHR delete, the weak-ETag/Location header forms, …) is cited N/A for the 1.0.3 declaration rather than driven against a release upstream never claimed. The verdict-bearing comparison below is therefore each party’s in-scope subset, never the raw record.

Profile verdicts

Profileferroehrupstream (Java)
COREpassfail
STANDARDpassfail
OPTIONSpassnot claimed
SEC-BASICpassnot claimed

In-scope outcomes

Runs compared: ferroehr (run of 2026-07-31) vs upstream EHRbase 2.34.0 (run of 2026-07-31) — the SAME catalogue through the same runner, each with its own committed party statement. Per the presentation rule, the headline is each party’s VERDICT SCOPE (the cases its own declarations select), never the raw record: a raw count would book release-dated and unclaimed surfaces against a party that never claimed them.

verdict scope (selected)drivenin-scope passedin-scope failedin-scope inconclusive
ferroehr86382682600
upstream (Java)499459136132191

An inconclusive row’s wire answered outside the operation’s bound outcome map, or its required ground could not be established (e.g. a refused provisioning exchange) — never counted as a failure of the behaviour under test. Every not-run row in the full committed record (docs/conformance/<sut>/results.json) carries a machine-readable citation: an undeclared option branch, an unclaimed capability, a release-dated behaviour outside the declared spec versions, or a ground the party’s topology cannot establish.

Capability-by-capability

Evidence tokens from each party’s computed verdicts: passed (every gating case green), failed (at least one gating case red), inconclusive (a gating case neither passed nor failed cleanly), not_evidenced (claimed, but no gating case produced a verdict — there is no excused state: a required capability without passing evidence fails its tier, whichever party claims it), or not claimed (absent from that party’s ICS).

Capabilityferroehrupstream (Java)
ActivityReportpassednot_evidenced
Adl14ArchetypeProvisioningpassednot_evidenced
Adl14OptProvisioningpassedfailed
Adl2ArchetypeProvisioningpassednot_evidenced
Adl2OptProvisioningpassednot_evidenced
AdminApipassednot_evidenced
AnonymousEhrspassednot_evidenced
AqlAdvancedpassedinconclusive
AqlBasicpassedfailed
AqlTerminologypassednot_evidenced
ArchetypeValidationpassedfailed
AuditAccountabilitypassednot_evidenced
AuthenticatedAccesspassedpassed
AuthorizationSeparationpassednot_evidenced
BulkEhrLoadpassednot_evidenced
ChangeSetspassedfailed
CompositionOpspassedinconclusive
DefinitionApipassedfailed
DemographicApipassednot_evidenced
DemographicArchetypeValidationpassednot_evidenced
DemographicArchivepassednot_evidenced
DirectoryOpspassedfailed
EhrApipassedfailed
EhrArchivepassednot_evidenced
EhrDemographicSeparationpassedpassed
EhrDumpLoadpassednot_evidenced
EhrExtractpassednot_evidenced
EhrOperationspassedfailed
EhrStatuspassedfailed
ItemTagspassednot_evidenced
MessageApipassednot_evidenced
PartyOperationspassednot_evidenced
PartyRelationshipOperationspassednot_evidenced
PhysicalDeletionpassednot_evidenced
QueryApipassedfailed
QueryProvisioningpassedfailed
Signingpassednot_evidenced
SimplifiedFormatspassednot_evidenced
SmartAppLaunchpassednot_evidenced
SystemApipassednot_evidenced
Tdspassednot_evidenced
TemplateExamplespassednot_evidenced
Versioningpassedfailed

Failures — both directions

ferroehr failures (with the upstream outcome on the identical case)

CaseFormatFailureupstream outcome
none — zero failing cases

Upstream failures by schedule chapter

Chapterfailed cases
CONT67
I_EHR_STATUS24
I_EHR_DIRECTORY12
I_DEFINITION_QUERY10
I_DEFINITION_ADL27
I_EHR_CONTRIBUTION7
I_DEFINITION_ADL146
I_EHR_SERVICE5
I_QUERY_SERVICE3
I_EHR_COMPOSITION1
I_ITS_REST_REVISION_HISTORY1
SIG1
Every upstream-failed case, with the ferroehr outcome on the identical case
CaseFormatUpstream failureferroehr outcome
CONT-COMP-content_card_1plus-context_anyexpected created, observed validation_failedpassed
CONT-COMP-content_card_1plus-context_mandexpected created, observed validation_failedpassed
CONT-COMP-content_card_3plus-context_anyexpected created, observed validation_failedpassed
CONT-COMP-content_card_3plus-context_mandexpected created, observed validation_failedpassed
CONT-COMP-content_card_3to5-context_anyexpected created, observed validation_failedpassed
CONT-COMP-content_card_3to5-context_mandexpected created, observed validation_failedpassed
CONT-COMP-content_card_any-context_anyexpected created, observed validation_failedpassed
CONT-COMP-content_card_any-context_mandexpected created, observed validation_failedpassed
CONT-COMP-content_card_mand-context_anyexpected created, observed validation_failedpassed
CONT-COMP-content_card_mand-context_mandexpected created, observed validation_failedpassed
CONT-COMP-content_card_opt-context_anyexpected created, observed validation_failedpassed
CONT-COMP-content_card_opt-context_mandexpected created, observed validation_failedpassed
CONT-COMPOSITION-content_cardinality_count6expected created, observed validation_failedpassed
CONT-COMPOSITION-context_existenceexpected created, observed validation_failedpassed
CONT-DV_CODED_TEXT-validate_openexpected created, observed validation_failedpassed
CONT-DV_DATE-validate_constraintexpected created, observed validation_failedpassed
CONT-DV_DATE-validate_rangeexpected created, observed validation_failedpassed
CONT-DV_DATE_TIME-validate_constraintexpected created, observed validation_failedpassed
CONT-DV_DATE_TIME-validate_rangeexpected created, observed validation_failedpassed
CONT-DV_DURATION-validate_fieldsexpected created, observed validation_failedpassed
CONT-DV_DURATION-validate_fields_rangeexpected created, observed validation_failedpassed
CONT-DV_DURATION-validate_rangeexpected created, observed validation_failedpassed
CONT-DV_IDENTIFIER-validate_all_listexpected created, observed validation_failedpassed
CONT-DV_IDENTIFIER-validate_all_patternexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_DATE-validate_lower_upper_constraintexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_DATE-validate_lower_upper_rangeexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_DATE_TIME-validate_lower_upper_constraintexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_DATE_TIME-validate_lower_upper_rangeexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_DURATION-validate_constraintexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_DURATION-validate_rangeexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_ORDINAL-validate_constraintexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_PROPORTION-validate_ratio_rangeexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_SCALE-validate_constraintexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_TIME-validate_lower_upper_constraintexpected created, observed validation_failedpassed
CONT-DV_INTERVAL_DV_TIME-validate_lower_upper_rangeexpected created, observed validation_failedpassed
CONT-DV_MULTIMEDIA-validate_media_typeexpected created, observed validation_failedpassed
CONT-DV_PARSABLE-validate_value_formalismexpected created, observed validation_failedpassed
CONT-DV_TEXT-validate_openexpected created, observed validation_failedpassed
CONT-DV_TIME-validate_constraintexpected created, observed validation_failedpassed
CONT-DV_TIME-validate_rangeexpected created, observed validation_failedpassed
CONT-EVENT-state_ex_mandexpected created, observed validation_failedpassed
CONT-EVENT-state_ex_optexpected created, observed validation_failedpassed
CONT-EVENT-type_anyexpected created, observed validation_failedpassed
CONT-EVENT-type_interval_eventexpected created, observed validation_failedpassed
CONT-EVENT-type_point_eventexpected created, observed validation_failedpassed
CONT-HIST-events_card_1plus-summary_ex_mandexpected created, observed validation_failedpassed
CONT-HIST-events_card_1plus-summary_ex_optexpected created, observed validation_failedpassed
CONT-HIST-events_card_3plus-summary_ex_mandexpected created, observed validation_failedpassed
CONT-HIST-events_card_3plus-summary_ex_optexpected created, observed validation_failedpassed
CONT-HIST-events_card_3to5-summary_ex_mandexpected created, observed validation_failedpassed
CONT-HIST-events_card_3to5-summary_ex_optexpected created, observed validation_failedpassed
CONT-HIST-events_card_any-summary_ex_mandexpected created, observed validation_failedpassed
CONT-HIST-events_card_any-summary_ex_optexpected created, observed validation_failedpassed
CONT-HIST-events_card_mand-summary_ex_mandexpected created, observed validation_failedpassed
CONT-HIST-events_card_mand-summary_ex_optexpected created, observed validation_failedpassed
CONT-HIST-events_card_opt-summary_ex_mandexpected created, observed validation_failedpassed
CONT-HIST-events_card_opt-summary_ex_optexpected created, observed validation_failedpassed
CONT-HISTORY-events_cardinality_count6expected created, observed validation_failedpassed
CONT-ITEM_STR-type_anyexpected created, observed validation_failedpassed
CONT-ITEM_STR-type_item_listexpected created, observed validation_failedpassed
CONT-ITEM_STR-type_item_singleexpected created, observed validation_failedpassed
CONT-ITEM_STR-type_item_tableexpected created, observed validation_failedpassed
CONT-ITEM_STR-type_item_treeexpected created, observed validation_failedpassed
CONT-OBS-state_ex_mand-protocol_ex_mandexpected created, observed validation_failedpassed
CONT-OBS-state_ex_mand-protocol_ex_optexpected created, observed validation_failedpassed
CONT-OBS-state_ex_opt-protocol_ex_mandexpected created, observed validation_failedpassed
CONT-OBS-state_ex_opt-protocol_ex_optexpected created, observed validation_failedpassed
I_DEFINITION_ADL14.upload_opt-invalid_optexpected validation_failed, observed not_acceptablepassed
I_DEFINITION_ADL14.upload_opt-valid_optexpected created, observed not_acceptablepassed
I_DEFINITION_ADL14.upload_opt-valid_opt_twice_conflictexpected created, observed not_acceptablepassed
I_DEFINITION_ADL14.upload_opt-valid_opt_twice_no_conflictexpected created, observed not_acceptablepassed
I_DEFINITION_ADL14.validate_opt-invalid_optexpected validation_failed, observed not_acceptablepassed
I_DEFINITION_ADL14.validate_opt-valid_optexpected created, observed not_acceptablepassed
I_DEFINITION_ADL2.get_artefact-example_unknownexpected not_found, observed not_acceptablepassed
I_DEFINITION_ADL2.get_artefact-version_prefixexpected created, observed not_acceptablepassed
I_DEFINITION_ADL2.upload_artefact-duplicate_conflictexpected created, observed not_acceptablepassed
I_DEFINITION_ADL2.upload_artefact-invalid_artefactsexpected validation_failed, observed not_acceptablepassed
I_DEFINITION_ADL2.upload_artefact-valid_optexpected created, observed not_acceptablepassed
I_DEFINITION_ADL2.valid_artefact-invalidexpected validation_failed, observed not_acceptablepassed
I_DEFINITION_ADL2.valid_artefact-validexpected created, observed not_acceptablepassed
I_DEFINITION_QUERY.list_queries-prefix_all_versions[0]/name: path resolves to nothingpassed
I_DEFINITION_QUERY.list_queries-version_get_xml_not_acceptableexpected not_acceptable, observed okpassed
I_DEFINITION_QUERY.list_queries-xml_not_acceptableexpected not_acceptable, observed okpassed
I_DEFINITION_QUERY.store_query-default_slot_with_higher_versionheader Location: value “http://localhost:8091/ehrbase/rest/openehr/v1/definition/query/orgpassed
I_DEFINITION_QUERY.store_query-dotted_nameexpected stored, observed bad_requestpassed
I_DEFINITION_QUERY.store_query-unqualified_nameexpected stored, observed bad_requestpassed
I_DEFINITION_QUERY.store_query-update_in_placeheader Location: value “http://localhost:8091/ehrbase/rest/openehr/v1/definition/query/orgpassed
I_DEFINITION_QUERY.store_query-version_duplicate_case_variant_nameexpected conflict, observed storedpassed
I_DEFINITION_QUERY.store_query-version_prefix_rejectedexpected bad_request, observed storedpassed
I_DEFINITION_QUERY.store_query-version_prerelease_rejectedexpected bad_request, observed storedpassed
I_EHR_COMPOSITION.get_versioned_composition-malformed_uidexpected bad_request, observed not_foundpassed
I_EHR_CONTRIBUTION.commit_contribution-delete_directoryexpected created, observed not_foundpassed
I_EHR_CONTRIBUTION.commit_contribution-deleted_member_with_dataexpected validation_failed, observed createdpassed
I_EHR_CONTRIBUTION.commit_contribution-ehr_status_incomplete_lifecycleexpected validation_failed, observed createdpassed
I_EHR_CONTRIBUTION.commit_contribution-ehr_status_invalid_change_typeexpected conflict, observed validation_failedpassed
I_EHR_CONTRIBUTION.commit_contribution-ehr_status_invalid_change_type_deletedexpected conflict, observed not_foundpassed
I_EHR_CONTRIBUTION.commit_contribution-fail_modify_non_existing_directoryexpected validation_failed, observed precondition_failedpassed
I_EHR_CONTRIBUTION.commit_contribution-non_exiting_optexpected template_not_found, observed validation_failedpassed
I_EHR_DIRECTORY.create_directory-ehr_not_modifiableexpected updated, observed precondition_missingpassed
I_EHR_DIRECTORY.delete_directory-ehr_with_directoryheader Last-Modified: expected present, got nonepassed
I_EHR_DIRECTORY.delete_directory-empty_ehrexpected not_found, observed precondition_failedpassed
I_EHR_DIRECTORY.delete_directory-etag_names_new_versionheader Last-Modified: expected present, got nonepassed
I_EHR_DIRECTORY.get_directory-deleted_headheader Last-Modified: expected present, got nonepassed
I_EHR_DIRECTORY.get_directory-directory_with_structureequivalent: retrieved content differs from committed (modulo the normative ignore-set); $/passed
I_EHR_DIRECTORY.get_directory_at_time-deleted_at_timeheader Last-Modified: expected present, got nonepassed
I_EHR_DIRECTORY.get_directory_at_version-deleted_versionheader Last-Modified: expected present, got nonepassed
I_EHR_DIRECTORY.update_directory-empty_ehrexpected not_found, observed precondition_failedpassed
I_EHR_DIRECTORY.update_directory-invalid_folderexpected validation_failed, observed precondition_missingpassed
I_EHR_DIRECTORY.update_directory-stale_if_matchheader ETag: expected the latest version uid, got nonepassed
I_EHR_DIRECTORY.update_directory-xmlcanonical-xmlexpected updated, observed precondition_missing
I_EHR_SERVICE.create_ehr-bulk_load_populationexpected created, observed validation_failedpassed
I_EHR_SERVICE.create_ehr-committal_headerscommit_audit/description/value: path resolves to nothingpassed
I_EHR_SERVICE.create_ehr-invalid_statusexpected validation_failed, observed createdpassed
I_EHR_SERVICE.create_ehr-wrong_methodheader Allow: expected a value matching “.*(GET.*POST|POST.GET).”, got nonepassed
I_EHR_SERVICE.get_ehr-malformed_ehr_idexpected bad_request, observed not_foundpassed
I_EHR_STATUS.clear_ehr_modifiable-bad_ehrexpected not_found, observed precondition_missingpassed
I_EHR_STATUS.clear_ehr_modifiable-existing_ehrexpected updated, observed precondition_missingpassed
I_EHR_STATUS.clear_ehr_modifiable-stale_if_matchexpected updated, observed precondition_missingpassed
I_EHR_STATUS.clear_ehr_queryable-bad_ehrexpected not_found, observed precondition_missingpassed
I_EHR_STATUS.clear_ehr_queryable-existing_ehrexpected updated, observed precondition_missingpassed
I_EHR_STATUS.clear_ehr_queryable-stale_if_matchexpected updated, observed precondition_missingpassed
I_EHR_STATUS.get_ehr_status-at_time_futureexpected updated, observed precondition_missingpassed
I_EHR_STATUS.get_ehr_status-at_time_omittedexpected updated, observed precondition_missingpassed
I_EHR_STATUS.get_ehr_status_at_version-addressed_versionexpected updated, observed precondition_missingpassed
I_EHR_STATUS.get_versioned_ehr_status-at_time_futureexpected updated, observed precondition_missingpassed
I_EHR_STATUS.get_versioned_ehr_status-at_time_omittedexpected updated, observed precondition_missingpassed
I_EHR_STATUS.get_versioned_ehr_status-contained_uid_formheader Last-Modified: expected present, got nonepassed
I_EHR_STATUS.get_versioned_ehr_status-container_shapeowner_id/type: “ehr” != expected “EHR”passed
I_EHR_STATUS.get_versioned_ehr_status-xmlcanonical-xmlheader Last-Modified: expected present, got nonepassed
I_EHR_STATUS.set_ehr_modifiable-bad_ehrexpected not_found, observed precondition_missingpassed
I_EHR_STATUS.set_ehr_modifiable-existing_ehrexpected updated, observed precondition_missingpassed
I_EHR_STATUS.set_ehr_modifiable-missing_if_matchexpected updated, observed precondition_missingpassed
I_EHR_STATUS.set_ehr_modifiable-stale_if_matchexpected updated, observed precondition_missingpassed
I_EHR_STATUS.set_ehr_modifiable-xml_bodycanonical-xmlexpected updated, observed precondition_missing
I_EHR_STATUS.set_ehr_queryable-bad_ehrexpected not_found, observed precondition_missingpassed
I_EHR_STATUS.set_ehr_queryable-existing_ehrexpected updated, observed precondition_missingpassed
I_EHR_STATUS.set_ehr_queryable-missing_if_matchexpected updated, observed precondition_missingpassed
I_EHR_STATUS.set_ehr_queryable-stale_if_matchexpected updated, observed precondition_missingpassed
I_EHR_STATUS.set_ehr_queryable-xml_bodycanonical-xmlexpected updated, observed precondition_missing
I_ITS_REST_REVISION_HISTORY.versioned_ehr_status_revision_history-two_versionscanonical-jsonexpected updated, observed precondition_missingpassed
I_QUERY_SERVICE.execute_ad_hoc_query-empty_db_bare_ehrrow count 100 != expected 1passed
I_QUERY_SERVICE.execute_ad_hoc_query-unknown_ehr_scopeexpected not_found, observed okpassed
I_QUERY_SERVICE.execute_stored_query-fetch_with_topexpected stored, observed bad_requestpassed
SIG-VERSION-ehr_status_signaturesignature: expected present, the ORIGINAL_VERSION envelope carries no signaturepassed

Both servers’ capability conformance, from each party’s committed verdicts (generated, diff-guarded — see Conformance for how to read the grid):

And the per-chapter outcomes side by side. Both charts render the same chapter-and-band taxonomy, so they read band-for-band: a band upstream did not exercise shows as an explicit no cases row in the same position. Compare the printed counts, not the bar lengths — each chart scales its bars to its own widest band, and the legend states that scale.

Reading the upstream results honestly: the failures concentrate where the catalogue pins strict specification behaviour — archetype-constraint validation depth (the content chapter), exact status codes and version headers, canonical-format details — that upstream implements differently or predates. An inconclusive row means upstream answered with a status the operation’s specification-cited outcome map does not contain, or refused the exchange that would have established the case’s required ground, so the runner refuses to guess a verdict. And where upstream simply does not implement a surface (the ITS-REST simplified-format media types, ADL 2 provisioning, demographics), or where the specification dates a behaviour to a REST-API release newer than the one upstream declares (upstream declares ITS-REST 1.0.3; the catalogue realizes 1.1.0), the result reads not claimed or N/A with a citation — upstream is never counted as failing a surface it never claimed, never against a release it never declared, and never on an ferroehr-only extension.

The principal upstream divergences, stated plainly

Each of these was reproduced live against the composed upstream stack during triage of the committed record, and each is grounded at or below upstream’s own declared ITS-REST 1.0.3 unless marked; the full wire evidence lives in the committed docs/conformance/ehrbase-java/results.json.

  • A quoted If-Match value is rejected (400 "UUID string too large") — including the server’s own echoed ETag — while only the non-standard unquoted form is accepted. The quoted form dates to Release 1.0.2.
  • Semantic model violations answer 400 instead of 422 (a Release 1.0.1 correction), and some model-invalid documents are accepted outright — an EHR_STATUS without its mandatory archetype details commits as 201.
  • The openehr-audit-details committal header is ignored (both the current and the deprecated spelling), and the stored audit description is itself model-invalid (a DV_TEXT with no value).
  • Canonical XML is served with the root element in no namespace, against the published XSD’s qualified target namespace; the stored-query list even serves an XML <List/> document that conforms to no published schema on a JSON-only operation.
  • Unqualified stored-query names are rejected although the specification makes the namespace optional and lists my_compositions as a valid example; a stale If-Match on a directory delete answers 404 where the specification requires 412; and 405 responses omit the required Allow header.
  • The stored-query listing does not match by prefix. The specification’s own worked example lists “all versions of all queries with names starting with org.openehr”; upstream returns the versions of an exactly-named query but answers 200 [] for any shorter prefix, so a client cannot discover what a namespace holds.
  • A malformed identifier in the path answers 404, not 400. Given a path segment that is not a UUID at all, upstream detects the type violation and still reports a miss — literally "EHR not found, in fact, only UUID-type IDs are supported" — for the EHR, versioned-COMPOSITION and CONTRIBUTION reads alike. (It does answer 400 for a malformed version identifier, so the behaviour is not even internally consistent.)
  • Directory writes against an EHR that has no directory answer 412. Both the update and the delete report Precondition Failed with the body “does not contain a directory” — a missing resource dressed as a failed precondition. HTTP requires the opposite order: a failure detectable before the precondition is evaluated takes precedence over evaluating it.
  • A contribution refusal surfaces as a raw 500. Committing a first version whose change type is deleted returns 500 "An internal error has occurred", where the specification assigns that family a 400 (“the modification type does not match the operation”). The neighbouring row is worse than an error: a creation whose lifecycle state is deleted is accepted (204) instead of refused.
  • (1.1.0-grounded) The template upload refuses Accept: application/json (406), serving only XML — the released parameter enumeration lists JSON first. This single refusal is what makes most content-chapter rows inconclusive: the runner’s provisioning uploads ask for JSON, upstream refuses, and the case’s ground never exists.

Two red rows are not upstream’s fault, and are called out rather than counted. Storing a query with no version in the URL has to land at some version, and no released sentence says which. Our suite pins 1.0.0 because a suite must pin something; upstream continues the existing series instead (a stored 2.0.0 makes the next version-less store 3.0.0). Both are defensible readings of a silence, so the two store_query-{default_slot_with_higher_version,update_in_place} rows record a difference of house convention, not a conformance defect — the open question is with openEHR, not with either implementation.

Performance

Both systems run the same committed step-load stress instrument (cnf-runner stress) on their own freshly seeded cnf.scale.10k corpus: the geometric ladder climbs until the system leaves the envelope (p99 over the budget or errors past tolerance), then bisects to the maximum sustainable throughput. Every number derives from the two committed stress.json reports; each load step embeds re-checkable histograms and, where sampled, per-container resource telemetry.

max sustainable throughputworst p99 at the kneeDB peak CPU at the kneeSUT peak RSS at the knee
ferroehr512 req/s134 ms101 %247 MB
upstream (Java)0 req/s— ms— %— MB

A stress report is exploration evidence: it earns no conformance class (classes are earned exclusively by the hour-long measured class runs) and carries no class vocabulary — the chart shows where each system breaks.

Method, in one paragraph

The conformance instrument derives every expected outcome from the openEHR specifications — never from either server’s observed behaviour — and runs against real composed deployments of both systems (scripts/conformance.sh, CONF_SUT=ehrbase-java for the upstream side). The stress instrument drives the same hospital-simulation workload (admissions, observations, medication rounds, lab contributions, chart reviews, corrections, discharges) built from official CKM templates with seeded determinism, so both servers receive byte-identical requests; latencies are coordinated-omission-corrected from each request’s planned arrival instant, and the ladder bisects to the last rate held inside the envelope. The full method chapters: Conformance · Benchmarks.