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

57 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
3. **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.
4. **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.
5. **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.
6. **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.
7. **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.
8. **_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.
9. **"refill" in user-facing text.** main.py:136 "30-day refill window" "resupply window". (Internal refill_window_days config keys non-urgent.)
10. **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
11. 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.
12. 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.
13. Audit calls hardcode "demo_user"/"0.0.0.0" (main.py:359, 381, 467, 549). Thread claims + request IP through.
14. 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.
15. Three conflicting status vocabularies RESOLVED by label decision above.
16. DocStatusBar.jsx:6-24 maps dead status strings; real values fall through to amber. Align keys with doc_state_machine.py values.
17. Duplicate patient_ids collapse in dict lookups (main.py:378, persistence.py:114). Key by (patient_id, shipment_date, component) or index-align.
18. 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.
19. Blank/unrecognized payer silently becomes commercial (normalizer.py:208). Surface warning in skipped_reasons/mapping_summary.
20. 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.
21. 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.
22. 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
23. Stale flag "OK" in _no_action_flags persistence.py:153.
24. Unused/redundant imports main.py:25, 499, 513.
25. catch(e){throw e} api.js:116-118; misleading warning text coverage_calculator.py:376.
26. escalateCount unused App.jsx:34.
27. Two parallel toast systems Toast.jsx + WorklistTable.jsx:45-53, 330-350.
28. expandedRow localStorage key auto-expands wrong row after re-import WorklistTable.jsx:41, 139, 199.
29. parseCSV naive comma split breaks quoted fields (fallback only) coverage.js:169-182.
30. EmptyState hardcodes light-theme colors EmptyState.jsx:8, 22.
31. /health/db leaks raw exception text main.py:339.
32. "now()" string literal to timestamptz persistence.py:193.
33. SUPPLY_LAPSED priority adds abs(days_until_visit) even when visit far future coverage_calculator.py:209-211.
34. 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.