Skip to content

Steer-01: EHR Builder — Real-Time Record Assembly

Read first: specs/sdd-mvp/05-ehr-builder.md, specs/sdd-mvp/02-event-driven-design.md, specs/sdd-mvp/03-data-architecture.md

Context & Goal

Build the EHR Builder service — the central system that assembles a structured patient health record from heterogeneous, asynchronous data sources (chat, documents, agents). Every new data point triggers an event and updates the record in real-time. This is the foundation that all other steer sessions write to.


Backend (curaway-ai/curaway-backend)

Step 1: Create EHR Builder Service

Create app/services/ehr_builder_service.py:

  • EHRBuilderService class with dependency injection for fhir_service, patient_service, graph_service
  • process_event(event: Event) -> EHRUpdateResult — main entry point, dispatches by event_type
  • merge_clinical_entities(patient_id, tenant_id, entities, source, confidence) -> list[FHIRResource] — creates/updates FHIR resources with conflict detection
  • update_from_chat(patient_id, tenant_id, extracted_data, source_message_id) -> EHRUpdateResult — processes structured data from Intake Agent chat extraction
  • calculate_intake_progress(patient_id, tenant_id, procedure_code) -> float — returns 0.0–1.0 based on weighted category completion (demographics 0.15, medical history 0.25, procedure-specific 0.25, preferences 0.10, documents 0.15, consent 0.10)
  • get_ehr_summary(patient_id, tenant_id) -> EHRSummary — returns complete summary for left panel
  • detect_conflicts(patient_id, existing, incoming) -> list[ConflictRecord] — compares against existing FHIR resources

Step 2: Merge Rules Implementation

Implement the four merge rules from spec: 1. Same source, newer data → update with audit trail (old value in event history) 2. Different sources, same field → prefer structured (Clinical Agent FHIR > document extraction > chat extraction > self-reported). Both stored, higher-confidence canonical. 3. Conflicting clinical data → store both with confidence scores, flag if delta > 0.3 4. Missing required fields → tracked in progress, Intake Agent asks proactively

Step 3: Intake Progress Calculator

Create progress calculation based on procedure-specific requirements: - Load procedure requirements from Neo4j (REQUIRES_TEST relationships) - Check each required/conditional/recommended item against patient data - Weight categories and compute total (see spec for weights) - For Aisha TKR: demographics, conditions (M17.11, E11, I10), required docs (X-ray, blood panel, ECG, HbA1c), preferences, consent

Step 4: EHR Summary Endpoint

Create GET /api/v1/patients/{patient_id}/ehr-summary: - Returns structured summary grouped by category (conditions, medications, allergies, documents, progress) - Each item includes source, confidence, created_at - Document checklist with required/conditional/recommended status - Total intake_progress percentage

Step 5: Event Handlers

Create event handlers in app/services/ehr_event_handlers.py: - handle_clinical_entities_extracted → call merge_clinical_entities - handle_fhir_resource_created → update Neo4j graph + Qdrant embedding - handle_chat_data_collected → call update_from_chat - handle_document_parsed → check for clinical content, chain to Clinical Agent if found - handle_consent_changed → update consent status in progress calculation - Each handler emits ehr_updated event after processing (for SSE)

Step 6: Anomaly Detection

Create app/services/anomaly_detector.py: - detect_expired_documents(patient_id, procedure_code) — flag docs older than validity_days - detect_out_of_range_labs(patient_id) — compare lab values against reference ranges - detect_missing_required_docs(patient_id, procedure_code) — check against procedure checklist - detect_conflicting_diagnoses(patient_id) — cross-reference conditions from different sources - Return list of AnomalyFlag objects with severity and suggested action

Step 7: Tests

Write tests in tests/test_ehr_builder.py: - Test merge rules (all 4 scenarios) - Test progress calculation with partial data - Test conflict detection with overlapping conditions - Test anomaly detection (expired doc, missing required doc) - Test event handler dispatch - Test EHR summary endpoint returns correct structure

Backend Verification

# Run EHR builder tests
python -m pytest tests/test_ehr_builder.py -v

# Check new endpoint exists
curl -s http://localhost:8000/docs | grep ehr-summary

# Type check
mypy app/services/ehr_builder_service.py --strict

Frontend (curaway-ai/curaway-frontend)

Step 1: EHR Summary Component

Create src/components/ehr/EHRSummary.tsx: - Fetches from GET /api/v1/patients/{id}/ehr-summary - Collapsible sections: Conditions, Medications, Allergies, Documents, Preferences - Each condition shows source badge (Agent/Chat/Document) and confidence indicator - Progress bar at top with percentage - Refreshes on ehr_update SSE event

Step 2: Document Checklist Component

Create src/components/ehr/DocumentChecklist.tsx: - Shows required/conditional/recommended documents for current procedure - Status icons: ✅ uploaded+analyzed, ⏳ processing, ⬜ missing, ⚠️ expired - Clicking missing doc opens file upload dialog - Updates via SSE document_update events

Step 3: Wire into Left Panel

Update the left panel (sliding 300px panel) to include: - Tab: "EHR" → EHRSummary component - Tab: "Docs" → DocumentChecklist component - Both tabs auto-refresh on SSE events

Step 4: Progress Strip Update

Update right-side progress strip to read intake_progress from EHR summary endpoint instead of local state. Subscribe to intake_progress SSE events.

Frontend Verification

npm run build  # must be clean
npx tsc --noEmit  # type check
# Manual: verify EHR summary renders in left panel with mock data

Checklist

  • [ ] Alembic migration (if any schema changes for anomaly flags)
  • [ ] Seed data: TKR procedure requirements in Neo4j (if not already seeded)
  • [ ] Error codes: EHR_MERGE_CONFLICT_001, EHR_PROGRESS_CALC_001, EHR_ANOMALY_001
  • [ ] Feature flag: ehr_anomaly_detection (Flagsmith)
  • [ ] Env vars: none new expected
  • [ ] Swagger: new /ehr-summary endpoint documented
  • [ ] Unit tests: 8+ tests for merge rules, progress, anomalies
  • [ ] PostHog: track ehr_summary_viewed, document_checklist_interaction
  • [ ] Loading states: skeleton loaders for EHR summary sections
  • [ ] Mobile: left panel collapses, EHR summary accessible via hamburger menu
  • [ ] CLAUDE.md: update both repos with EHR Builder service description
  • [ ] Rollback: EHR Builder service is additive — disable via feature flag if issues