See Frozen contracts and Trust verification for verifying an EvidenceBundle.v0.1 (including integrity, schema, hashchain, and invariant trace) with labtrust verify-bundle --bundle <dir>.
The FHIR R4 exporter converts Receipt.v0.1 (from an evidence bundle or receipts directory) into valid HL7 FHIR R4 JSON. The repo targets valid FHIR R4 with deterministic resource IDs and missing data represented using the standard data-absent-reason extension and Observation.dataAbsentReason where appropriate.
- Bundle: type =
collection; contains Specimen (when present), Observation(s), and DiagnosticReport. - When specimen is missing: No Specimen resource is emitted;
Observation.specimenandDiagnosticReport.specimenare set to a Reference object containing only the data-absent-reason extension (valueCode:unknown). - When Observation has no numeric value:
value[x]is omitted;Observation.dataAbsentReasonis populated with the HL7 data-absent-reason code system and codeunknown. - IDs: All resource ids are deterministic (specimen_id or content-addressed hash; result_id or index-based); in-bundle references resolve. Placeholder ids such as
id="placeholder"orSpecimen/placeholderare never emitted.
No external FHIR libraries are required; output is pure JSON with lightweight structural validation.
labtrust export-fhir --receipts <dir> --out <dir> [--filename fhir_bundle.json]- --receipts: Directory containing receipt files (e.g.
EvidenceBundle.v0.1/withreceipt_*.v0.1.json) or any folder withreceipt_*.v0.1.json. Ifmanifest.jsonis present,partner_idandpolicy_fingerprintare read and added to the bundle meta. - --out: Output directory; the FHIR bundle JSON is written here.
- --filename: Output filename (default:
fhir_bundle.json).
| Receipt field | FHIR resource / path | Notes |
|---|---|---|
| specimen_id | Specimen.id, Specimen.identifier | id = specimen_id when present; otherwise deterministic content-addressed hash of receipt. identifier system urn:labtrust:specimen, value = same id |
| accession_ids[0] | Specimen.accessionIdentifier.value | First accession only |
| timestamps.received / timestamps.accepted | Specimen.receivedTime | Converted to FHIR dateTime (UTC ISO 8601); if only integer timestamp, also in extension received-timestamp |
| result_id | Observation.id, DiagnosticReport.id | One Observation and one DiagnosticReport per result receipt |
| panel_id | Observation.code.coding | system urn:labtrust:test, code and display = panel_id (or result_id if no panel_id) |
| device_ids[0] | Observation.extension | url http://labtrust.org/fhir/StructureDefinition/device-identifier, valueIdentifier (no Device resource in bundle) |
| reason_codes (CRIT / HIGH / LOW) | Observation.interpretation | CRIT → v3-ObservationInterpretation code CR (Critical); HIGH → H; LOW → L |
| timestamps.result_generated / released | Observation.issued, DiagnosticReport.effectiveDateTime | FHIR dateTime |
| decision | DiagnosticReport.status | RELEASED → final; HELD → partial; REJECTED → entered-in-error; BLOCKED → registered |
| (specimen link) | DiagnosticReport.specimen | When specimen receipts exist: reference to first Specimen in bundle (#Specimen/<id>). When none exist: Reference with only data-absent-reason extension (no Specimen resource). |
- final: Receipt decision = RELEASED (result released to care).
- partial: Receipt decision = HELD (result held, not yet final).
- entered-in-error: Receipt decision = REJECTED (specimen/result rejected).
- registered: Receipt decision = BLOCKED or other (registered, not yet final).
- partner_id: Included in
Bundle.meta.tagwith systemhttp://labtrust.org/fhir/partnerand code = partner_id (from manifest or optional override). - policy_fingerprint: Included in
Bundle.meta.extensionwith urlhttp://labtrust.org/fhir/StructureDefinition/policy-fingerprintand valueString = policy_fingerprint (from manifest).
- Structural checks: Bundle has
resourceType"Bundle",type"collection", andentry[]withfullUrlandresource. Each resource hasresourceTypeandid. References that usereferenceresolve within the bundle (same-bundle references use#ResourceType/id). Specimen may be represented by a Reference with only the data-absent-reason extension (noreference), in which case no resolution is required. No resourceidor fullUrl may contain "placeholder". - Determinism: Same receipts directory (same file order and content) produces identical bundle JSON (canonical key ordering).
- Export contract schema:
policy/schemas/fhir_bundle_export.v0.1.schema.jsondescribes the minimal export contract (required keys, entry structure). This is not full FHIR profile validation.
Terminology validation (optional): The CLI command labtrust validate-fhir --bundle <path> --terminology <value_set_json> [--strict] checks coded elements (e.g. Observation.code, Observation.interpretation) in a FHIR bundle against a value set. Use this when you need codes to conform to a specific terminology beyond the minimal export contract. See Coordination benchmark card – Full FHIR or terminology validation.
When the receipts contain only result receipts (no specimen receipts), no Specimen resource is emitted. Observation.specimen and DiagnosticReport.specimen are set to a Reference object that contains only the HL7 data-absent-reason extension (no reference field, no Specimen in bundle):
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
"valueCode": "unknown"
}
]
}Receipts omit numeric lab values. The exporter leaves out value[x] and sets Observation.dataAbsentReason:
{
"dataAbsentReason": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/data-absent-reason",
"code": "unknown"
}
]
}
}Placeholder text and placeholder IDs are omitted. Optional context may be added via Observation.note with explicit wording when needed.
- Numeric result value: Receipts omit lab values; Observation uses dataAbsentReason as above. Real value/unit mapping requires extending the receipt or a separate value feed.
- One Observation per result: Each result receipt becomes one Observation and one DiagnosticReport. Multiple observations per report (e.g. panel with many analytes) would require multiple result receipts or a different mapping.
- Specimen–result linking: When specimen receipts exist, the first Specimen in the bundle is referenced by all Observations and DiagnosticReports. When none exist, specimen is represented via data-absent-reason extension only. Explicit specimen–result linking requires specimen_id on the result receipt.
- Device resource: Device is represented as an Observation extension (identifier); a standalone Device resource is omitted from the bundle.
- FHIR validation scope: The minimal export contract and reference resolution are checked; full FHIR profile or terminology validation is a separate optional step (
validate-fhir).
- Run:
labtrust reproduce --profile minimal --out runs/my_repro - Export receipts:
labtrust export-receipts --run runs/my_repro/taska/logs/cond_0/episodes.jsonl --out runs/my_repro/taska/cond_0_export - Export FHIR:
labtrust export-fhir --receipts runs/my_repro/taska/cond_0_export/EvidenceBundle.v0.1 --out runs/my_repro/taska/cond_0_fhir
All tests (including export-receipts and export-fhir) should be green.