89 lines
9.4 KiB
Markdown
89 lines
9.4 KiB
Markdown
# Signal Readiness Model — Design Brief for Pi
|
|
|
|
**Date:** 2026-06-23
|
|
**Status:** Design input for Pi. Pi produces the build design; then Claude builds.
|
|
**Inputs:** Session 2026-06-23 product decisions + end-to-end simulation + independent cto-architect review (folded in).
|
|
|
|
## 1. What Signal is
|
|
DMEPOS CGM documentation-readiness tracker. CSV in produces a per-patient stoplight worklist flagging documentation gaps before claim/PA submission. Signal identifies; supplier staff act. No PHI beyond a client-supplied patient_id (random UUID). Stack: FastAPI (Railway) + Supabase + Vite/React (Vercel) + Clerk.
|
|
|
|
## 2. Locked product decisions (authoritative; in the knowledge graph)
|
|
1. Signal NEVER determines, guesses, or extrapolates the payer plan type. Client supplies plan type as a REQUIRED mapped field. Missing means "plan type needed", grade what we can, never guess.
|
|
2. Signal does NOT determine eligibility or coverage. Remove that language from content. Rename "coverage" code to "readiness".
|
|
3. Every determination CITES its basis: the rule that fired + the client-supplied input.
|
|
4. Worklist unit = ONE record per patient UUID; all open items grouped; device detail nested.
|
|
5. Multi-device is real (Dexcom CGM + Omnipod pump). CGM-only graded now; non-CGM preserved/displayed but NOT graded; other DMEPOS categories later.
|
|
6. Monthly resupplies = separate dates of service, NOT duplicates. True duplicate = same patient + same device + same date of service.
|
|
7. Validated PA-vs-claim: visit, SWO, PECOS, diagnosis are claim/coverage requirements (universal per LCD L33822). PA is the only prior-auth item, payer-dependent. Medicare FFS requires no PA. Medicaid heaviest (initial + renewal). Medicaid uses STATE enrollment, not PECOS.
|
|
|
|
## 3. Recommended data model (5 entities, from the architecture review)
|
|
Smallest model that satisfies the locked decisions.
|
|
|
|
- **Patient (rollup unit):** patient_id (client UUID, only identifier, hashed in logs), org_id, coverage_lines[], rollup_status (DERIVED, never stored as truth).
|
|
- **CoverageLine (the gradeable unit, the keystone):** key (patient_id, device_type, plan_id). plan_type REQUIRED + client-supplied/mapped: medicare | medicare_advantage | medicaid | commercial | UNKNOWN. gradeable:bool (true only for CGM this phase; pumps/other = false). shipments[], doc_items[], line_status (derived), timing_flag (existing CoverageFlag, per line).
|
|
- **Shipment (history under a line):** key (patient_id, device_type, date_of_service). quantity, component, order_number, hcpcs. is_current:bool (exactly one per line = latest DOS).
|
|
- **DocItem (one of the 5, per line):** doc_type (swo|visit|pecos|pa|diagnosis), required_state (REQUIRED|NOT_REQUIRED|NOT_EVALUATED, computed from plan_type+rules), input_state (SUPPLIED|ABSENT), value, source (csv|staff_override|computed), citation.
|
|
- **Override (staff action, persisted):** key (org_id, patient_hash, device_type, doc_type). status, status_date, expiry_date, confirmed_by, confirmed_at.
|
|
|
|
plan_type lives on CoverageLine, NOT Patient. Grade at the line; display rolls up to the patient. (Checked against per-patient-single-plan: dual-eligible and multi-device patients would mis-grade, credibility-ending in a documentation tool. One extra grouping key, not a new subsystem.)
|
|
|
|
## 4. The verdict rule (kills the false green)
|
|
Separate two axes the current code conflates into one string:
|
|
- required_state: does this plan need this item? (from plan_type + rules, independent of CSV contents)
|
|
- input_state: did the client supply data? (SUPPLIED | ABSENT)
|
|
|
|
Per DocItem:
|
|
- NOT_REQUIRED + any input -> satisfied (green-eligible)
|
|
- REQUIRED + SUPPLIED + good -> satisfied
|
|
- REQUIRED + SUPPLIED + bad -> gap (At Risk / Action Needed)
|
|
- REQUIRED + ABSENT -> NOT_EVALUATED, blocks green, never satisfied
|
|
|
|
Rule: a line is "Clear to Ship" only if EVERY required item is satisfied. One required-but-absent item forces at least Action Needed. Green is earned by positive evidence on every required item, never granted by absence of contradicting evidence.
|
|
|
|
plan_type = UNKNOWN: cannot compute required_state, so the line is "Plan Type Needed" and structurally cannot be green. Still grade the universal items that do not depend on plan (SWO, visit, diagnosis per LCD L33822); PECOS and PA sit NOT_EVALUATED.
|
|
|
|
Patient rollup: light = worst gradeable line. Hard floor: any gradeable line in "Plan Type Needed" caps the patient no greener than Action Needed. Severity: Plan Type Needed / At Risk > Action Needed > On Track > Clear to Ship. Ungraded non-CGM lines excluded from the light and tab counts. Tabs count patients, not rows.
|
|
|
|
## 5. Cascade with citation
|
|
Cascade entry = flat typed record (not a plain string, not event sourcing):
|
|
- doc_type, severity (blocker|alert = Type A/B), reason, rule_id (names a key in payer_rules.json), client_input ("you marked plan type = Medicaid"), requirement, basis, rules_version.
|
|
Display string is RENDERED from these fields, not stored as prose.
|
|
Prerequisite: rules become addressable data. Move the _PECOS_REQUIRED / _PA_REQUIRED / _PA_NOT_REQUIRED logic OUT of Python sets and INTO payer_rules.json with stable rule IDs, so the citation points at the same data that drove the decision. Also fixes a latent bug: the Python sets and the JSON can currently disagree.
|
|
|
|
## 6. Override + recompute + rollup
|
|
- Override keyed (org_id, patient_hash, device_type, doc_type). Frontend cache keyed `${patient_id}:${device_type}`. Without device_type an override leaks across a patient's devices (present bug).
|
|
- Scope: VISIT is patient-scoped (the visit is the person; fans out to all that patient's gradeable lines). SWO / PA / diagnosis / PECOS are line-scoped. Current code treats everything as patient-scoped, which is why confirm-visit works and doc-status leaks.
|
|
- Recompute returns { line: updated line, rollup: new patient status }. Recompute the overridden line, then re-roll-up the patient. No full batch rebuild per click.
|
|
|
|
## 7. Dedup / aggregation
|
|
- Group by patient UUID; keep per-device detail.
|
|
- True-duplicate key = (patient_id, device_type, date_of_service). SMALL OPEN DECISION: the current code's dedup key also includes order_number. If DOS-level dedup is the contract, drop order_number from the key and keep it as a displayed attribute. Recommended default: drop it, treat same patient+device+DOS as one, keep latest. Pi/Kisa to confirm.
|
|
- Split-shipment tiebreaker: same patient+device+DOS with different quantities is not a duplicate; define (sum quantities, or order by ingestion).
|
|
- Grade the CURRENT shipment only (latest DOS per line); keep prior shipments as collapsed history. Today calculate runs per record, so 6 months of history = 6 worklist lines (the row-vs-patient bug).
|
|
|
|
## 8. Live bugs in the current code (this pass exists to fix these)
|
|
- Worklist is ROW-keyed, not patient-keyed end to end (rowKey = patient_id + index; localDocStates keyed by bare patient_id; one result per shipment). Decisions 4/5/6 assume a patient-grouped model the code does not implement. This is the core work.
|
|
- Plan-type GUESSING is still live: _normalize_payer keyword-matches the payer name, and a `default` payer config + raw-string fallback can grade an unknown plan and show green. Both violate decision #1. Consume the client-mapped plan_type field; use the payer NAME only for display; route unmatched/unknown to "Plan Type Needed", not `default`. Remove the default grade path.
|
|
- Override leak across a patient's devices (see section 6).
|
|
|
|
## 9. The 5 tracked items + rules engine
|
|
doc_state_machine.py grades SWO, qualifying visit (6-month), PECOS (Medicare/MA only), PA (MA/Medicaid/Commercial; not Medicare FFS), diagnosis. Rules in payer_rules.json. Medicaid uses state enrollment not PECOS; the Medicaid state-enrollment item is DEFERRED for the pilot (PECOS shows N/A for Medicaid), documented as a known gap, not an omission.
|
|
|
|
## 10. YAGNI / pilot scope (hold the line)
|
|
- Grade CGM only; non-CGM lines exist + display but gradeable=false. No multi-category grading.
|
|
- Six-field flat cascade entry; no event sourcing, no per-entry history (the audit log exists).
|
|
- No Medicaid state-enrollment item this phase.
|
|
- Override scope = the two facts staff change: visit (patient-scoped) + SWO/PA (line-scoped). No general override framework.
|
|
- Recompute returns {line, rollup}; no full re-batch per click.
|
|
|
|
## 11. Original blockers + trust fixes (mapping)
|
|
- Blocker 1 (MA misclassification) -> DELETE the name-guesser + require plan type + remove `default` grade path.
|
|
- Blocker 2 (duplicate patient_id) -> patient-grouped aggregation with per-device detail + DOS-level dedup.
|
|
- Blocker 3 (override does not recompute) -> device-keyed override + recompute + re-roll-up; persist override on next upload.
|
|
- Trust: retention 6 years (fix 2 stale "7 year" lines); privacy BAA wording attorney-gated (not built by us); audit-log persistence to DB (verify; part of override path).
|
|
|
|
## 12. Proposed build sequence (after Pi's design)
|
|
Rename coverage->readiness -> extract rules to addressable config (IDs) -> patient/line/shipment grouping + required-vs-input verdict -> device-keyed override + recompute + rollup -> require-plan-type + client value-mapping + remove guesser/default -> frontend nesting + "plan type needed" state. code-auditor reviews each before it ships.
|
|
|
|
## 13. The ask for Pi
|
|
Take a stab at the build design from this model. Resolve the small dedup order_number decision (section 7), the frontend nesting/UX for patient -> device -> doc plus the "plan type needed" state, and the client plan-type value-mapping UX at import. Keep it pilot-scoped. Hand back; then Claude builds.
|