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: no placeholder IDs, and missing data is 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. No
id="placeholder"orSpecimen/placeholderis ever 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 to ensure codes conform to a specific terminology; it is not part of 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 do not carry numeric lab values. The exporter omits value[x] entirely and sets Observation.dataAbsentReason:
{
"dataAbsentReason": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/data-absent-reason",
"code": "unknown"
}
]
}
}No placeholder text or placeholder IDs are used. Optional context may be added via Observation.note with explicit, non-placeholder wording if needed.
- No numeric result value: Receipts do not carry lab values; Observation uses dataAbsentReason as above. Real value/unit mapping would require 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.
- No Device resource: Device is represented as an Observation extension (identifier); no Device resource is emitted.
- No full FHIR validation: Only the minimal export contract and reference resolution are checked; no FHIR profile or terminology validation.
- 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.