update docs and prep for frontend

This commit is contained in:
voltsrage
2026-06-19 15:44:58 +08:00
parent abc781c9c0
commit 49973271e5
20 changed files with 1071 additions and 26 deletions
@@ -1,5 +1,7 @@
# Medication Correlation Design Decisions
**Status:** Implemented (Phase 15). Medication administration CRUD, `MedicationCorrelationHelper`, and integration with `WarningEvaluator` and `News2Detector` are in production. Verification: `./scripts/run-phase15-verification.sh` and `MedicationCorrelationTests`. The simulator scenario `VigilCare.Simulator/Scenarios/List/medication-false-alarm-01.json` exercises the end-to-end flow.
## The problem this solves
A patient with a blood pressure of 140/90 receives metoprolol (a beta-blocker that
@@ -12,7 +14,7 @@ Clinicians who see these false positives repeatedly stop trusting the alert syst
that point, the system is worse than useless — it trains people to ignore alerts,
including the real ones.
Phase 15 solves this by **annotating** alerts with medication context. The alert still
Phase 15 addresses this by **annotating** alerts with medication context. The alert still
fires (the BP is genuinely low and may need monitoring), but the details say:
> SYSTOLIC_BP value 95 is below warning low of 90. — note: metoprolol 25mg (PO)
@@ -25,7 +27,7 @@ medication is working — keep monitoring."
## How the pieces fit together
There are six components in Phase 15. Here is how a request flows through them, starting
There are six components. Here is how a request flows through them, starting
from when a nurse records a medication and ending when an annotated alert is created.
```
@@ -61,7 +63,7 @@ New observation arrives (e.g. SYSTOLIC_BP = 95)
│ 1. Load threshold │ (from Redis cache)
│ 2. Check breach │ (is 95 < warningLow of 90?)
│ 3. Build details │ ("SYSTOLIC_BP value 95 is below warning low of 90.")
│ 4. ► Annotate ◄ │ NEW in Phase 15
│ 4. ► Annotate ◄ │ MedicationCorrelationHelper
│ 5. INSERT alert │ (idempotent — skips if one already open)
└────────┬────────────┘
@@ -163,6 +165,10 @@ Drug names in clinical systems come in all forms — "Metoprolol", "METOPROLOL",
lookups, the correlation works regardless of how the nurse typed the drug name. This
avoids a class of bugs where correlation silently fails because the case doesn't match.
The shipped `appsettings.json` includes mappings for common cardiovascular, vasopressor,
opioid, sedative, diuretic, and antibiotic agents — not just metoprolol. Add or adjust
entries under `MedicationCorrelation:DrugVitalMappings` without a migration.
---
### 3. MedicationService
@@ -307,7 +313,7 @@ Both `WarningEvaluator` and `News2Detector` already follow a pattern:
2. Build a details string describing the breach
3. INSERT the alert into the database
Phase 15 adds one step between 2 and 3:
Phase 15 inserts one step between 2 and 3:
```
2. Build details string
@@ -315,7 +321,7 @@ Phase 15 adds one step between 2 and 3:
3. INSERT the alert (with the possibly-annotated details)
```
This minimal insertion point means no changes to the threshold logic, the idempotent
This insertion point required no changes to the threshold logic, the idempotent
INSERT pattern, the outbox event publishing, or the alert suppression logic. Each of
those systems continues to work exactly as before.
@@ -384,7 +390,7 @@ pharmaceutical review.
## Testing strategy
The tests are structured in three files, each targeting a different layer:
Twelve tests across three files cover the medication subsystem:
**MedicationServiceTests (4 tests)** — tests the service layer directly. Can a medication
be created on an active encounter? Does a discharged encounter get rejected? Does
@@ -393,14 +399,17 @@ pagination work? Does the time-window filter exclude old records? These tests ca
**MedicationCorrelationTests (5 tests)** — tests the full integration from medication
recording through alert creation. These seed a medication into the database, then invoke
`WarningEvaluator.EvaluateAsync` and check whether the resulting alert's `details` field
contains the medication annotation. This is the most important test file because it
verifies the end-to-end behavior that Phase 15 exists to provide.
`WarningEvaluator.EvaluateAsync` (and NEWS2 paths where applicable) and check whether the
resulting alert's `details` field contains the medication annotation. This is the most
important test file because it verifies the end-to-end behavior the feature exists to provide.
**MedicationValidationTests (3 tests)** — tests the HTTP validation layer. These send
invalid requests via `HttpClient` and assert 400 responses. They don't seed encounters
because the validator rejects the request before the service layer runs.
Run with `dotnet test --filter "FullyQualifiedName~Medication"` or
`./scripts/run-phase15-verification.sh` (requires API + Docker Compose).
All tests run against a real PostgreSQL database and real Redis instance (using test
containers on different ports). No mocking. This means the tests catch real issues like
SQL translation failures, index problems, and configuration registration mistakes that