Skip to content

Medical Advice Remediation — Feature Spec

Status: Partially implemented — Fixes 1,2,3,5 merged (PR #84). Fix 4 (disclaimer banner) not started. Fix 6 (footer) parked (legal). Depends on: Clinical advisor sign-off on the proposed wording Companion steer: ai-steer/medical-advice-audit-steer.md

This is the implementation plan for the audit findings documented in the medical advice audit steer. Do not merge any of this without clinical review — the wording changes are the whole point, and clinical wording should never be drive-by-merged.


Overview

Six surgical fixes ranked by risk and ROI. Each fix is small enough to ship as its own PR; the recommended order ships them as one batch (except #6, which depends on legal review).


Fix 1 — Rewrite risk_assessor mitigation language

Finding: Risk assessor mitigation strings use imperative verbs ("Optimize", "Hold", "Avoid", "Coordinate") that read as clinical instructions to the patient.

Files: - app/services/risk_assessor.py_comorbidity_mitigation() and the inline mitigation strings inside assess_risks() (~20 strings)

Pattern: Convert from imperative to descriptive. Replace verbs like "Optimize X" with "Providers typically look for X before surgery".

Proposed new strings (NEEDS CLINICAL REVIEW):

Current (imperative) Proposed (descriptive)
"Optimize HbA1c <8% pre-op; coordinate insulin/oral agents with surgical protocol." "Providers typically request HbA1c <8% before scheduling. Your treating physician can advise on glycemic optimization."
"Continue BP meds except day-of (per anesthesia guidance)." "Providers typically review BP medication timing with the patient's cardiologist before surgery."
"Cardiology clearance and bridging anticoagulation plan." "Providers typically request cardiology clearance and a bridging plan before surgery for patients with this history."
"Echo within 6 months; BNP and cardiology clearance." "Providers typically review a recent echo and BNP, and consult cardiology, before scheduling."
"Avoid nephrotoxic drugs; confirm contrast safety pre-imaging." "Providers typically check renal function before contrast imaging and review medication choices with nephrology."
"PFTs and pulmonology clearance; bronchodilator availability." "Providers typically request pulmonary function tests and pulmonology review for patients with this history."
"Bring CPAP machine; flag for anesthesia." "Providers typically ask patients to bring their CPAP and flag this for the anesthesia team."
"Iron studies; correct hemoglobin if elective surgery permits." "Providers typically review iron studies and may request optimization before elective surgery."
"Coordinate with prescribing physician on hold timing (typically 2–5 days pre-op) and bridging therapy." "Providers typically coordinate with the prescribing physician on a hold and bridging plan (often 2–5 days pre-op)."
"Discuss hold timing with cardiologist (typically 5–7 days pre-op)." "Providers typically coordinate hold timing with cardiology (often 5–7 days pre-op)."
"Discuss perioperative management with prescribing physician; some agents are held, others continued." "Providers typically coordinate perioperative management with the prescribing physician."
"Hold typically 5–7 days pre-op unless contraindicated." "Providers typically ask patients to hold these for 5–7 days pre-op when clinically appropriate."
"Hematology workup; correct before elective surgery." "Providers typically request a hematology workup and may defer elective surgery for correction."
"Hematology workup; possible platelet transfusion pre-op." "Providers typically request a hematology workup; transfusion may be considered pre-op."
"Confirm anticoagulation hold and recheck INR pre-op." "Providers typically confirm the anticoagulation hold plan and recheck INR before surgery."
"Pre-op cardiology and anesthesia consult recommended." "Providers typically request cardiology and anesthesia consults for patients in this age range."
"Confirm cardiac and renal function are in range pre-op." "Providers typically confirm cardiac and renal function are in range before scheduling."
"Confirm HbA1c and adjust insulin/oral agents per surgical protocol." "Providers typically request a recent HbA1c; medication adjustments are coordinated with the patient's prescriber."
"Confirm renal function with repeat labs if value is older than 30 days." "Providers typically request renal function within 30 days of surgery."
"Nephrology consult recommended; avoid nephrotoxic agents." "Providers typically request nephrology consult and review medication choices for patients with this eGFR range."

Verb removal checklist — none of the new strings should contain: Optimize, Hold, Avoid, Bring, Correct, Coordinate (as imperative to the patient — "providers coordinate" is fine), Discuss, Confirm, Recheck, Stop, Take, Continue (when directed at the patient).

Tests: Update tests/test_risk_assessor.py mitigation assertions to match the new strings. ~30 lines of test changes.


Fix 2 — Reframe lab_analyzer auto-detected comorbidities

Finding: lab_analyzer.py produces entries like "Prediabetes — detected from HbA1c 6.3%" which is a diagnostic label assigned by the system to a patient.

Files: - app/agents/lab_analyzer.pydetect_comorbidities_from_labs()

Pattern: Change from diagnostic label to range-based observation. Replace name: "Prediabetes" with name: "HbA1c 6.3% — in the range providers flag as prediabetes". Same data, no diagnostic verb.

Proposed new format (NEEDS CLINICAL REVIEW):

Current name Proposed name
"Prediabetes" "HbA1c {value}% — in the prediabetes range"
"Type 2 Diabetes" (HbA1c >6.5) "HbA1c {value}% — in the diabetes range"
"Mild Anemia" "Hemoglobin {value} g/dL — below the typical reference range"
"Anemia" "Hemoglobin {value} g/dL — below the typical reference range"
"Iron Deficiency" "Ferritin {value} ng/mL — below the typical reference range"
"Reduced Kidney Function" "eGFR {value} mL/min — providers typically flag values below 60 for review"
"CKD Stage 3" "eGFR {value} mL/min — providers typically flag values below 60 for review"
"Hyperlipidemia" "LDL {value} mg/dL — above the typical reference range"
"Hypothyroidism" "TSH {value} mIU/L — above the typical reference range"
"Hyperthyroidism" "TSH {value} mIU/L — below the typical reference range"

Critical: The icd10 and risk_level fields stay the same — they're machine-readable categories used by matching, not patient-facing strings. Only the name field (which is rendered in the EHR drawer) changes.

Tests: Update lab analyzer test fixtures (~10 lines).


Fix 3 — Reframe records-first reply

Finding: Records-first reply uses "You'll need a CBC..." which is prescriptive in form.

Files: - app/agents/case_orchestrator.py_format_required_records_block() - app/agents/llm_conversation.pyrecords_first phase prompt

Pattern: Pure framing change. Same data, descriptive instead of imperative.

Proposed change to _format_required_records_block header:

# Current
"REQUIRED RECORDS FOR THIS PROCEDURE (use these EXACT names and validity windows in "
"your reply — do NOT invent your own list, do NOT change the validity periods):\n\n"

# Proposed
"PROVIDER-REQUESTED RECORDS FOR THIS PROCEDURE (these are the records international "
"providers typically request before reviewing this case — use these exact names and "
"validity windows in your reply, do NOT invent your own list, do NOT change the "
"validity periods):\n\n"

And the LLM phase prompt instructions in llm_conversation.py records_first phase need to include:

"Frame the records as 'providers typically request' rather than 'you'll need'. The list comes from the receiving hospital's standard intake requirements, not from a Curaway clinical recommendation."


Fix 4 — Add disclaimer banners to clinical sections

Finding: EHR Risk Assessment section has no context — it just shows risk factors as if Curaway is presenting medical findings.

Files: - curaway-health-navigator/src/components/ehr/FullEHRDrawer.tsxRiskAssessmentSection - curaway-health-navigator/src/components/panels/EHRPanel.tsx — Risk Assessment section - curaway-health-navigator/src/components/panels/SummaryPanel.tsx — risk teaser line

Pattern: A small disclaimer rendered above the risks list.

Proposed text (NEEDS CLINICAL + LEGAL REVIEW):

"These are factors international providers consider when reviewing your case. They are not medical advice — discuss any concerns with your treating physician."

Visual: Small italic gray text, ~12px, above the section content. Not a banner with an icon (too alarming) — just a brief contextual note.

Same disclaimer on the EHR drawer comorbidities section since auto-detected comorbidities live there.


Fix 5 — CI test for prescriptive verbs in patient-facing strings

Finding: Nothing currently prevents a future PR from re-introducing imperative clinical verbs into patient-facing rich cards.

Files: - New: tests/test_no_medical_advice.py

Pattern: Same as test_voice_compliance.py — scans for forbidden verbs in specific patient-facing string sources.

Sources to scan: - app/services/risk_assessor.py_comorbidity_mitigation() return values + inline mitigation strings - app/agents/lab_analyzer.pyname field of detected comorbidities - app/agents/case_orchestrator.py_format_required_records_block() output and the on-site tests notice text - Any string that ends up in a rich_content payload patient-facing

Forbidden verbs (when used as imperative directed at the patient): - Optimize, Hold, Avoid, Bring, Correct, Discuss, Confirm, Recheck, Stop, Take, Continue, Coordinate - You should, You need to, You must, Make sure you, Be sure to - Diagnostic verbs: you have, diagnosed with, suffering from, condition is

Allowed exceptions: When wrapped in "providers typically..." or "international providers..." framing, or inside a comment / docstring block. Same exception pattern as test_voice_compliance.py.

Test should fail the build when an error-severity violation is detected (similar to test_voice_compliance.py).


Finding: No "no medical advice" disclaimer anywhere in the platform footer.

Files: - curaway-health-navigator/src/components/Footer.tsx (or wherever the marketing-site footer lives) - The conversational app footer (if any)

Pattern: Standard healthtech boilerplate.

Proposed text (NEEDS LEGAL REVIEW — wording is jurisdiction-specific):

"Curaway Health Technologies is a medical travel coordination platform. We help patients connect with international providers — we are not a medical practice and do not provide medical advice, diagnosis, or treatment. Always seek the advice of your physician or other qualified health provider with any questions you may have regarding a medical condition."

This needs legal review before merging — wording varies by jurisdiction (US, UAE, UK, EU all have different rules) and "always seek the advice" phrasing has specific FDA precedent.


Suggested rollout order

  1. Tonight, before any more real-patient testing: Fixes 1–4 as one PR (the linguistic fixes + the disclaimer banner). Requires clinical advisor sign-off on the proposed strings.
  2. This week: Fix 5 (CI test) so we can't regress.
  3. Whenever legal is consulted: Fix 6 (footer disclaimer).

What this audit deliberately does NOT change

  • The risk assessor logic itself (rules, thresholds, severities) — those are clinically defensible and ADR-0013 documents the reasoning
  • The lab analyzer detection thresholds — same
  • The Neo4j REQUIRES_TEST data — sourced from providers, not Curaway
  • The CoT prompts (PR #76) — internal only, currently safe
  • The matching engine — pure data, no clinical advice

This is a wording and disclaimer remediation, not a feature rollback. The clinical intelligence is valuable; the words around it need to be honest about what they are (provider considerations) rather than what they aren't (clinical instructions).


Edge Cases

These edge cases apply to the remaining Fix 4 (disclaimer banner) — Fixes 1, 2, 3, and 5 are already merged (PR #84).

Edge Case Scenario Handling Severity
Disclaimer dismissed but patient navigates to a new case Patient views the EHR drawer for Case A, sees the disclaimer, mentally registers it. They switch to Case B — should the disclaimer re-appear? If dismissed state is stored globally (e.g., localStorage), the patient never sees it again even on a new case with different risk factors. If stored per-case, they see it every time they switch cases. Store dismissed state per-session (not per-case, not permanently). Use sessionStorage with key disclaimer_dismissed. The disclaimer appears once per browser session regardless of case switches — this balances visibility with annoyance. On a new browser session (new tab, next day), it reappears. Do not use a backend flag — this is a pure UI concern. The disclaimer text is identical across cases so per-case display adds friction without value. Medium
Disclaimer text differs by jurisdiction The patient's country_of_residence may require different disclaimer language. For example, UK patients may need a reference to the Care Quality Commission, UAE patients to the DHA, US patients to FDA guidance. A single disclaimer string may not be legally sufficient for all jurisdictions. For MVP, use a single generic disclaimer (the one in Fix 4: "These are factors international providers consider..."). It deliberately avoids jurisdiction-specific claims. Post-MVP, add a disclaimer_overrides map in config/voice_rules.yaml keyed by country code. The RiskAssessmentSection component reads the patient's country from case metadata and selects the appropriate text. If no override exists, fall back to the generic text. Flag any jurisdiction-specific text for legal review before deployment. Low
Screen reader accessibility The disclaimer is small italic gray text (12px) — it may be visually de-emphasized but must still be announced by screen readers. If rendered as a decorative element or with aria-hidden, assistive technology users miss it entirely. Render the disclaimer as a <p> with role="note" and aria-label="Important disclaimer about this section". Do not use aria-hidden. Ensure the text color meets WCAG AA contrast ratio against the background (gray-on-white needs to be at least 4.5:1 for 12px text — verify the specific gray value). Use text-gray-500 minimum on white backgrounds; prefer text-gray-600 for safety. Add a sr-only prefix like "Note:" if the visual styling alone doesn't convey that it's a disclaimer. Medium
Disclaimer overlaps with chat input bar on mobile On mobile viewports (< 768px), the EHR drawer opens as a full-screen overlay. If the disclaimer is positioned at the top of the Risk Assessment section and the section is near the bottom of the scrollable area, the disclaimer could be clipped or overlap with the fixed input bar / bottom nav. The disclaimer is part of the scrollable content inside the EHR drawer — it should scroll with the section, not be fixed-positioned. Ensure the EHR drawer's scroll container has pb-[env(safe-area-inset-bottom)] plus additional padding for the bottom nav height (already addressed in P0 item 3.5 of the mobile-responsive spec). Test specifically on iPhone SE (375px) with the Risk Assessment section expanded to verify the disclaimer is fully visible without scrolling behind the bottom nav. Low

Implementation checklist

When approved for execution:

  • [ ] Clinical advisor sign-off on Fix 1 wording (20 strings)
  • [ ] Clinical advisor sign-off on Fix 2 wording (10 strings)
  • [ ] Update app/services/risk_assessor.py mitigations
  • [ ] Update app/agents/lab_analyzer.py comorbidity names
  • [ ] Update app/agents/case_orchestrator.py records-first framing
  • [ ] Update app/agents/llm_conversation.py records_first phase prompt
  • [ ] Add disclaimer banner to RiskAssessmentSection in FullEHRDrawer.tsx
  • [ ] Add disclaimer banner to EHRPanel.tsx Risk Assessment section
  • [ ] Update tests/test_risk_assessor.py for new mitigation strings
  • [ ] Update lab analyzer test fixtures for new comorbidity names
  • [ ] New tests/test_no_medical_advice.py CI scanner
  • [ ] Legal review of Fix 6 disclaimer text
  • [ ] Footer disclaimer in marketing site
  • [ ] Footer disclaimer in conversation app

Reference

  • Audit findings: ai-steer/medical-advice-audit-steer.md
  • Risk assessor design: ADR-0013
  • Existing voice rules: config/voice_rules.yaml
  • Existing output validator: app/services/output_validator.py
  • Existing CI scanner pattern: tests/test_voice_compliance.py