Provenance and Data Quality
Provenance
Track data origin, authorship, and transformation for audit and compliance.
Activity Codes
Use http://terminology.hl7.org/CodeSystem/v3-DataOperation:
- CREATE — resource first created (new patient registration, new order).
- UPDATE — resource modified (lab result amended, diagnosis updated).
- DELETE — resource removed (entered-in-error correction).
- APPEND — addendum to existing resource (addendum to clinical note).
Additional activity codes from http://terminology.hl7.org/CodeSystem/provenance-participant-type:
- originator — the entity that first created the content.
- reviewer — the entity that verified the content.
- legal — the entity that attested to the legal authenticity.
Agent Types
Provenance.agent[].type from http://terminology.hl7.org/CodeSystem/provenance-participant-type:
- author — who wrote/created the resource.
→ Reference(Practitioner/{id}) or Reference(Patient/{id}).
- performer — who performed the activity described.
→ Reference(Practitioner/{id}).
- verifier — who verified/approved the resource.
→ Reference(Practitioner/{id}).
- attester — who attested to the accuracy.
→ Reference(Practitioner/{id}) or Reference(Organization/{id}).
- informant — who provided the information.
→ Reference(Patient/{id}) or Reference(RelatedPerson/{id}).
- custodian — who is responsible for ongoing maintenance.
→ Reference(Organization/{id}).
- assembler — device/system that assembled the resource.
→ Reference(Device/{id}).
Agent.onBehalfOf
When a practitioner acts on behalf of an organization:
- Provenance.agent[].onBehalfOf → Reference(Organization/{id}).
- Common: resident documenting on behalf of attending, nurse on behalf of hospital.
Entity (Source Tracking)
Provenance.entity[] tracks what the resource was derived from:
- role: derivation, revision, quotation, source, removal.
- what: Reference to the source resource or document.
- Use for:
- Lab result derived from a Specimen.
- Amended DiagnosticReport revision of a prior report.
- Clinical note quoting a prior note.
- Data migrated from a legacy system (source = Device or DocumentReference).
Recorded Timestamp
- Provenance.recorded — MUST be an Instant (full datetime with timezone).
- Use
datetime.now(timezone.utc).isoformat().
- Should be at or after the resource's own lastUpdated timestamp.
Common Provenance Patterns
EHR data entry:
target: Patient/{id}
activity: CREATE
agent: [{ type: author, who: Practitioner/{id} }]
recorded: 2025-03-15T10:30:00Z
Lab result from analyzer:
target: Observation/{id}
activity: CREATE
agent: [
{ type: author, who: Device/{id} }, ← lab instrument
{ type: verifier, who: Practitioner/{id} } ← pathologist review
]
entity: [{ role: source, what: Specimen/{id} }]
Amended report:
target: DiagnosticReport/{id} (status: amended)
activity: UPDATE
agent: [{ type: author, who: Practitioner/{id} }]
entity: [{ role: revision, what: DiagnosticReport/{original_id} }]
Data received via HL7v2 interface:
target: Observation/{id}
activity: CREATE
agent: [
{ type: assembler, who: Device/{id} }, ← integration engine
{ type: custodian, who: Organization/{id} } ← receiving hospital
]
policy: ["http://hospital.example.org/policy/data-import"]
Security and Policy
- Provenance.policy — list of policy URIs that apply (consent, data use agreements).
- Use for HIPAA, research consent, data sharing agreements.
- Example:
["http://hospital.example.org/policy/hipaa-minimum-necessary"].
Data Quality Variation
Reflect real-world EHR messiness — not all data is clean, complete, or correct.
Missing and Sparse Data
- Vary completeness: ~60% of patients fully documented, ~30% partially, ~10% sparse.
- Sparse patients: Only demographics + 1–2 encounters (newly registered, transferred in).
- Missing optional fields: Omit phone, email, address.line[1], maritalStatus randomly.
- Missing clinical detail: Some Conditions without onset, some Observations without interpretation.
Data Absent Reasons
Use Observation.dataAbsentReason with http://terminology.hl7.org/CodeSystem/data-absent-reason:
- unknown — value is unknown (most common, ~40% of absent values).
- asked-unknown — patient was asked but doesn't know.
- temp-unknown — temporarily unavailable (pending lab result).
- not-asked — question was not asked (screening not performed).
- asked-declined — patient declined to answer.
- masked — value hidden due to security/privacy (restricted access).
- not-applicable — observation not applicable to this patient.
- unsupported — system doesn't support this measurement.
- as-text — value provided only as narrative text.
- error — measurement error occurred.
- not-a-number — result is NaN (e.g., division by zero in calculated field).
- not-performed — test/procedure was not performed.
- negative-results-not-stored — only positive results are stored (common for cultures).
Status Variation
Reflect that not everything succeeds:
- Conditions: ~5% verificationStatus = "unconfirmed" or "provisional".
~2% = "refuted" (ruled out). ~1% = "entered-in-error".
- Encounters: ~3% status = "cancelled" (patient no-show). ~1% = "entered-in-error".
- Observations: ~2% status = "cancelled" (specimen lost). ~1% = "entered-in-error".
- MedicationRequests: ~5% status = "cancelled" or "stopped" (adverse reaction, patient refusal).
- Procedures: ~3% status = "not-done" with statusReason (contraindication, refusal).
- Immunizations: ~5% status = "not-done" with statusReason (patient objection, medical precaution).
Corrections and Amendments
- Amended observations: Original status = "amended", new Observation with corrected value.
Link via Provenance.entity[].role = "revision".
- Corrected reports: DiagnosticReport status = "corrected" with updated conclusion.
- Entered-in-error: Resources with status "entered-in-error" should have Provenance
with activity = DELETE documenting who marked it and when.
Duplicate and Conflicting Data
- Duplicate patients: ~2% of patients have slight name/DOB variations (data entry errors).
Different MRNs but same person — realistic for EMPI scenarios.
- Conflicting vitals: Occasionally two BP readings for same encounter with different values
(nurse station vs. physician re-check).
- Stale data: Some conditions remain "active" despite being clinically resolved
(problem list not updated — very common in real EHRs).
Temporal Data Quality
- Backdated entries: Some records entered days after the encounter (late documentation).
Provenance.recorded > resource.period.end.
- Future dates: Occasionally a typo creates a future date (entered-in-error scenario).
- Out-of-order events: Discharge summary recorded before some inpatient notes.
- Timezone inconsistencies: Mix of UTC and local timezone offsets in DateTime fields
(reflects real multi-site EHR data).
1---2name: provenance-data-quality3description: Generate Provenance resources for audit trails with agents, activities, entities, and security labels. Model realistic EHR data quality variation with missing fields, sparse records, data-absent reasons, duplicate records, and correction workflows. Use when user mentions provenance, audit, data quality, data origin, missing data, sparse records, data completeness, data lineage, correction, amendment, or data governance.4---5
6# Provenance and Data Quality
7
8## Provenance
9
10Track data origin, authorship, and transformation for audit and compliance.
11
12### Activity Codes
13
14Use http://terminology.hl7.org/CodeSystem/v3-DataOperation:
15- **CREATE** — resource first created (new patient registration, new order).
16- **UPDATE** — resource modified (lab result amended, diagnosis updated).
17- **DELETE** — resource removed (entered-in-error correction).
18- **APPEND** — addendum to existing resource (addendum to clinical note).
19
20Additional activity codes from http://terminology.hl7.org/CodeSystem/provenance-participant-type:
21- **originator** — the entity that first created the content.
22- **reviewer** — the entity that verified the content.
23- **legal** — the entity that attested to the legal authenticity.
24
25### Agent Types
26
27Provenance.agent[].type from http://terminology.hl7.org/CodeSystem/provenance-participant-type:
28- **author** — who wrote/created the resource.
29 → Reference(Practitioner/{id}) or Reference(Patient/{id}).
30- **performer** — who performed the activity described.
31 → Reference(Practitioner/{id}).
32- **verifier** — who verified/approved the resource.
33 → Reference(Practitioner/{id}).
34- **attester** — who attested to the accuracy.
35 → Reference(Practitioner/{id}) or Reference(Organization/{id}).
36- **informant** — who provided the information.
37 → Reference(Patient/{id}) or Reference(RelatedPerson/{id}).
38- **custodian** — who is responsible for ongoing maintenance.
39 → Reference(Organization/{id}).
40- **assembler** — device/system that assembled the resource.
41 → Reference(Device/{id}).
42
43### Agent.onBehalfOf
44
45When a practitioner acts on behalf of an organization:
46- Provenance.agent[].onBehalfOf → Reference(Organization/{id}).
47- Common: resident documenting on behalf of attending, nurse on behalf of hospital.
48
49### Entity (Source Tracking)
50
51Provenance.entity[] tracks what the resource was derived from:
52- **role**: derivation, revision, quotation, source, removal.
53- **what**: Reference to the source resource or document.
54- Use for:
55 * Lab result derived from a Specimen.
56 * Amended DiagnosticReport revision of a prior report.
57 * Clinical note quoting a prior note.
58 * Data migrated from a legacy system (source = Device or DocumentReference).
59
60### Recorded Timestamp
61
62- Provenance.recorded — MUST be an Instant (full datetime with timezone).
63- Use `datetime.now(timezone.utc).isoformat()`.
64- Should be at or after the resource's own lastUpdated timestamp.
65
66### Common Provenance Patterns
67
68**EHR data entry:**
69```
70target: Patient/{id}
71activity: CREATE
72agent: [{ type: author, who: Practitioner/{id} }]
73recorded: 2025-03-15T10:30:00Z
74```
75
76**Lab result from analyzer:**
77```
78target: Observation/{id}
79activity: CREATE
80agent: [
81 { type: author, who: Device/{id} }, ← lab instrument
82 { type: verifier, who: Practitioner/{id} } ← pathologist review
83]
84entity: [{ role: source, what: Specimen/{id} }]
85```
86
87**Amended report:**
88```
89target: DiagnosticReport/{id} (status: amended)
90activity: UPDATE
91agent: [{ type: author, who: Practitioner/{id} }]
92entity: [{ role: revision, what: DiagnosticReport/{original_id} }]
93```
94
95**Data received via HL7v2 interface:**
96```
97target: Observation/{id}
98activity: CREATE
99agent: [
100 { type: assembler, who: Device/{id} }, ← integration engine
101 { type: custodian, who: Organization/{id} } ← receiving hospital
102]
103policy: ["http://hospital.example.org/policy/data-import"]
104```
105
106### Security and Policy
107
108- Provenance.policy — list of policy URIs that apply (consent, data use agreements).
109- Use for HIPAA, research consent, data sharing agreements.
110- Example: `["http://hospital.example.org/policy/hipaa-minimum-necessary"]`.
111
112## Data Quality Variation
113
114Reflect real-world EHR messiness — not all data is clean, complete, or correct.
115
116### Missing and Sparse Data
117
118- **Vary completeness**: ~60% of patients fully documented, ~30% partially, ~10% sparse.
119- **Sparse patients**: Only demographics + 1–2 encounters (newly registered, transferred in).
120- **Missing optional fields**: Omit phone, email, address.line[1], maritalStatus randomly.
121- **Missing clinical detail**: Some Conditions without onset, some Observations without interpretation.
122
123### Data Absent Reasons
124
125Use Observation.dataAbsentReason with http://terminology.hl7.org/CodeSystem/data-absent-reason:
126- **unknown** — value is unknown (most common, ~40% of absent values).
127- **asked-unknown** — patient was asked but doesn't know.
128- **temp-unknown** — temporarily unavailable (pending lab result).
129- **not-asked** — question was not asked (screening not performed).
130- **asked-declined** — patient declined to answer.
131- **masked** — value hidden due to security/privacy (restricted access).
132- **not-applicable** — observation not applicable to this patient.
133- **unsupported** — system doesn't support this measurement.
134- **as-text** — value provided only as narrative text.
135- **error** — measurement error occurred.
136- **not-a-number** — result is NaN (e.g., division by zero in calculated field).
137- **not-performed** — test/procedure was not performed.
138- **negative-results-not-stored** — only positive results are stored (common for cultures).
139
140### Status Variation
141
142Reflect that not everything succeeds:
143- **Conditions**: ~5% verificationStatus = "unconfirmed" or "provisional".
144 ~2% = "refuted" (ruled out). ~1% = "entered-in-error".
145- **Encounters**: ~3% status = "cancelled" (patient no-show). ~1% = "entered-in-error".
146- **Observations**: ~2% status = "cancelled" (specimen lost). ~1% = "entered-in-error".
147- **MedicationRequests**: ~5% status = "cancelled" or "stopped" (adverse reaction, patient refusal).
148- **Procedures**: ~3% status = "not-done" with statusReason (contraindication, refusal).
149- **Immunizations**: ~5% status = "not-done" with statusReason (patient objection, medical precaution).
150
151### Corrections and Amendments
152
153- **Amended observations**: Original status = "amended", new Observation with corrected value.
154 Link via Provenance.entity[].role = "revision".
155- **Corrected reports**: DiagnosticReport status = "corrected" with updated conclusion.
156- **Entered-in-error**: Resources with status "entered-in-error" should have Provenance
157 with activity = DELETE documenting who marked it and when.
158
159### Duplicate and Conflicting Data
160
161- **Duplicate patients**: ~2% of patients have slight name/DOB variations (data entry errors).
162 Different MRNs but same person — realistic for EMPI scenarios.
163- **Conflicting vitals**: Occasionally two BP readings for same encounter with different values
164 (nurse station vs. physician re-check).
165- **Stale data**: Some conditions remain "active" despite being clinically resolved
166 (problem list not updated — very common in real EHRs).
167
168### Temporal Data Quality
169
170- **Backdated entries**: Some records entered days after the encounter (late documentation).
171 Provenance.recorded > resource.period.end.
172- **Future dates**: Occasionally a typo creates a future date (entered-in-error scenario).
173- **Out-of-order events**: Discharge summary recorded before some inpatient notes.
174- **Timezone inconsistencies**: Mix of UTC and local timezone offsets in DateTime fields
175 (reflects real multi-site EHR data).
176