Signal/docs/reviews/2026-06-11-quality-review.md
Kisa cc2a5f4b47 coverage calculator and UI refinements; add demo MVP gap analysis and quality review docs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 13:04:23 -04:00

6.5 KiB
Raw Blame History

Signal Quality Review — 2026-06-11

Read-only review of signal-ui and python-backend by background agent. No fixes applied yet. Execution plan: surgical pass = 2 CRITICAL + 8 HIGH first, then label unification (see decision note below).

Label decision (Kisa, 2026-06-11): All user-facing status labels are exactly four, consistent everywhere without fail: At Risk, Action Needed, On Track, Clear to Ship. Mapping: SUPPLY_LAPSED/VISIT_REQUIRED/RENEWAL_CRITICAL → At Risk; RENEWAL_ELEVATED/RENEWAL_SOON/TRANSFER_PENDING/NO_RECENT_SHIPMENT → Action Needed; RESUPPLY_READY → Clear to Ship; ACTIVE → On Track. Define once in backend; Badge.jsx, exports, and coverage.js consume it. Supersedes FLAG_LABELS in api/main.py:117-127 and coverage.js:20-29. This resolves findings 15 and 16.

CRITICAL

  1. Confirm Visit sends fabricated shipment date (today) and hardcoded quantity 1. ConfirmVisitModal.jsx:16-23 derives shipment = today; :58 hardcodes quantity 1. Backend recompute (main.py:538-546) then yields wrong Days Left/coverage end/flag; visit validation (main.py:514-523) anchors wrong. Fix: add last_shipment_date and quantity to RecordOut and pass through.
  2. Privacy page claims an executed BAA that does not exist. Privacy.jsx:104-110; also :97 says 7-year audit retention vs audit_logger.py 6 years. Fix: rewrite HIPAA section to actual de-identified posture; align retention number.

HIGH

  1. NO_RECENT_SHIPMENT unreachable — coverage_calculator.py:269-288 returns SUPPLY_LAPSED before the 12-month check at :312-328; same in coverage.js:127-154. Fix: check days_since_shipment >= 365 before supply-lapsed early return.
  2. Tricare → commercial. normalizer.py:202-208 normalizes first (no Tricare entries, default commercial), making coverage_calculator._normalize_payer TFL logic dead code; raw payer display string also destroyed. Fix: normalize in one place, keep raw string for display, add Tricare to normalizer.
  3. Frontend export drops order_number, hcpcs, recommended_next_step_code. api.js:62-89 omits them; export rows get blanks + NO_ACTION_NEEDED (main.py:170-172 defaults). Fix: map all three in apiRecordToLocal.
  4. Fallback priority math inverted. coverage.js:93-102: RENEWAL_CRITICAL 500 < RENEWAL_ELEVATED 700; SUPPLY_LAPSED 1000 vs backend 2000; uses daysUntilEnd vs backend days_until_visit_due. Fix: copy backend _compute_priority exactly.
  5. Confirmed visits never reflected in doc state; confirming regresses checklist. main.py:262 passes confirmed_visit_date=None; /api/confirm-visit rebuild (main.py:538-545) loses csv_* doc fields, frontend merge (WorklistTable.jsx:71-77) overwrites row with regressed state. Fix: pass confirmed date into compute_doc_state; echo existing doc fields through confirm payload.
  6. _recommended_next_step_code precedence bug. main.py:245 guard is constantly True (DocStateOut has no cascade attr); diagnosis "Missing" never checked → READY_TO_BILL with open diagnosis gap. Fix: drop broken guard, add diagnosis check.
  7. "refill" in user-facing text. main.py:136 "30-day refill window" → "resupply window". (Internal refill_window_days config keys non-urgent.)
  8. Frontend FLAG enum missing TRANSFER_PENDING; payer config drift. coverage.js:9-18 (8 vs 9 flags), no csv_visit_date support; getPayerConfig gives medicaid/commercial visitRenewalDays null vs payer_rules.json 180; visit due = shipment+180 vs backend (shipment30)+180. Fix: mirror backend flag set, payer table, 30-day offset.

MEDIUM

  1. Backend failures silently swallowed; weak local fallback runs (api.js:21-34, App.jsx:61-74); real CSVs can show "Loaded 0 patients" with no error. Surface backend errors; fall back only on network failure.
  2. Audit entries never reach WORM audit_log table — audit_logger.py:147-148 needs db_conn, no caller passes one. Add Supabase REST sink in log_event.
  3. Audit calls hardcode "demo_user"/"0.0.0.0" (main.py:359, 381, 467, 549). Thread claims + request IP through.
  4. Org resolution reads only "o" claim (main.py:371, 526); org_id-shaped JWTs silently land in shared gaboro-pilot org. Read o.id with org_id fallback; fail closed otherwise.
  5. Three conflicting status vocabularies — RESOLVED by label decision above.
  6. DocStatusBar.jsx:6-24 maps dead status strings; real values fall through to amber. Align keys with doc_state_machine.py values.
  7. Duplicate patient_ids collapse in dict lookups (main.py:378, persistence.py:114). Key by (patient_id, shipment_date, component) or index-align.
  8. Mapping review Override dropdown is a no-op (CSVImport.jsx:117-123); example_value (:52) never sent by backend. Wire it or remove until wired; checklist counts it PASS — correct the checklist if not wiring now.
  9. Blank/unrecognized payer silently becomes commercial (normalizer.py:208). Surface warning in skipped_reasons/mapping_summary.
  10. VITE_SIGNAL_API_KEY path would ship API key in public bundle if ever set (api.js:8). Remove frontend API-key path; Clerk token covers production.
  11. No explicit PHI-column guard in normalizer; bare "patient"/"id" aliases broad (normalizer.py:20-26). Add rejection list (name/dob/ssn/phone/address) that fails upload with clear message; drop bare "patient" alias. Checklist claims "PHI blocked at normalizer" — currently implicit only.
  12. Client/backend disagree on 6-month window: setMonth(-6) (ConfirmVisitModal.jsx:39-41) vs 183 days (main.py:514). Use 183 days both sides.

LOW

  1. Stale flag "OK" in _no_action_flags — persistence.py:153.
  2. Unused/redundant imports — main.py:25, 499, 513.
  3. catch(e){throw e} — api.js:116-118; misleading warning text — coverage_calculator.py:376.
  4. escalateCount unused — App.jsx:34.
  5. Two parallel toast systems — Toast.jsx + WorklistTable.jsx:45-53, 330-350.
  6. expandedRow localStorage key auto-expands wrong row after re-import — WorklistTable.jsx:41, 139, 199.
  7. parseCSV naive comma split breaks quoted fields (fallback only) — coverage.js:169-182.
  8. EmptyState hardcodes light-theme colors — EmptyState.jsx:8, 22.
  9. /health/db leaks raw exception text — main.py:339.
  10. "now()" string literal to timestamptz — persistence.py:193.
  11. SUPPLY_LAPSED priority adds abs(days_until_visit) even when visit far future — coverage_calculator.py:209-211.
  12. Sidebar hardcodes "Billing Staff" role + dead Settings link — Sidebar.jsx:126-130, 143-145.

Clean

  • Auth: /api/upload, /api/export, /api/confirm-visit all behind require_auth; frontend passes Clerk Bearer tokens. Only /health endpoints open (see 31).
  • PHI in audit log: all resource_ids hashed before storage.