EHR Packet / Audit Generation
These tasks ask for a single normalized JSON object built from records spread across many read-only EHR API
resources (patients, conditions, medications, allergies, encounters, documents, immunizations, disclosures,
referrals, duplicate candidates, service-requests, providers, ICD-10, service-codes, audit-logs). The answer
must conform exactly to a per-task answer_template.json. The environment provides an environment_access.md
listing the allowed endpoints and a base URL — use those endpoints at run time; do not assume paths from memory.
Universal workflow
- Read the prompt and the answer template end-to-end first. The template is the contract: it names every required key, gives enums, and states ordering / set-semantics rules (e.g. "treated as a set by code", "sorted alphabetically", "newest to oldest"). Build the object key-for-key against the template.
- Fetch every referenced record. Identify the IDs named in the prompt (patient_ids, candidate_id, referral_id, batch_id, provider_id, service_request_id) and pull each relevant sub-resource. Fetch the shared directories (providers, icd10, service-codes, audit-logs, duplicate candidates, referrals) once.
- Reconcile against raw records, not summaries. Previews/intake claims inside referral or duplicate records are hints, not truth — verify against the patient's actual clinical lists and document store.
- Emit only the JSON object. No prose, no explanations, no leftover template placeholder strings. Use
nullwhere the schema allowsstring | null. Dates areYYYY-MM-DD. - Sort every array as the template dictates, even when it says evaluation treats it as a set — sorting is cheap insurance and some fields are order-sensitive.
Cross-cutting patterns (verified across task families)
Active clinical lists
- The "active condition/medication/allergy keys" are the
normalized_keyvalues of records whosestatusis"active", taken from the patient list endpoints (not from any duplicatemerge_preview). - Union across all patients in scope; de-duplicate by
normalized_key; sort ascending. - Inactive /
entered-in-errorrecords and unrelated distractors go inexcluded_distractors, not the union. - When a template has both a
clinical_unionsblock and anactive_key_unionsblock, both hold the same endpoint-derived active union. The separateactive_list_reconciliationblock isendpoint_union − merge_preview_union(the keys the live lists add that the preview missed). Do not put the preview union in the union blocks — both must hold the endpoint union.
Duplicate candidates
match_signalsandconflict_signals: copy them verbatim from the candidate record.merge_previewgives a tentative target/source and a preview of clinical keys; treat it as a claim to reconcile, not as the authoritative list.- Disposition mapping (key insight — the candidate's own
statusfield is the guide, not a re-derivation):- Strong identity match + only a benign conflict (e.g.
address_abbreviation, name variant) and the source chart already markedcanonical_status: duplicate→ready_to_merge/merge_ready_with_conflict_review, target = the active/preferred patient, source = the duplicate. - Serious / contradictory conflicts (e.g.
opposite_laterality_problem,different_given_name,different_phone) combined with strong match signals (same DOB/insurance/address) → genuinely ambiguous →candidate_status: needs_review,decision: review_hold, andmerge_target_patient_id/merge_source_patient_id=null(no target/source is designated while on hold). Concludingnot_duplicate/do_not_mergeis the wrong call for these ambiguous cases; useneeds_review/review_hold. - Confirmed duplicate with target designated →
merge, target/source populated.
- Strong identity match + only a benign conflict (e.g.
Evidence and document selection
- For merge packets, evidence documents are identity or external-continuity documents only (e.g.
identity_verification,external_cardiology_note).chart_summaryand other internal summaries are distractors — list their IDs inexcluded_distractorsand their type inexcluded_document_types. - Audit logs: include only logs whose
patient_idmatches the case; unrelated patients' logs go inexcluded_distractors. - The specialist contact for a merge packet is the provider behind any "shared external … document" match signal (e.g. an external cardiology note → the cardiologist at the originating facility).
Allergy readiness
- If the allergy record is fully populated (allergen, reaction, severity, status), prefer
readiness_status: complete_documentedand the enum that literally matches the documented allergen/reaction/severity — do not downgrade toallergy_details_incompletejust because a coordination note says "confirm before letter". - A coordination note that says "confirm … before letter" still makes the overall packet
hold_for_clinical_clarification/hold_for_allergy_clarificationwithallergy_incompleteinblocking_issues— the data is complete, but send-readiness is held.
ICD-10 / code validation
- For each code, look it up to get
chapter,expected_terms,requires_laterality. narrative_match: does the narrative contain (case-insensitive) anyexpected_term? If yes, it matches. Keep this check simple — requiring the full laterality-bearing term or a body-part+pathology conjunction over-segments and flags clean referrals as mismatches.- Mismatch types:
laterality_mismatch(narrative's left/right conflicts with the code),narrative_mismatch(no expected term present),missing_laterality(code requires laterality, narrative names the right body part but no side). Don't stackmissing_lateralityonto a wrong-body-part narrative. - Out-of-range is chapter-strict. For a batch whose
expected_chapterisMusculoskeletal, any code in another chapter isout_of_range_chapter— includingInjury-chapter S-codes (e.g. meniscus-tear codes) even though they are clinically orthopedic. Onlyunknown_code(not in the ICD-10 directory at all) uses theunknown_codeissue type.
Referral code set
primary_code= the referral'sdiagnosis_code.supporting_codes= only the codes that directly support the primary narrative (e.g. the symptom code behind "… with exertional dyspnea"), matching the chosendiagnosis_summary_choice. Do not dump every referral-relevant code in —supporting_codesholds only the directly supporting ones.
Risk flags (care-transition packets)
- Derive each allowed risk flag from concrete evidence: a condition
normalized_key, a medicationnormalized_key, and/or an encounter whosecare_plan_notesmention the requirement. A note like "packet requires glucose plan and fall-risk note" yieldsperioperative_glucose_plan_neededandfall_risk_note_required. packet_readiness.status=ready_with_risk_flags(notnot_ready) when the packet is structurally complete — risk flags travel with the packet for the recipient to act on;blocking_issue_codesare only for missing structural components (missing patient/recipient/lists/encounters/immunization/disclosure, ordisclosure_not_permitted).- A risk flag with no condition/med/encounter evidence (e.g. an allergy-driven flag, when the evidence schema has no allergy_keys field) is still emitted with empty evidence arrays.
Handoff encounters
- "Four most relevant recent handoff encounters": take the most recent encounters that are relevant to the
transition, excluding unrelated-laterality visits (e.g. a knee encounter when the surgery is for the hip)
and stale/out-of-window ones. Note the chosen IDs in
source_selection.selected_encounter_ids(newest to oldest) and the rest inexcluded_encounter_ids(sorted ascending).
Batch audit structure
duplicate_groups: same patient resubmitted →same_patient_resubmission,recommended_disposition: consolidate_under_original;referral_idssorted, original first by id.insurance_patient_anomalies: two different patients sharing oneinsurance_id(same DOB/address, different given names) →shared_insurance_different_patients,recommended_disposition: verify_insurance_membership_do_not_merge.follow_up_queues:authorization_missing= referrals withauthorization_status: missing;records_request= referrals missingoffice_note;imaging_follow_up= referrals missing bothmriandxray. Sort each ascending.- Action-plan tiers: Tier 1 = urgent referrals + duplicate blockers
(
urgent_coding_or_duplicate_blocker); Tier 2 = routine referrals with coding/auth issues (routine_coding_auth_or_document_blocker); Tier 3 = routine referrals whose only issue is missing documents (administrative_document_completion).owner_provider_id= the referral's receiving provider. summary_counts: derive every count from the lists above so they are internally consistent;validated_ready_no_follow_up_count= referrals in none of the issue lists.
Discipline when self-reviewing
- Change one conceptual thing at a time when revising. If you cannot tell whether a field is right, do not pile multiple speculative edits on top of each other — you will not be able to attribute an effect, and a field that turns out to be loosely checked is not worth continued tweaking.
- Prefer the interpretation that uses the raw record evidence over a clever re-derivation. Several plausible-but-wrong rewrites came from over-thinking a field the straightforward reading already got right: treating a benign abbreviation as a hard conflict, treating an orthopedic Injury-chapter code as in-range, downgrading a fully-documented allergy to "incomplete", or narrowing the narrative-match rule until clean referrals looked like mismatches.
- Never swap a value you are confident in for a coin-flip alternative. When two interpretations are roughly equally likely and the current one is already defensible, keep it and spend the attention on fields you can verify against fetched records.
See reference/task_families.md for the per-family field checklists.