EHR quality-governance packets & audits
A family of tasks: you are given a short prompt, an answer_template.json, and read-only access
to an EHR/referral environment (patients, conditions, medications, allergies, encounters,
immunizations, disclosures, documents, service-requests, duplicate candidates, referrals,
audit-logs, providers, an ICD-10 reference, and a service-code reference). You return one JSON
object that matches the template exactly. The template — not your prose — defines the keys,
types, enums, required_value fields, and ordering. There is no scoring tool available while
solving a real task; rely on the rules below and on faithful reading of the environment.
Universal workflow
- Read
answer_template.jsonfirst, then the prompt. The template is authoritative. Note every key, enum set,required_value(e.g. a fixedtask_id), type (incl. nullable), arrayset_semantics, and each array's ordering rule. Your output must have exactly those keys/types. - List the case objects named in the prompt: patient id(s), duplicate-candidate id, referral id(s), ServiceRequest id, batch id, recipient/receiving provider id, etc.
- Gather evidence from the read endpoints described in the task's own environment-access file (use whatever base URL and routes that file provides). Pull the relevant record families: patient demographics, active clinical lists, encounters, documents, immunizations, disclosures, the duplicate candidate / referral / ServiceRequest object, the provider directory, and the ICD-10 / service-code references for any codes involved. A record family that isn't relevant to the template can be skipped.
- Apply the domain rules (below) that correspond to the packet type.
- Normalize and sort every array per the template, set
required_valuefields, and emit JSON only — no commentary, no trailing prose.
Reading records: status, normalized_key, faithfulness
- Condition/medication/allergy records carry
statusand anormalized_key. "Active" meansstatus == "active". Entries withinactive/entered-in-errorare not active. - Active-key unions/lists = the set of
normalized_keyvalues over every active record, deduped across sources and (for merges) across both patients, then sorted per the template (usually ascending/alphabetical).- Include all active keys — even generic-looking placeholder keys (e.g.
baseline_*) and duplicated allergens. A comprehensive active list keeps everything active. (Dropping such placeholders from a comprehensive list is wrong.)
- Include all active keys — even generic-looking placeholder keys (e.g.
- "excluded"/"distractor" key buckets = records whose
status != active, bynormalized_key. - Copy verbatim: ids, dates, phones, faxes, codes, descriptions, and
sourcestrings come straight from the environment; don't paraphrase them.
Selection depends on the field's PURPOSE (the central judgment)
The same raw data feeds different fields with opposite selection criteria — always read what the field is for:
- Comprehensive / preservation lists (merge unions, care-transition active lists): keep all active records, placeholders included.
- Relevance-filtered lists (referral-letter diagnosis/medication highlights): keep only records tied to the stated reason and drop placeholders/distractors.
Distractor patterns to actively reject
See references/field-conventions.md for the full list. In brief: name collisions (same display
name but different DOB/insurance = different people), placeholder normalized_keys, routine
chart_summary/ehr_export documents, inactive records, audit-logs for other patients or
unrelated/test events, and soft coordination notes ("confirm …") that don't actually change a
complete record. Include or exclude each strictly by the field's purpose.
Packet-type rules
A. Duplicate merge-readiness / duplicate review
- Read the candidate for
status,match_signals,conflict_signals, andmerge_preview(preferred_target_patient_id/source_patient_id). Validate each signal against the two patients' actual demographics/conditions before emitting it. - Target/canonical = the patient with
canonical_status == "active"(the other hasduplicate/possible_duplicate, or the candidate names the preferred target); source = the other. When no merge is decided, target/source arenull. - Disposition ladder (map to whatever enums the template gives):
- Strong matches + only trivial/normalizable conflicts (address abbreviation like "St" vs
"Street", a name nickname/variant) → clean ready / merge-ready. Still record the difference
as a
conflict_signal/demographic_conflict, but do not downgrade the disposition, setmanual_review_required, or add a review note. - Strong matches and substantive conflicts (opposite-laterality problems, genuinely different given names, different phone) on an unresolved candidate → needs_review / review_hold with null merge target/source. (This is the correct outcome for a "twins-like" pair: same DOB/insurance/address but different name/phone and opposite-side problems — hold, do not jump to do_not_merge.)
- Reserve do_not_merge for clear different-people-with-no-merge-value; reserve merge/confirmed for clean confirmed duplicates.
- Strong matches + only trivial/normalizable conflicts (address abbreviation like "St" vs
"Street", a name nickname/variant) → clean ready / merge-ready. Still record the difference
as a
- Reconciliation (
patient_active_list_endpoints_over_duplicate_preview): keys added = active keys from the patient endpoints minus the keys already inmerge_preview, per category. - demographic_matches = demographic fields whose values are equal across the two patients (dob, family_name, insurance_id, phone, sex). demographic_conflicts = fields that differ (given_name, address), including variant/abbreviation differences.
- Evidence documents follow the template's
document_selection_policy(e.g. identity-verification + external clinical-continuity docs, statusfinal). Routinechart_summary/ehr_exportdocs are excluded → put them in the distractor bucket and name the excluded type. Evidence audit_ids = audit entries for these patients that reference this case; other-patient / unrelated / test-tagged entries are distractors. - packet_contact:
specialist_provider= the provider matching the external continuity document's facility/service line;primary_care_provider= the patient's PCP block. Copy provider fields from the directory.
B. Referral coordination packet (specialty letter)
patient_referralfrom the referral object (batch_id, service_line, requested_date) + patient.- referral-relevant conditions and supporting codes = only codes clinically tied to the
referral reason: the referral's own
diagnosis_codeand the presenting symptom in the narrative. Comorbidities arereferral_relevant=falseeven when one of their medications is still highlighted. - ICD validation via the ICD reference:
valid_matches_narrativewhen the code's chapter fits the service line and the narrative matches the code'sexpected_terms; also emit the chapter andnarrative_match. - allergy_readiness: a single fully-populated active allergy (allergen+reaction+severity+status)
=
complete_documented, ready, no follow-up — even if a coordination note says "confirm …" (that note is a soft distractor; the record is complete). Letter enums offering a fully-specified allergy statement confirm completeness. - recent_encounter_evidence = the encounter whose notes/diagnoses match the referral reason (not necessarily the most recent visit).
- medication_highlights = only referral-relevant meds; exclude placeholder/distractor meds
(e.g. a
baseline_*med with an implausible route). - Letter-field enums: choose each value to match the derived facts; the option names themselves are strong hints about the intended answers.
C. Care-transition handoff packet
- Recipient = the named provider (from the directory). disclosure = the disclosure whose
recipient_provider_id == recipientandstatus == "permitted". - Handoff encounters = the N (template length) most relevant recent encounters for this transition — those tied to the transition's condition/service (e.g. the numbered handoff/ care-transition series about the surgical joint), newest→oldest. Not merely the most-recent encounters: exclude unrelated specialties, other body regions, and stale overflow beyond N. Record selected ids (newest→oldest) and all reviewed-but-excluded ids (ascending).
- latest_immunization = the immunization with the max date.
- risk_flags (from the template's
allowed_values) derived from evidence: a condition (memory-loss → cognitive flag; hypertension → hypertension flag), a condition+medication combo (insulin + diabetes → insulin-dependent flag), an allergy (latex → latex-allergy flag), and encounter notes that explicitly require a plan/note (→ fall-risk-note-required, perioperative-glucose-plan-needed).risk_flag_evidencelinks each flag to its supporting active condition/medication keys (and the encounter carrying a note-based requirement). - Readiness:
ready_with_risk_flagswhen flags exist but nothing blocks;blocking_issue_codescome only from the template'smissing_*/not-permittedenum — clinical requirements (glucose plan, fall-risk note) are risk flags, not blockers.readyonly if no flags;not_readyonly if a real blocker exists.
D. ServiceRequest quality check
- Copy scalar fields verbatim.
service_code_validvia the service-code reference (code active and its service_line consistent with the performer).performer_service_linefrom the performer provider.reason_code_validationper code (ordered by code):valid= code exists in the ICD reference;chapter= its ICD chapter;matches_patient_evidence= the patient has an active condition carrying that code (any source counts, including imaging/MRI reports). - SBAR coverage:
complete=truewhen all four sections (situation, background, assessment, recommendation) are present and non-empty; emitsections_present+missing_sections.
E. Referral batch audit
- Scope the batch by filtering all referrals to the batch id yourself (a
batch_idquery param may be ignored by the server).record_count= row count (duplicate resubmissions count);unique_patient_count= distinct patient ids. - invalid / out-of-range codes = codes whose ICD chapter is clinically wrong for the batch's
service line (e.g. a Respiratory code in an orthopedic batch). Injury-chapter S-codes (meniscus
tears, etc.) are acceptable for orthopedics — do not flag them as out-of-range. Use
unknown_codeonly for codes absent from the ICD reference. - laterality/narrative mismatch: compare the code (its
expected_terms+ laterality) to the narrative —laterality_mismatch(opposite side, same region),missing_laterality(same condition, no side stated),narrative_mismatch(different region/condition). Do not over-flag: a generic-symptom narrative that matches the code's region+side is not a mismatch, and prefer a single dominantmismatch_typeper row rather than stacking types. - duplicate_groups = same-patient resubmissions (a "-DUP" id / a "duplicate resubmission" coordination note); disposition = consolidate-under-original; tier all group rows as tier-1 duplicate blockers.
- insurance/patient anomalies = shared insurance id across different patients (e.g. twins) → verify-membership, do-not-merge. Same display name but different insurance/DOB is a distractor, not an anomaly.
- follow-up queues from referral fields: authorization missing/pending from
authorization_status; records-request = referrals lacking an office-note document; imaging follow-up = missing/pending imaging. - action_plan tiers: Tier 1 = urgent-urgency coding errors + invalid/out-of-range codes +
duplicate blockers; Tier 2 = routine coding/auth/document blockers; Tier 3 = administrative
document completion only.
owner_provider_id= the referral's receiving provider. Every referral needing action is tiered once; count fully-clean rows as validated-ready. - summary_counts are derived from the sections above — compute them from your own arrays, never independently.
Output discipline
- JSON only; exact template keys, types, enums, and
required_values. - Sort each array as the template says (default alphabetical/ascending; honor "newest→oldest",
"by code", or "by id" when stated). Treat
set_semanticsarrays as unordered sets of the correct members. - When a value is genuinely absent, use the template's nullable/
missingoption rather than inventing one. - Prefer the smallest correct set: false-positive members (extra ids, over-flagged rows, guessed controlled-vocabulary codes) cost as much as misses.