# Signal Demo MVP — Core Build Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build Confirm Visit workflow, 90/60/45 priority system, full documentation state machine, CSV cycle reset diff, transferred patient detection, and polished worklist UI for the Robert Robinson (Gaboro) demo on June 10, 2026. **Architecture:** Extend the existing FastAPI backend (Railway) with new status flags, a doc state machine, confirmed visit persistence in Supabase, and a Confirm Visit API endpoint. Extend the React/Vite frontend (Vercel) with expandable rows, a 5-dot doc status bar, and the Confirm Visit modal that updates live without a page refresh. **Tech Stack:** Python 3.11 / FastAPI (Railway), Supabase (PostgreSQL + RLS), React 18 / Vite / Tailwind CSS (pnpm), Clerk auth (JWT v2) **Key URLs:** - Backend (live): https://signal-api-production-91c2.up.railway.app - Frontend (local dev): http://localhost:5173 — run `cd signal-ui && pnpm dev` - Frontend (Vercel): https://signal-ui-xi.vercel.app - Deploy backend: `railway up --detach` from project root (forces fresh build) - Run tests: `cd /path/to/signal && python -m pytest tests/ -v` **PHI Contract (non-negotiable):** All backend code may only touch: patient_id, device_type, shipment_date, quantity, payer, component, and doc status fields (swo_status, pa_status, etc.). No patient names, SSNs, DOBs, addresses, or contact info. patient_id is hashed before storage. **Clerk org for Gaboro:** org_3EPAEcAw06V2yGMSkxE3UjqIA3c (already in CLAUDE.md + provisioned in Supabase via DEMO_ORG_SLUG="gaboro-pilot") --- ## File Map **Create:** - `python-backend/core/doc_state_machine.py` — DocState dataclass, payer-dependent doc requirements, cascade logic - `python-backend/db/migrations/001_add_confirmed_visits.sql` — Supabase migration for confirmed_visits table - `signal-ui/src/components/DocStatusBar.jsx` — 5 colored dots, one per doc item - `signal-ui/src/components/ConfirmVisitModal.jsx` — date input modal with validation - `signal-ui/src/components/CascadeAlert.jsx` — cascade consequence chain display - `tests/test_doc_state_machine.py` - `tests/test_coverage_flags.py` **Modify:** - `python-backend/core/coverage_calculator.py` — new CoverageFlag enum, 90/60/45 visit tier logic, visit date priority chain, updated ShipmentRecord with optional doc fields - `python-backend/api/main.py` — updated RecordOut (doc_state, visit_confidence, cascade, is_transfer), new confirm-visit endpoint, updated FLAG_LABELS - `python-backend/api/normalizer.py` — add doc columns + transfer columns to HEADER_MAP, update ShipmentRecord construction - `python-backend/core/persistence.py` — add get_confirmed_visit(), upsert_confirmed_visit(), load_confirmed_visits_for_org() - `python-backend/config/payer_rules.json` — add medicare_advantage section, pa_required/pecos_required flags - `python-backend/db/schema.sql` — add confirmed_visits table definition - `signal-ui/src/components/WorklistTable.jsx` — expandable rows, DocStatusBar, Confirm Visit button, cascade display - `signal-ui/src/components/Badge.jsx` — new status labels + visual weight (bold/normal per tier) - `signal-ui/src/lib/api.js` — add confirmVisit() function --- ## Task 1: Extend ShipmentRecord with optional doc fields **Files:** - Modify: `python-backend/core/coverage_calculator.py:42-55` Read `python-backend/core/coverage_calculator.py` before editing. - [ ] **Step 1: Write the failing test** Create `tests/test_coverage_flags.py`: ```python import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent / "python-backend")) from datetime import date, timedelta from core.coverage_calculator import ShipmentRecord, calculate_coverage, CoverageFlag TODAY = date.today() def make_record(**kwargs): defaults = dict( patient_id="PT001", device_type="dexcom_g7", shipment_date=TODAY - timedelta(days=10), quantity=1, payer="medicare", component="sensor", ) defaults.update(kwargs) return ShipmentRecord(**defaults) def test_shipment_record_accepts_csv_visit_date(): r = make_record(csv_visit_date=TODAY - timedelta(days=30)) assert r.csv_visit_date == TODAY - timedelta(days=30) def test_shipment_record_accepts_doc_fields(): r = make_record( csv_swo_status="On File", csv_pecos_verified="Yes", csv_pa_status="Not Required", csv_diagnosis_on_file="Yes", ) assert r.csv_swo_status == "On File" assert r.csv_pecos_verified == "Yes" def test_shipment_record_doc_fields_default_none(): r = make_record() assert r.csv_visit_date is None assert r.csv_swo_status is None assert r.csv_transfer_from is None ``` - [ ] **Step 2: Run test — verify it fails** ```bash cd /Users/sttil-solutions/projects/signal && python -m pytest tests/test_coverage_flags.py -v ``` Expected: FAIL — ShipmentRecord has no field `csv_visit_date` - [ ] **Step 3: Add optional doc fields to ShipmentRecord** Replace the existing ShipmentRecord dataclass (lines ~42-55) with: ```python from typing import Optional # add to imports if not already present @dataclass(frozen=True) class ShipmentRecord: """ Minimal shipment record. Only non-PHI fields allowed. patient_id: Supplier's internal MRN or account number. This is the sole crosswalk key — no real identity data here. csv_* fields: Optional doc status columns parsed from the CSV. csv_transfer_from: Non-empty string means this is a transferred patient. """ patient_id: str device_type: str shipment_date: date quantity: int payer: str component: str = "sensor" # Optional doc fields — populated when CSV contains these columns csv_visit_date: Optional[date] = None csv_swo_status: Optional[str] = None # "On File" | "Pending" | "Expired" csv_pecos_verified: Optional[str] = None # "Yes" | "No" csv_pa_status: Optional[str] = None # "Approved" | "Pending" | "Denied" | "Not Required" csv_diagnosis_on_file: Optional[str] = None # "Yes" | "No" csv_transfer_from: Optional[str] = None # prior supplier name if transfer ``` - [ ] **Step 4: Run test — verify it passes** ```bash cd /Users/sttil-solutions/projects/signal && python -m pytest tests/test_coverage_flags.py::test_shipment_record_accepts_csv_visit_date tests/test_coverage_flags.py::test_shipment_record_doc_fields_default_none -v ``` Expected: PASS - [ ] **Step 5: Commit** ```bash cd /Users/sttil-solutions/projects/signal git add python-backend/core/coverage_calculator.py tests/test_coverage_flags.py git commit -m "feat: extend ShipmentRecord with optional doc state fields from CSV" ``` --- ## Task 2: New CoverageFlag enum + 90/60/45 priority system **Files:** - Modify: `python-backend/core/coverage_calculator.py` - [ ] **Step 1: Write failing tests** Add to `tests/test_coverage_flags.py`: ```python def test_supply_lapsed_flag_when_coverage_ended(): # Shipment 200 days ago, 1 sensor (10 wear days) — coverage ended long ago r = make_record(shipment_date=TODAY - timedelta(days=200), quantity=1) result = calculate_coverage(r) assert result.flag == CoverageFlag.SUPPLY_LAPSED def test_renewal_critical_at_45_days(): # Last confirmed visit was 135 days ago — next visit due in 45 days r = make_record( shipment_date=TODAY - timedelta(days=10), quantity=1, csv_visit_date=TODAY - timedelta(days=135), ) result = calculate_coverage(r) assert result.flag == CoverageFlag.RENEWAL_CRITICAL def test_renewal_elevated_at_60_days(): r = make_record( shipment_date=TODAY - timedelta(days=10), quantity=1, csv_visit_date=TODAY - timedelta(days=120), ) result = calculate_coverage(r) assert result.flag == CoverageFlag.RENEWAL_ELEVATED def test_renewal_soon_at_90_days(): r = make_record( shipment_date=TODAY - timedelta(days=10), quantity=1, csv_visit_date=TODAY - timedelta(days=90), ) result = calculate_coverage(r) assert result.flag == CoverageFlag.RENEWAL_SOON def test_active_when_visit_far_away(): r = make_record( shipment_date=TODAY - timedelta(days=10), quantity=1, csv_visit_date=TODAY - timedelta(days=10), ) result = calculate_coverage(r) assert result.flag == CoverageFlag.ACTIVE def test_transfer_pending_flag(): r = make_record(csv_transfer_from="Prior Supplier LLC") result = calculate_coverage(r) assert result.flag == CoverageFlag.TRANSFER_PENDING def test_priority_sort_order(): lapsed = make_record(shipment_date=TODAY - timedelta(days=200)) critical = make_record(patient_id="PT002", shipment_date=TODAY - timedelta(days=10), csv_visit_date=TODAY - timedelta(days=135)) active = make_record(patient_id="PT003", shipment_date=TODAY - timedelta(days=10), csv_visit_date=TODAY - timedelta(days=10)) from core.coverage_calculator import calculate_batch results = calculate_batch([active, critical, lapsed]) assert results[0].flag == CoverageFlag.SUPPLY_LAPSED assert results[1].flag == CoverageFlag.RENEWAL_CRITICAL assert results[2].flag == CoverageFlag.ACTIVE ``` - [ ] **Step 2: Run tests — verify they fail** ```bash cd /Users/sttil-solutions/projects/signal && python -m pytest tests/test_coverage_flags.py -v 2>&1 | head -40 ``` - [ ] **Step 3: Replace CoverageFlag enum + update calculate_coverage** In `python-backend/core/coverage_calculator.py`, replace the entire CoverageFlag enum and CoverageResult dataclass and all calculation functions with the following. Read the existing file first to preserve imports. **New CoverageFlag enum** (replace existing): ```python class CoverageFlag(str, Enum): SUPPLY_LAPSED = "SUPPLY_LAPSED" # supply cycle ended, no new shipment VISIT_REQUIRED = "VISIT_REQUIRED" # visit date past due, no confirmed new visit TRANSFER_PENDING = "TRANSFER_PENDING" # transferred patient, all docs need verification RENEWAL_CRITICAL = "RENEWAL_CRITICAL" # ≤ 45 days to next visit due RENEWAL_ELEVATED = "RENEWAL_ELEVATED" # ≤ 60 days to next visit due RENEWAL_SOON = "RENEWAL_SOON" # ≤ 90 days to next visit due RESUPPLY_READY = "RESUPPLY_READY" # within refill window, visit not yet urgent ACTIVE = "ACTIVE" # all clear ``` **New CoverageResult dataclass** (replace existing): ```python @dataclass class CoverageResult: patient_id: str device_type: str payer: str component: str last_shipment_date: date coverage_end_date: date next_visit_due_date: Optional[date] flag: CoverageFlag days_until_coverage_end: int days_until_visit_due: Optional[int] priority_score: int visit_date_confidence: str = "estimated" # "confirmed" | "estimated" is_transfer: bool = False rule_version: str = RULE_VERSION ``` **New helper functions** (replace _compute_priority): ```python def _resolve_visit_date( record: ShipmentRecord, confirmed_visit_date: Optional[date] = None, ) -> tuple[Optional[date], str]: """ Visit date priority chain: 1. CSV visit date column (if present + valid) 2. Supabase confirmed visit date (stored by staff) 3. Estimated proxy: shipment_date - 30 days Returns (resolved_date, confidence) where confidence is 'confirmed' or 'estimated'. """ if record.csv_visit_date: return record.csv_visit_date, "confirmed" if confirmed_visit_date: return confirmed_visit_date, "confirmed" estimated = record.shipment_date - timedelta(days=30) return estimated, "estimated" def _compute_visit_flag( next_visit_due: date, today: date, ) -> CoverageFlag: """Map days-to-next-visit to the appropriate tier flag.""" days = (next_visit_due - today).days if days < 0: return CoverageFlag.VISIT_REQUIRED if days <= 45: return CoverageFlag.RENEWAL_CRITICAL if days <= 60: return CoverageFlag.RENEWAL_ELEVATED if days <= 90: return CoverageFlag.RENEWAL_SOON return CoverageFlag.ACTIVE def _compute_priority(flag: CoverageFlag, days_until_visit: Optional[int]) -> int: """ Priority score for worklist sort. Higher = more urgent. Supply Lapsed and Transfer Pending are always highest. Within visit tiers, urgency increases as days decrease. """ urgency_days = abs(days_until_visit) if days_until_visit is not None else 0 if flag == CoverageFlag.SUPPLY_LAPSED: return 2000 + urgency_days if flag == CoverageFlag.TRANSFER_PENDING: return 1800 if flag == CoverageFlag.VISIT_REQUIRED: return 1500 + urgency_days if flag == CoverageFlag.RENEWAL_CRITICAL: return 1000 + (45 - max(0, urgency_days)) if flag == CoverageFlag.RENEWAL_ELEVATED: return 700 + (60 - max(0, urgency_days)) if flag == CoverageFlag.RENEWAL_SOON: return 400 + (90 - max(0, urgency_days)) if flag == CoverageFlag.RESUPPLY_READY: return 200 return 0 # ACTIVE ``` **New calculate_coverage function** (replace existing): ```python def calculate_coverage( record: ShipmentRecord, as_of: Optional[date] = None, confirmed_visit_date: Optional[date] = None, ) -> CoverageResult: """ Calculate coverage status for a single shipment record. Args: record: ShipmentRecord with non-PHI fields only. as_of: Date to evaluate against. Defaults to today. confirmed_visit_date: Confirmed visit date from Supabase (staff-entered). """ rules = _load_payer_rules() today = as_of or date.today() # Transferred patients: all docs need verification before anything else if record.csv_transfer_from: return CoverageResult( patient_id=record.patient_id, device_type=record.device_type, payer=record.payer, component=record.component, last_shipment_date=record.shipment_date, coverage_end_date=record.shipment_date, next_visit_due_date=None, flag=CoverageFlag.TRANSFER_PENDING, days_until_coverage_end=0, days_until_visit_due=None, priority_score=1800, visit_date_confidence="estimated", is_transfer=True, rule_version=RULE_VERSION, ) wear_days = _get_wear_days(rules, record.device_type, record.component) payer_config = _get_payer_config(rules, record.payer) total_wear_days = wear_days * record.quantity coverage_end = record.shipment_date + timedelta(days=total_wear_days) days_until_end = (coverage_end - today).days # Supply lapsed — coverage cycle ended if days_until_end < 0: visit_date, confidence = _resolve_visit_date(record, confirmed_visit_date) next_visit_due = visit_date + timedelta(days=180) if visit_date else None days_visit = (next_visit_due - today).days if next_visit_due else None return CoverageResult( patient_id=record.patient_id, device_type=record.device_type, payer=record.payer, component=record.component, last_shipment_date=record.shipment_date, coverage_end_date=coverage_end, next_visit_due_date=next_visit_due, flag=CoverageFlag.SUPPLY_LAPSED, days_until_coverage_end=days_until_end, days_until_visit_due=days_visit, priority_score=_compute_priority(CoverageFlag.SUPPLY_LAPSED, days_visit), visit_date_confidence=confidence, rule_version=RULE_VERSION, ) visit_renewal_days = payer_config.get("visit_renewal_days") refill_window_days = payer_config.get("refill_window_days", 30) # Resolve visit date using priority chain visit_date, confidence = _resolve_visit_date(record, confirmed_visit_date) next_visit_due: Optional[date] = None days_until_visit: Optional[int] = None flag = CoverageFlag.ACTIVE if visit_renewal_days and visit_date: next_visit_due = visit_date + timedelta(days=visit_renewal_days) days_until_visit = (next_visit_due - today).days flag = _compute_visit_flag(next_visit_due, today) # If no visit urgency but within refill window, mark resupply ready if flag == CoverageFlag.ACTIVE and days_until_end <= refill_window_days: flag = CoverageFlag.RESUPPLY_READY priority = _compute_priority(flag, days_until_visit) return CoverageResult( patient_id=record.patient_id, device_type=record.device_type, payer=record.payer, component=record.component, last_shipment_date=record.shipment_date, coverage_end_date=coverage_end, next_visit_due_date=next_visit_due, flag=flag, days_until_coverage_end=days_until_end, days_until_visit_due=days_until_visit, priority_score=priority, visit_date_confidence=confidence, rule_version=RULE_VERSION, ) ``` **Update calculate_batch** to pass confirmed_visit_date (leave the dict lookup for now — that's added in Task 5): ```python def calculate_batch( records: list[ShipmentRecord], as_of: Optional[date] = None, confirmed_visits: Optional[dict[str, date]] = None, ) -> list[CoverageResult]: """ confirmed_visits: dict mapping patient_id_hash → confirmed visit date. Loaded from Supabase by persistence layer before calling this function. """ import hashlib confirmed_visits = confirmed_visits or {} results = [] for record in records: patient_hash = hashlib.sha256(record.patient_id.encode()).hexdigest() confirmed_date = confirmed_visits.get(patient_hash) try: result = calculate_coverage(record, as_of=as_of, confirmed_visit_date=confirmed_date) results.append(result) except ValueError as exc: logger.warning("Skipping record for patient_id hash — %s", exc) results.sort(key=lambda r: r.priority_score, reverse=True) return results ``` - [ ] **Step 4: Run tests — verify they pass** ```bash cd /Users/sttil-solutions/projects/signal && python -m pytest tests/test_coverage_flags.py -v ``` Expected: All PASS - [ ] **Step 5: Commit** ```bash cd /Users/sttil-solutions/projects/signal git add python-backend/core/coverage_calculator.py tests/test_coverage_flags.py git commit -m "feat: 90/60/45 visit priority system with new CoverageFlag enum" ``` --- ## Task 3: Doc State Machine **Files:** - Create: `python-backend/core/doc_state_machine.py` - Create: `tests/test_doc_state_machine.py` - [ ] **Step 1: Write the failing test** Create `tests/test_doc_state_machine.py`: ```python import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent / "python-backend")) from datetime import date, timedelta from core.doc_state_machine import compute_doc_state, DocState TODAY = date.today() def test_medicare_ffs_pa_not_required(): state = compute_doc_state( payer_type="medicare", csv_swo_status="On File", csv_visit_date=TODAY - timedelta(days=30), confirmed_visit_date=None, csv_pecos_verified="Yes", csv_pa_status=None, csv_diagnosis_on_file="Yes", is_transfer=False, ) assert state.pa == "Not Required" assert state.pecos == "Verified" def test_medicaid_pecos_not_applicable(): state = compute_doc_state( payer_type="medicaid", csv_swo_status="On File", csv_visit_date=TODAY - timedelta(days=30), confirmed_visit_date=None, csv_pecos_verified=None, csv_pa_status="Approved", csv_diagnosis_on_file="Yes", is_transfer=False, ) assert state.pecos == "N/A" assert state.pa == "Approved" def test_transfer_patient_all_pending(): state = compute_doc_state( payer_type="medicare", csv_swo_status=None, csv_visit_date=None, confirmed_visit_date=None, csv_pecos_verified=None, csv_pa_status=None, csv_diagnosis_on_file=None, is_transfer=True, ) assert state.swo == "Pending — Verify" assert state.visit == "Pending — Verify" assert state.pecos == "Pending — Verify" assert state.pa == "Pending — Verify" assert state.diagnosis == "Pending — Verify" def test_cascade_visit_not_confirmed(): state = compute_doc_state( payer_type="medicare", csv_swo_status="On File", csv_visit_date=None, confirmed_visit_date=None, csv_pecos_verified="Yes", csv_pa_status=None, csv_diagnosis_on_file="Yes", is_transfer=False, ) assert len(state.cascade) > 0 assert any("visit" in c.lower() for c in state.cascade) def test_no_cascade_when_all_clear(): state = compute_doc_state( payer_type="medicare", csv_swo_status="On File", csv_visit_date=TODAY - timedelta(days=30), confirmed_visit_date=None, csv_pecos_verified="Yes", csv_pa_status=None, csv_diagnosis_on_file="Yes", is_transfer=False, ) assert state.cascade == [] ``` - [ ] **Step 2: Run test — verify it fails** ```bash cd /Users/sttil-solutions/projects/signal && python -m pytest tests/test_doc_state_machine.py -v 2>&1 | head -20 ``` Expected: FAIL — module not found - [ ] **Step 3: Create doc_state_machine.py** Create `python-backend/core/doc_state_machine.py`: ```python """ doc_state_machine.py Signal — STTIL Solutions Computes per-patient documentation status across 5 items. Requirements are payer-dependent. Produces cascade consequence chain. PHI Contract: no patient names, DOBs, or contact info in any parameter. """ from dataclasses import dataclass, field from datetime import date from typing import Optional # Payer types that require PECOS enrollment _PECOS_REQUIRED = {"medicare", "medicare_advantage"} # Payer types that require Prior Authorization for CGM _PA_REQUIRED = {"medicare_advantage", "medicaid", "commercial"} # Medicare FFS: PA not required (CGM coverage expansion 2023 + CMS-1828-F exemption path) _PA_NOT_REQUIRED = {"medicare"} @dataclass class DocState: swo: str = "Pending" # "On File" | "Pending" | "Expired" visit: str = "Not Confirmed" # "Confirmed YYYY-MM-DD" | "Estimated YYYY-MM-DD" | "Not Confirmed" pecos: str = "Not Verified" # "Verified" | "Not Verified" | "N/A" | "Pending — Verify" pa: str = "Required — Not Started" # "Not Required" | "Approved" | "Pending" | "Denied" | "Required — Not Started" | "Pending — Verify" diagnosis: str = "Missing" # "On File" | "Missing" | "Pending — Verify" cascade: list[str] = field(default_factory=list) def _resolve_visit_display( csv_visit_date: Optional[date], confirmed_visit_date: Optional[date], is_transfer: bool, ) -> tuple[str, bool]: """ Returns (display_string, is_confirmed). is_confirmed=True means the date is reliable (from CSV or staff-confirmed). """ if is_transfer: return "Pending — Verify", False if csv_visit_date: return f"Confirmed {csv_visit_date.isoformat()}", True if confirmed_visit_date: return f"Confirmed {confirmed_visit_date.isoformat()}", True return "Not Confirmed — Estimated Only", False def _build_cascade( swo: str, visit_confirmed: bool, pecos: str, pa: str, diagnosis: str, payer_type: str, ) -> list[str]: """ Build the downstream consequence chain for missing/at-risk doc items. Only add cascade steps that are actually blocked. """ cascade: list[str] = [] if not visit_confirmed: cascade.append("Qualifying visit not confirmed") if swo in ("Pending", "Expired"): cascade.append("SWO validity at risk") if payer_type in _PA_REQUIRED and pa in ("Pending", "Required — Not Started"): cascade.append("PA renewal cannot proceed") cascade.append("Next shipment blocked") return cascade if swo in ("Pending", "Expired"): cascade.append("SWO missing or expired") if payer_type in _PECOS_REQUIRED and pecos not in ("Verified",): cascade.append("PECOS enrollment at risk") cascade.append("Next shipment blocked") if diagnosis == "Missing": cascade.append("Diagnosis documentation missing — required for all payers") return cascade def compute_doc_state( payer_type: str, csv_swo_status: Optional[str], csv_visit_date: Optional[date], confirmed_visit_date: Optional[date], csv_pecos_verified: Optional[str], csv_pa_status: Optional[str], csv_diagnosis_on_file: Optional[str], is_transfer: bool = False, ) -> DocState: """ Compute the full documentation state for one patient. payer_type: "medicare" | "medicare_advantage" | "medicaid" | "commercial" All csv_* fields are Optional — None means not present in the CSV. """ # Transfer patients: everything is pending verification if is_transfer: return DocState( swo="Pending — Verify", visit="Pending — Verify", pecos="Pending — Verify", pa="Pending — Verify", diagnosis="Pending — Verify", cascade=[ "Patient transferred from prior supplier", "Verify: SWO from current prescriber, qualifying visit within 6 months, PA status with current payer", ], ) # SWO swo_map = {"on file": "On File", "pending": "Pending", "expired": "Expired"} swo = swo_map.get((csv_swo_status or "").strip().lower(), "Pending") # Visit visit_display, visit_confirmed = _resolve_visit_display( csv_visit_date, confirmed_visit_date, is_transfer ) # PECOS — only required for Medicare if payer_type in _PECOS_REQUIRED: pecos_val = (csv_pecos_verified or "").strip().lower() pecos = "Verified" if pecos_val in ("yes", "verified", "true", "1") else "Not Verified" else: pecos = "N/A" # PA — not required for Medicare FFS; required for MA, Medicaid, Commercial if payer_type in _PA_NOT_REQUIRED: pa = "Not Required" elif csv_pa_status: pa_map = { "approved": "Approved", "pending": "Pending", "denied": "Denied", "not required": "Not Required", "not started": "Required — Not Started", } pa_key = csv_pa_status.strip().lower() pa = pa_map.get(pa_key, "Required — Not Started") else: pa = "Required — Not Started" # Diagnosis dx_val = (csv_diagnosis_on_file or "").strip().lower() diagnosis = "On File" if dx_val in ("yes", "true", "on file", "1") else "Missing" cascade = _build_cascade(swo, visit_confirmed, pecos, pa, diagnosis, payer_type) return DocState( swo=swo, visit=visit_display, pecos=pecos, pa=pa, diagnosis=diagnosis, cascade=cascade, ) ``` - [ ] **Step 4: Run tests — verify they pass** ```bash cd /Users/sttil-solutions/projects/signal && python -m pytest tests/test_doc_state_machine.py -v ``` Expected: All PASS - [ ] **Step 5: Commit** ```bash cd /Users/sttil-solutions/projects/signal git add python-backend/core/doc_state_machine.py tests/test_doc_state_machine.py git commit -m "feat: doc state machine — 5-item payer-dependent requirements with cascade logic" ``` --- ## Task 4: payer_rules.json updates + Supabase confirmed_visits migration **Files:** - Modify: `python-backend/config/payer_rules.json` - Create: `python-backend/db/migrations/001_add_confirmed_visits.sql` - Modify: `python-backend/db/schema.sql` - [ ] **Step 1: Update payer_rules.json** Read the file first, then replace its contents: ```json { "_comment": "Wear-day rules by device type and payer. Updated 2026-06-07. Run quarterly review per compliance checklist.", "devices": { "dexcom_g6": { "display_name": "Dexcom G6", "sensor_wear_days": 10, "transmitter_wear_days": 90, "components": ["sensor", "transmitter"] }, "dexcom_g7": { "display_name": "Dexcom G7", "sensor_wear_days": 10, "components": ["sensor"] }, "freestyle_libre_2": { "display_name": "FreeStyle Libre 2", "sensor_wear_days": 14, "components": ["sensor"] }, "freestyle_libre_3": { "display_name": "FreeStyle Libre 3", "sensor_wear_days": 14, "components": ["sensor"] }, "omnipod_5": { "display_name": "Omnipod 5", "pod_wear_days": 3, "sensor_wear_days": 14, "components": ["pod", "sensor"] } }, "payer_rules": { "medicare": { "visit_renewal_days": 180, "refill_window_days": 30, "pa_required": false, "pecos_required": true, "_note": "Medicare FFS. PA not required (CGM coverage expansion 2023). Face-to-face visit required every 6 months. CMS-1828-F: suppliers at ≥90% affirmation rate may qualify for PA exemption from June 1, 2026.", "covered_devices": ["dexcom_g6", "dexcom_g7", "freestyle_libre_2", "freestyle_libre_3"] }, "medicare_advantage": { "visit_renewal_days": 180, "refill_window_days": 30, "pa_required": true, "pecos_required": true, "_note": "Medicare Advantage — PA required, varies by plan. Verify plan-specific rules.", "covered_devices": ["dexcom_g6", "dexcom_g7", "freestyle_libre_2", "freestyle_libre_3"] }, "medicaid": { "visit_renewal_days": 180, "refill_window_days": 30, "pa_required": true, "pecos_required": false, "_pa_note_pa": "PA required — Keystone First, UPMC Health Plan, Highmark BCBS require PA. NJ FamilyCare, Horizon BCBS NJ require PA.", "covered_devices": [] }, "commercial": { "visit_renewal_days": 180, "refill_window_days": 30, "pa_required": true, "pecos_required": false, "_note": "Commercial payer rules vary by plan. PA required by default — verify per plan.", "covered_devices": [] }, "default": { "visit_renewal_days": 180, "refill_window_days": 30, "pa_required": true, "pecos_required": false, "covered_devices": [] } } } ``` - [ ] **Step 2: Create Supabase migration SQL** Create `python-backend/db/migrations/001_add_confirmed_visits.sql`: ```sql -- Migration 001: Add confirmed_visits table -- Run in Supabase SQL Editor: Dashboard > SQL Editor > New query -- Safe to run multiple times. create table if not exists confirmed_visits ( id uuid primary key default uuid_generate_v4(), org_id uuid not null references organizations(id) on delete cascade, patient_id_hash text not null, -- SHA-256 of patient_id — no raw PHI confirmed_date date not null, confirmed_by text, -- staff identifier (not PHI — just a label) created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique(org_id, patient_id_hash) ); create index if not exists idx_confirmed_visits_org on confirmed_visits(org_id); create index if not exists idx_confirmed_visits_hash on confirmed_visits(patient_id_hash); alter table confirmed_visits enable row level security; ``` - [ ] **Step 3: Add confirmed_visits to schema.sql** Read `python-backend/db/schema.sql`, then append before the final `-- Indexes` section: ```sql -- Confirmed visit dates (staff-entered, persists across CSV imports) create table if not exists confirmed_visits ( id uuid primary key default uuid_generate_v4(), org_id uuid not null references organizations(id) on delete cascade, patient_id_hash text not null, confirmed_date date not null, confirmed_by text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique(org_id, patient_id_hash) ); create index if not exists idx_confirmed_visits_org on confirmed_visits(org_id); create index if not exists idx_confirmed_visits_hash on confirmed_visits(patient_id_hash); alter table confirmed_visits enable row level security; ``` - [ ] **Step 4: Run the migration in Supabase** The migration must be run manually in the Supabase dashboard SQL editor (there is no CLI migration runner configured). Copy the contents of `python-backend/db/migrations/001_add_confirmed_visits.sql` and paste into the Supabase dashboard SQL editor at your project. After running, verify: `SELECT COUNT(*) FROM confirmed_visits;` should return 0 with no error. - [ ] **Step 5: Commit** ```bash cd /Users/sttil-solutions/projects/signal git add python-backend/config/payer_rules.json python-backend/db/migrations/001_add_confirmed_visits.sql python-backend/db/schema.sql git commit -m "feat: payer_rules medicare_advantage + PA/NJ rules; confirmed_visits migration" ``` --- ## Task 5: Persistence — confirmed visit read/write + upload diff **Files:** - Modify: `python-backend/core/persistence.py` Read `python-backend/core/persistence.py` before editing — preserve all existing functions. - [ ] **Step 1: Add confirmed visit functions** Append to `python-backend/core/persistence.py`: ```python def upsert_confirmed_visit( org_id: str, patient_id_hash: str, confirmed_date: date, confirmed_by: str = "staff", ) -> bool: """ Insert or update a confirmed visit date for a patient. Returns True on success, False if Supabase unavailable. """ client = get_client() if not client: return False try: client.table("confirmed_visits").upsert({ "org_id": org_id, "patient_id_hash": patient_id_hash, "confirmed_date": confirmed_date.isoformat(), "confirmed_by": confirmed_by, "updated_at": "now()", }, on_conflict="org_id,patient_id_hash").execute() return True except Exception as e: logger.error(f"Failed to upsert confirmed visit: {e}") return False def load_confirmed_visits_for_org(org_id: str) -> dict[str, date]: """ Load all confirmed visit dates for an org. Returns dict mapping patient_id_hash → confirmed_date. """ client = get_client() if not client: return {} try: result = client.table("confirmed_visits") \ .select("patient_id_hash,confirmed_date") \ .eq("org_id", org_id) \ .execute() return { row["patient_id_hash"]: date.fromisoformat(row["confirmed_date"]) for row in (result.data or []) } except Exception as e: logger.error(f"Failed to load confirmed visits: {e}") return {} def get_or_create_org(clerk_org_id: str | None = None) -> str | None: """Public wrapper around _get_or_create_org for use by the API.""" return _get_or_create_org(clerk_org_id=clerk_org_id) ``` - [ ] **Step 2: Verify imports in main.py still work** ```bash cd /Users/sttil-solutions/projects/signal/python-backend && python -c "from core.persistence import upsert_confirmed_visit, load_confirmed_visits_for_org, get_or_create_org; print('OK')" ``` Expected: `OK` - [ ] **Step 3: Commit** ```bash cd /Users/sttil-solutions/projects/signal git add python-backend/core/persistence.py git commit -m "feat: confirmed visit persistence — upsert and bulk load per org" ``` --- ## Task 6: Normalizer updates — doc columns + transfer columns **Files:** - Modify: `python-backend/api/normalizer.py` Read `python-backend/api/normalizer.py` before editing. - [ ] **Step 1: Add doc column mappings to HEADER_MAP** In normalizer.py, add these entries to the `HEADER_MAP` dict (after the existing "component" entry): ```python "csv_visit_date": [ "visit_date", "visit date", "qualifying_visit_date", "qualifying visit date", "last_visit_date", "last visit date", "face_to_face_date", "f2f_date", "encounter_date", "encounter date", "physician_visit_date", ], "csv_swo_status": [ "swo_status", "swo status", "swo", "standing_written_order", "standing written order", "order_status", "order status", ], "csv_pecos_verified": [ "pecos_verified", "pecos verified", "pecos", "pecos_status", "enrollment_verified", "enrollment verified", ], "csv_pa_status": [ "pa_status", "pa status", "prior_auth_status", "prior auth status", "prior_authorization_status", "auth_status", "pa", "authorization", ], "csv_diagnosis_on_file": [ "diagnosis_on_file", "diagnosis on file", "diagnosis", "dx_on_file", "dx on file", "icd_on_file", "icd on file", ], "csv_transfer_from": [ "transfer_from", "transfer from", "previous_supplier", "previous supplier", "prior_supplier", "prior supplier", "transfer_status", "transferred_from", ], ``` - [ ] **Step 2: Update the ShipmentRecord construction in normalize_csv** In the `normalize_csv` function, after the existing `component` parsing, add: ```python # Optional doc fields — only populated if the CSV contains these columns csv_visit_date: Optional[date] = None raw_visit = mapped.get("csv_visit_date", "") if raw_visit: csv_visit_date = _parse_date(raw_visit) # None if unparseable (non-error) csv_swo_status = mapped.get("csv_swo_status") or None csv_pecos_verified = mapped.get("csv_pecos_verified") or None csv_pa_status = mapped.get("csv_pa_status") or None csv_diagnosis_on_file = mapped.get("csv_diagnosis_on_file") or None transfer_raw = mapped.get("csv_transfer_from", "").strip() csv_transfer_from = transfer_raw if transfer_raw else None ``` Then update the `ShipmentRecord(...)` constructor call to include these fields: ```python records.append(ShipmentRecord( patient_id=patient_id, device_type=device_type, shipment_date=shipment_date, quantity=quantity, payer=payer, component=component, csv_visit_date=csv_visit_date, csv_swo_status=csv_swo_status, csv_pecos_verified=csv_pecos_verified, csv_pa_status=csv_pa_status, csv_diagnosis_on_file=csv_diagnosis_on_file, csv_transfer_from=csv_transfer_from, )) ``` - [ ] **Step 3: Update _normalize_payer in normalizer.py to distinguish Medicare Advantage** Find the PAYER_MAP dict and update the medicare_advantage line: ```python "medicare advantage": "medicare_advantage", # distinct from medicare FFS ``` - [ ] **Step 4: Verify the normalizer still runs on a baseline CSV** ```bash cd /Users/sttil-solutions/projects/signal && python -c " from python_backend.api.normalizer import normalize_csv import pathlib text = pathlib.Path('test-data/sample-batch-01-ok.csv').read_text() records, skipped, summary = normalize_csv(text) print(f'Records: {len(records)}, Skipped: {len(skipped)}') print('First record:', records[0] if records else 'none') " ``` Expected: Records >= 1, no errors Alternatively run from python-backend dir: ```bash cd /Users/sttil-solutions/projects/signal/python-backend && python -c " from api.normalizer import normalize_csv import pathlib, sys text = pathlib.Path('../test-data/sample-batch-01-ok.csv').read_text() records, skipped, summary = normalize_csv(text) print(f'Records: {len(records)}, Skipped: {len(skipped)}') " ``` - [ ] **Step 5: Commit** ```bash cd /Users/sttil-solutions/projects/signal git add python-backend/api/normalizer.py git commit -m "feat: normalizer adds doc column mappings and transfer column detection" ``` --- ## Task 7: API — updated RecordOut + confirm-visit endpoint + upload integration **Files:** - Modify: `python-backend/api/main.py` Read `python-backend/api/main.py` before editing. - [ ] **Step 1: Add imports at top of main.py** Add these imports (after existing imports): ```python import hashlib from datetime import date as date_type from core.doc_state_machine import compute_doc_state, DocState from core.persistence import ( persist_export, persist_upload, upsert_confirmed_visit, load_confirmed_visits_for_org, get_or_create_org, ) ``` Remove the existing `from core.persistence import persist_export, persist_upload` line. - [ ] **Step 2: Update FLAG_LABELS and FLAG_ACTIONS for new flags** Replace the existing FLAG_LABELS and FLAG_ACTIONS dicts: ```python FLAG_LABELS = { "SUPPLY_LAPSED": "Supply Lapsed", "VISIT_REQUIRED": "Visit Required", "TRANSFER_PENDING": "Needs Verification — Transfer", "RENEWAL_CRITICAL": "Renewal Due — Critical", "RENEWAL_ELEVATED": "Renewal Due SOON", "RENEWAL_SOON": "Renewal Due SOON", "RESUPPLY_READY": "Resupply Ready", "ACTIVE": "Active", } FLAG_ACTIONS = { "SUPPLY_LAPSED": "Contact Prescriber — Supply Lapsed", "VISIT_REQUIRED": "Contact Prescriber — Visit Overdue", "TRANSFER_PENDING": "Verify All Documentation", "RENEWAL_CRITICAL": "Contact Prescriber — Confirm Visit Appointment", "RENEWAL_ELEVATED": "Contact Prescriber — Schedule Qualifying Visit", "RENEWAL_SOON": "Monitor — Visit Due in 90 Days", "RESUPPLY_READY": "Initiate Resupply", "ACTIVE": "No action needed", } ``` - [ ] **Step 3: Update RecordOut to include doc state and visit confidence** Replace the existing RecordOut class: ```python class DocStateOut(BaseModel): swo: str visit: str pecos: str pa: str diagnosis: str class RecordOut(BaseModel): patient_id: str device_type: str device_display: str payer: str component: str days_until_coverage_end: int days_until_visit_due: Optional[int] = None flag: str priority_score: int coverage_end_date: str next_visit_due_date: Optional[str] = None action: str status_label: str reason: str rule_version: str visit_date_confidence: str = "estimated" is_transfer: bool = False doc_state: Optional[DocStateOut] = None cascade: list[str] = [] ``` - [ ] **Step 4: Update _to_record_out to compute doc state** Replace the existing `_to_record_out` function. It now needs access to the original ShipmentRecord to compute doc state: ```python def _to_record_out(r, record=None) -> RecordOut: flag_val = r.flag.value if hasattr(r.flag, "value") else str(r.flag) # Compute doc state if we have the original record doc_state_out = None cascade = [] if record is not None: normalized_payer = _normalize_payer_type(r.payer) doc = compute_doc_state( payer_type=normalized_payer, csv_swo_status=record.csv_swo_status, csv_visit_date=record.csv_visit_date, confirmed_visit_date=None, # already factored into r.next_visit_due_date csv_pecos_verified=record.csv_pecos_verified, csv_pa_status=record.csv_pa_status, csv_diagnosis_on_file=record.csv_diagnosis_on_file, is_transfer=bool(record.csv_transfer_from), ) doc_state_out = DocStateOut( swo=doc.swo, visit=doc.visit, pecos=doc.pecos, pa=doc.pa, diagnosis=doc.diagnosis ) cascade = doc.cascade return RecordOut( patient_id=r.patient_id, device_type=r.device_type, device_display=DEVICE_DISPLAY.get(r.device_type, r.device_type), payer=r.payer, component=r.component, days_until_coverage_end=r.days_until_coverage_end, days_until_visit_due=r.days_until_visit_due, flag=flag_val, priority_score=r.priority_score, coverage_end_date=r.coverage_end_date.isoformat(), next_visit_due_date=r.next_visit_due_date.isoformat() if r.next_visit_due_date else None, action=FLAG_ACTIONS.get(flag_val, "Review"), status_label=FLAG_LABELS.get(flag_val, flag_val), reason=_build_reason(flag_val, r.days_until_coverage_end, r.days_until_visit_due), rule_version=r.rule_version, visit_date_confidence=getattr(r, "visit_date_confidence", "estimated"), is_transfer=getattr(r, "is_transfer", False), doc_state=doc_state_out, cascade=cascade, ) def _normalize_payer_type(payer: str) -> str: """Map raw payer string to doc_state_machine payer_type.""" from core.coverage_calculator import _normalize_payer normalized = _normalize_payer(payer) # normalizer maps "medicare advantage" → "medicare_advantage" but coverage_calc maps it to "commercial" # Use the raw payer string for MA detection if "medicare advantage" in payer.lower() or "medicare_advantage" in payer.lower(): return "medicare_advantage" return normalized ``` - [ ] **Step 5: Update the /api/upload endpoint to pass records to _to_record_out and load confirmed visits** In the `upload_csv` function, replace: ```python results = calculate_batch(records, as_of=date.today()) out = [_to_record_out(r) for r in results] ``` with: ```python # Load confirmed visit dates from Supabase for this org clerk_org_id = claims.get("o", {}).get("id") if isinstance(claims.get("o"), dict) else None org_id = get_or_create_org(clerk_org_id=clerk_org_id) confirmed_visits = load_confirmed_visits_for_org(org_id) if org_id else {} results = calculate_batch(records, as_of=date.today(), confirmed_visits=confirmed_visits) # Build a lookup from patient_id to original record for doc state computation record_lookup = {r.patient_id: r for r in records} out = [_to_record_out(r, record=record_lookup.get(r.patient_id)) for r in results] ``` Also remove the duplicate `clerk_org_id` extraction later in the function (it was used for persist_upload) and use the one computed above. - [ ] **Step 6: Add the confirm-visit endpoint** Add after the existing `/api/export` endpoint: ```python class ConfirmVisitRequest(BaseModel): patient_id: str confirmed_date: str # ISO date string YYYY-MM-DD # Fields needed to recompute coverage after confirmation shipment_date: str payer: str device_type: str quantity: int = 1 component: str = "sensor" @app.post("/api/confirm-visit") async def confirm_visit( body: ConfirmVisitRequest, claims: dict = Depends(require_auth), ): """ Store a staff-confirmed qualifying visit date and return the updated coverage record. The confirmed date persists across all future CSV imports for this patient_id. """ from core.coverage_calculator import ShipmentRecord, calculate_coverage # Validate dates try: confirmed = date_type.fromisoformat(body.confirmed_date) shipment = date_type.fromisoformat(body.shipment_date) except ValueError as e: raise HTTPException(status_code=400, detail=f"Invalid date: {e}. Use YYYY-MM-DD.") if confirmed > date_type.today(): raise HTTPException(status_code=400, detail="Confirmed date cannot be in the future.") # Get org clerk_org_id = claims.get("o", {}).get("id") if isinstance(claims.get("o"), dict) else None org_id = get_or_create_org(clerk_org_id=clerk_org_id) if not org_id: raise HTTPException(status_code=503, detail="Organization not found.") # Hash and store patient_hash = hashlib.sha256(body.patient_id.encode()).hexdigest() success = upsert_confirmed_visit(org_id, patient_hash, confirmed, confirmed_by="staff") if not success: raise HTTPException(status_code=503, detail="Failed to save confirmed visit.") # Recompute coverage with confirmed visit date record = ShipmentRecord( patient_id=body.patient_id, device_type=body.device_type, shipment_date=shipment, quantity=body.quantity, payer=body.payer, component=body.component, ) result = calculate_coverage(record, confirmed_visit_date=confirmed) log_event( AuditAction.CSV_INGEST, f"confirm-visit:{body.patient_id[:8]}", "staff", "success", "0.0.0.0", detail=f"Visit confirmed {confirmed.isoformat()}", ) return _to_record_out(result, record=record) ``` - [ ] **Step 7: Verify the backend starts cleanly** ```bash cd /Users/sttil-solutions/projects/signal && python -m uvicorn python-backend.api.main:app --port 8001 --no-access-log 2>&1 | head -5 ``` Expected: no import errors. Kill with Ctrl+C after confirming. Or from the python-backend directory: ```bash cd /Users/sttil-solutions/projects/signal/python-backend && python -m uvicorn api.main:app --port 8001 2>&1 | head -5 ``` - [ ] **Step 8: Commit** ```bash cd /Users/sttil-solutions/projects/signal git add python-backend/api/main.py git commit -m "feat: confirm-visit endpoint + RecordOut with doc_state + cascade" ``` --- ## Task 8: Deploy backend to Railway - [ ] **Step 1: Deploy** ```bash cd /Users/sttil-solutions/projects/signal && railway up --detach ``` - [ ] **Step 2: Wait for build and verify health** After Railway build completes (watch status at https://railway.app or `railway status`): ```bash curl -s https://signal-api-production-91c2.up.railway.app/health | python3 -c "import json,sys; d=json.load(sys.stdin); print(d)" ``` Expected: `{'status': 'ok', 'service': 'signal-api', 'version': '1.0.0'}` --- ## Task 9: Frontend — Badge.jsx update for new status labels **Files:** - Modify: `signal-ui/src/components/Badge.jsx` Read `signal-ui/src/components/Badge.jsx` before editing. - [ ] **Step 1: Replace Badge.jsx with updated flag handling** The new flags are: SUPPLY_LAPSED, VISIT_REQUIRED, TRANSFER_PENDING, RENEWAL_CRITICAL, RENEWAL_ELEVATED, RENEWAL_SOON, RESUPPLY_READY, ACTIVE. Visual weight rules per spec: - SUPPLY_LAPSED: dark red, bold - VISIT_REQUIRED: red bold + warning icon - RENEWAL_CRITICAL: red bold - RENEWAL_ELEVATED: amber bold - RENEWAL_SOON: amber normal - RESUPPLY_READY: teal/brand, normal - ACTIVE: green normal - TRANSFER_PENDING: orange, normal ```jsx const FLAG_CONFIG = { SUPPLY_LAPSED: { label: "Supply Lapsed", bg: "bg-[rgba(180,0,0,0.12)]", text: "text-[#B00000]", border: "border-[#B00000]", weight: "font-bold", icon: "⚠", }, VISIT_REQUIRED: { label: "Visit Required", bg: "bg-[rgba(220,30,30,0.10)]", text: "text-[#CC2222]", border: "border-[#CC2222]", weight: "font-bold", icon: "⚠", }, TRANSFER_PENDING: { label: "Needs Verification", bg: "bg-[rgba(200,120,0,0.10)]", text: "text-[#C87800]", border: "border-[#C87800]", weight: "font-semibold", icon: "↔", }, RENEWAL_CRITICAL: { label: "Renewal Due — Critical", bg: "bg-[rgba(220,60,0,0.10)]", text: "text-[#DC3C00]", border: "border-[#DC3C00]", weight: "font-bold", icon: "", }, RENEWAL_ELEVATED: { label: "Renewal Due SOON", bg: "bg-[rgba(203,107,32,0.12)]", text: "text-[#CB6B20]", border: "border-[#CB6B20]", weight: "font-semibold", icon: "", }, RENEWAL_SOON: { label: "Renewal Due SOON", bg: "bg-[rgba(203,107,32,0.08)]", text: "text-[#CB6B20]", border: "border-[#CB6B20]", weight: "font-normal", icon: "", }, RESUPPLY_READY: { label: "Resupply Ready", bg: "bg-[rgba(46,163,163,0.10)]", text: "text-[#1A7070]", border: "border-[#2EA3A3]", weight: "font-normal", icon: "", }, ACTIVE: { label: "Active", bg: "bg-[rgba(40,160,80,0.10)]", text: "text-[#1A8040]", border: "border-[#28A050]", weight: "font-normal", icon: "", }, }; export default function Badge({ flag }) { const cfg = FLAG_CONFIG[flag] || { label: flag, bg: "bg-gray-100", text: "text-gray-600", border: "border-gray-300", weight: "font-normal", icon: "", }; return ( {cfg.icon && {cfg.icon}} {cfg.label} ); } ``` - [ ] **Step 2: Commit** ```bash cd /Users/sttil-solutions/projects/signal git add signal-ui/src/components/Badge.jsx git commit -m "feat: Badge updated for new status flags with visual weight system" ``` --- ## Task 10: Frontend — DocStatusBar component **Files:** - Create: `signal-ui/src/components/DocStatusBar.jsx` - [ ] **Step 1: Create DocStatusBar.jsx** ```jsx /** * DocStatusBar — 5 colored dots showing SWO, Visit, PECOS, PA, Diagnosis status. * Green = complete, Amber = pending/estimated, Red = missing/denied. */ const DOT_STATUS = { // SWO "On File": "green", "Expired": "red", // Visit "Not Confirmed — Estimated Only": "amber", "Not Confirmed": "amber", // PECOS "Verified": "green", "Not Verified": "red", "N/A": "gray", // PA "Not Required": "green", "Approved": "green", "Denied": "red", // Diagnosis "On File": "green", "Missing": "red", // Transfer / generic pending "Pending — Verify": "amber", "Pending": "amber", "Required — Not Started": "red", }; function dotColor(status) { if (!status) return "amber"; const s = status.trim(); if (s.startsWith("Confirmed")) return "green"; if (s.startsWith("Approved")) return "green"; return DOT_STATUS[s] || "amber"; } const COLOR_MAP = { green: "bg-[#28A050]", amber: "bg-[#CB6B20]", red: "bg-[#CC2222]", gray: "bg-[#AAAAAA]", }; const LABELS = ["SWO", "Visit", "PECOS", "PA", "Dx"]; export default function DocStatusBar({ docState }) { if (!docState) return null; const items = [ docState.swo, docState.visit, docState.pecos, docState.pa, docState.diagnosis, ]; return (