Longitudinal Journey contracts¶
OpenMed v3 uses six immutable records as the boundary between ingestion, clinical extraction, reconciliation, applications, and storage:
ClinicalArtifactidentifies source or derived bytes without embedding them.EvidenceLocatorpoints to exact text, structured, document, DICOM, or table coordinates in an artifact.ClinicalFactrecords one typed value with evidence and a derivation digest.ConflictSetgroups facts that cannot all be treated as current truth.ResolutionEventappends a policy or human review decision without rewriting the underlying facts.DatasetSnapshotpins the inputs, schemas, files, components, licenses, and splits of a governed dataset build.
These records are assistive data contracts. They do not establish clinical truth, clinical validation, or authorization for autonomous patient-care actions.
Deterministic identifiers and digests¶
Identifiers are opaque and carry only a controlled record-kind prefix. Use new_opaque_id() for an unlinkable random identifier or derived_opaque_id() when identical canonical inputs must produce the same identifier.
from openmed.clinical import (
ClinicalArtifact,
derived_opaque_id,
new_opaque_id,
sha256_digest,
)
source_id = new_opaque_id("source")
artifact = ClinicalArtifact(
artifact_id=derived_opaque_id("artifact", source_id, "synthetic-note-1"),
artifact_type="clinical_note",
media_type="text/plain",
content_hash=sha256_digest(b"Synthetic fixture content"),
byte_size=25,
source_id=source_id,
recorded_at="2026-01-02T03:04:05Z",
)
assert artifact.to_json() == ClinicalArtifact.from_json(artifact.to_json()).to_json()
canonical_json() sorts object keys, rejects non-finite numbers, and emits a compact byte-stable representation. canonical_digest() hashes that exact representation. compute_derivation_hash() additionally binds a component, component version, sorted input identifiers, and JSON configuration.
Evidence coordinate types¶
An EvidenceLocator accepts one of six explicit coordinate shapes:
| Type | Required coordinates |
|---|---|
text_span | zero-based half-open start and end |
json_pointer | RFC 6901-style pointer |
message_field | bounded structured path such as PID.3.1 |
page_box | one-based page, [x0, y0, x1, y1], and coordinate space |
dicom_element | study, series, instance UIDs and canonical tag |
table_cell | one-based row and column with an optional sheet |
Invalid spans, malformed pointer escapes, inverted or out-of-bounds boxes, malformed DICOM identifiers, and zero-based table cells fail before a record is created.
Forward-compatible reads¶
The current contract version is 1.0.0. Readers accept newer minor and patch versions with the same major version. Unknown additive top-level fields are stored immutably and written back unchanged:
payload = artifact.to_dict()
payload["schema_version"] = "1.2.0"
payload["future_review_state"] = {"code": "synthetic-state"}
restored = ClinicalArtifact.from_dict(payload)
assert restored.to_dict()["future_review_state"] == {"code": "synthetic-state"}
An unsupported major version raises JourneySchemaVersionError. This is a fail-closed boundary: OpenMed does not guess how to reinterpret a breaking schema.
Derivation graph validation¶
Use validate_contract_graph() before committing a group of records. It checks:
- globally unique opaque identifiers;
- artifact, evidence, fact, conflict, resolution, and snapshot references;
- conflict membership for selected or rejected facts;
- same-conflict resolution supersession; and
- cycles in artifact, fact, resolution, and snapshot derivations.
The returned diagnostics contain counts only. Validation errors identify the record category but never echo identifiers or clinical values.
JSON Schemas¶
load_journey_schema(name) and load_all_journey_schemas() expose bundled Draft 2020-12 schemas for all six records. Schemas allow additive top-level fields within major version 1 but keep every currently defined field and nested evidence shape strict.
The schemas ship in openmed/core/schemas/json/ and require no network access.