chore: update docs
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
A production-quality clinical backend built with ASP.NET Core 8, PostgreSQL, Apache Kafka, RabbitMQ, Elasticsearch, Redis, and MinIO. The domain models the observe-alert-acknowledge lifecycle at the center of any clinical monitoring system: patient encounters, continuous vital sign and lab result ingest, real-time sepsis and NEWS2 scoring, and clinician notification with automatic escalation.
|
A production-quality clinical backend built with ASP.NET Core 8, PostgreSQL, Apache Kafka, RabbitMQ, Elasticsearch, Redis, and MinIO. The domain models the observe-alert-acknowledge lifecycle at the center of any clinical monitoring system: patient encounters, continuous vital sign and lab result ingest, real-time sepsis and NEWS2 scoring, and clinician notification with automatic escalation.
|
||||||
|
|
||||||
**Implementation status:** Thirty-one planned phases are complete through Phase 33 (plus Phases 20–23) — from schema and CRUD through Kafka, Elasticsearch CQRS, sepsis detection, RabbitMQ paging with DLQ escalation, reconciliation jobs, Prometheus/Grafana observability, the MinIO Parquet data lake, clinical data model expansion, warning alerts and orders, the NEWS2 composite scoring engine, trend detection with alert suppression, qSOFA bedside screening, medication administration with alert correlation annotations, the console replay simulator, the **Vue 3 ward dashboard**, clinician feedback mode, **Glasgow Coma Scale (GCS) scoring**, **SOFA organ-dysfunction scoring with baseline tracking and delta sepsis alerts**, the **Sepsis-3 clinical refactor** (SIRS removed, qSOFA repositioned as screening, SOFA delta ≥ 2 triggers bundles), **frontend GCS entry and SOFA display**, **expanded simulator scenarios with clinical validation**, the **Site & Gateway Registry** with dual authentication, shared clinical sync contracts, and fleet health Prometheus gauges, the **Ward Gateway Service** (local-first clinical path with offline buffering and central sync), the **FHIR R4 Inbound Facade** for EHR integration, **Role-Based Access Control (RBAC) with clinical audit logging**, the **Dashboard Gap Analysis Fixes** (SOFA/GCS/qSOFA history charts, patient banner, encounter timeline, medication markers on vital charts), the **Enhanced Dashboard** (department overview, sepsis bundle board, critical alert notifications, shift handoff reports, vitals entry form, sortable/filterable ward table), **Degraded Operations Visibility** (gateway fleet operations panel, stale gateway auto-detection, discharge summary API, admin panels for user/threshold/audit/reconciliation management, degraded-mode banner), and **Alert Quality Analytics** (server-side clinician feedback with `AlertFeedback` entity, `AlertQualityAggregatorService` background metrics, quality metrics API, Grafana alert quality dashboard). Post-phase hardening includes health check endpoints, Kafka poison pill protection, outbox dead-letter with retry tracking, data lake partial-commit safety, MRN sequence-based generation, FHIR bundle transaction rollback, **FHIR R4 read/search endpoints** (Patient and Encounter), **alert threshold deletion with audit trail**, **FHIR API key rotation** (constant-time multi-key validation), **authorization failure logging** with Prometheus metrics, **JWT signing key validation** at startup, and **concurrency hardening** (transactional sepsis bundle creation, unique active encounter constraint). See [Implemented Phases](#implemented-phases) for the full breakdown. Guides: [dashboard-guide.md](docs/dashboard-guide.md) (technical), [clinical-testing-guide.md](docs/clinical-testing-guide.md) (doctors & nurses).
|
**Implementation status:** Thirty-two planned phases are complete through Phase 34 (plus Phases 20–23) — from schema and CRUD through Kafka, Elasticsearch CQRS, sepsis detection, RabbitMQ paging with DLQ escalation, reconciliation jobs, Prometheus/Grafana observability, the MinIO Parquet data lake, clinical data model expansion, warning alerts and orders, the NEWS2 composite scoring engine, trend detection with alert suppression, qSOFA bedside screening, medication administration with alert correlation annotations, the console replay simulator, the **Vue 3 ward dashboard**, clinician feedback mode, **Glasgow Coma Scale (GCS) scoring**, **SOFA organ-dysfunction scoring with baseline tracking and delta sepsis alerts**, the **Sepsis-3 clinical refactor** (SIRS removed, qSOFA repositioned as screening, SOFA delta ≥ 2 triggers bundles), **frontend GCS entry and SOFA display**, **expanded simulator scenarios with clinical validation**, the **Site & Gateway Registry** with dual authentication, shared clinical sync contracts, and fleet health Prometheus gauges, the **Ward Gateway Service** (local-first clinical path with offline buffering and central sync), the **FHIR R4 Inbound Facade** for EHR integration, **Role-Based Access Control (RBAC) with clinical audit logging**, the **Dashboard Gap Analysis Fixes** (SOFA/GCS/qSOFA history charts, patient banner, encounter timeline, medication markers on vital charts), the **Enhanced Dashboard** (department overview, sepsis bundle board, critical alert notifications, shift handoff reports, vitals entry form, sortable/filterable ward table), **Degraded Operations Visibility** (gateway fleet operations panel, stale gateway auto-detection, discharge summary API, admin panels for user/threshold/audit/reconciliation management, degraded-mode banner), **Alert Quality Analytics** (server-side clinician feedback with `AlertFeedback` entity, `AlertQualityAggregatorService` background metrics, quality metrics API, Grafana alert quality dashboard), and **Explainable Alerts** (immutable JSONB `explanation` on composite alerts with score contributors, trend context, structured medication context, and bedside `NarrativeSummary`; `AlertResponse` DTO on GET/list/acknowledge/resolve; dashboard `AlertReasoning.vue`; ES indexer and data lake propagation; ward gateway sync). Post-phase hardening includes health check endpoints, Kafka poison pill protection, outbox dead-letter with retry tracking, data lake partial-commit safety, MRN sequence-based generation, FHIR bundle transaction rollback, **FHIR R4 read/search endpoints** (Patient and Encounter), **alert threshold deletion with audit trail**, **FHIR API key rotation** (constant-time multi-key validation), **authorization failure logging** with Prometheus metrics, **JWT signing key validation** at startup, and **concurrency hardening** (transactional sepsis bundle creation, unique active encounter constraint). See [Implemented Phases](#implemented-phases) for the full breakdown. Guides: [dashboard-guide.md](docs/dashboard-guide.md) (technical), [clinical-testing-guide.md](docs/clinical-testing-guide.md) (doctors & nurses).
|
||||||
|
|
||||||
## Domain Model — How It Maps to a Real Clinical System
|
## Domain Model — How It Maps to a Real Clinical System
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ An `idempotencyKey` (partial unique index) prevents duplicate observations when
|
|||||||
|
|
||||||
### ClinicalAlert
|
### ClinicalAlert
|
||||||
|
|
||||||
A `ClinicalAlert` is generated when an observation breaches a threshold, when the qSOFA engine detects two or more organ-dysfunction criteria (screening), when SOFA delta ≥ 2 from baseline confirms sepsis, or when the NEWS2 engine computes a medium/high-risk composite score (or a single-parameter score of 3). Lifecycle: `open → acknowledged → resolved` (or `escalated` after a five-minute NACK cycle through the RabbitMQ dead-letter queue). Alerts carry an audit trail: who acknowledged, when, and with what note. Only `SOFA_SEPSIS` alerts trigger automatic sepsis bundle creation.
|
A `ClinicalAlert` is generated when an observation breaches a threshold, when the qSOFA engine detects two or more organ-dysfunction criteria (screening), when SOFA delta ≥ 2 from baseline confirms sepsis, or when the NEWS2 engine computes a medium/high-risk composite score (or a single-parameter score of 3). Lifecycle: `open → acknowledged → resolved` (or `escalated` after a five-minute NACK cycle through the RabbitMQ dead-letter queue). Alerts carry an audit trail: who acknowledged, when, and with what note. Composite alerts from NEWS2, SOFA, GCS, and trend detection also carry an immutable JSONB `explanation` snapshot — score contributors, trend context, medication context, and a bedside narrative — frozen at alert creation time. The human-readable `details` string remains for backward compatibility. Only `SOFA_SEPSIS` alerts trigger automatic sepsis bundle creation.
|
||||||
|
|
||||||
### SepsisBundle
|
### SepsisBundle
|
||||||
|
|
||||||
@@ -70,9 +70,9 @@ An `OutboxEvent` is written in the same transaction as any observation or alert,
|
|||||||
- **Trend Detection Engine** — `TrendAnalyzerService` Kafka consumer (`trend-analyzer`) tracks rate-of-change for five vital parameters (`HEART_RATE`, `RESP_RATE`, `SYSTOLIC_BP`, `TEMP_C`, `SPO2`) using Redis sliding-window history; when velocity exceeds configured thresholds (e.g. 72→95 bpm in 30 min), creates a `RAPID_DETERIORATION` alert even if the current value is below warning thresholds; Prometheus `trend_alerts_total` and `trend_analysis_duration_seconds`
|
- **Trend Detection Engine** — `TrendAnalyzerService` Kafka consumer (`trend-analyzer`) tracks rate-of-change for five vital parameters (`HEART_RATE`, `RESP_RATE`, `SYSTOLIC_BP`, `TEMP_C`, `SPO2`) using Redis sliding-window history; when velocity exceeds configured thresholds (e.g. 72→95 bpm in 30 min), creates a `RAPID_DETERIORATION` alert even if the current value is below warning thresholds; Prometheus `trend_alerts_total` and `trend_analysis_duration_seconds`
|
||||||
- **Alert Suppression Windows** — acknowledging a suppressible alert (`WARNING_*`, `NEWS2_WARNING`) sets a Redis key `suppress:{encounterId}:{alertType}` with a configurable TTL (default 30 min from `AlertSuppression` config; optional per-code override via `alert_thresholds.suppression_window_minutes`); `WarningEvaluator` and `News2Detector` check suppression before creating new warning alerts; critical alerts (`CRITICAL_*`, `NEWS2_EMERGENCY`, `SEPSIS_WARNING`, `RAPID_DETERIORATION`) are never suppressed; observations and NEWS2 scores continue to persist during suppression; Prometheus `alert_suppressions_total`
|
- **Alert Suppression Windows** — acknowledging a suppressible alert (`WARNING_*`, `NEWS2_WARNING`) sets a Redis key `suppress:{encounterId}:{alertType}` with a configurable TTL (default 30 min from `AlertSuppression` config; optional per-code override via `alert_thresholds.suppression_window_minutes`); `WarningEvaluator` and `News2Detector` check suppression before creating new warning alerts; critical alerts (`CRITICAL_*`, `NEWS2_EMERGENCY`, `SEPSIS_WARNING`, `RAPID_DETERIORATION`) are never suppressed; observations and NEWS2 scores continue to persist during suppression; Prometheus `alert_suppressions_total`
|
||||||
- **Medication Administration** — `POST /encounters/:id/medications` records drug administrations (name, dose, route, timestamp, administered-by); `GET /encounters/:id/medications` lists with optional `since` filter; `GET /medications/:id` detail; active-encounter guard; FluentValidation on request DTOs
|
- **Medication Administration** — `POST /encounters/:id/medications` records drug administrations (name, dose, route, timestamp, administered-by); `GET /encounters/:id/medications` lists with optional `since` filter; `GET /medications/:id` detail; active-encounter guard; FluentValidation on request DTOs
|
||||||
- **Medication Correlation Annotations** — `MedicationCorrelationHelper` appends medication context to warning and NEWS2 alert details when a mapped drug was administered within the correlation window (default 90 min); drug-to-vital mappings in `MedicationCorrelation` config (`appsettings.json`); annotates rather than suppresses — alerts still fire; sepsis, trend, and critical sync-path alerts are never annotated; design rationale in `docs/decisions/medication-correlation-design.md`
|
- **Medication Correlation Annotations** — `MedicationCorrelationHelper` appends medication context to warning alert `details` when a mapped drug was administered within the correlation window (default 90 min); explainable alerts (NEWS2, SOFA, GCS, rapid deterioration) receive structured `MedicationContext` in the JSONB `explanation` via `TryGetContextAsync()`; drug-to-vital mappings in `MedicationCorrelation` config (`appsettings.json`); annotates rather than suppresses — alerts still fire; sepsis, trend, and critical sync-path alerts are never annotated; design rationale in `docs/decisions/medication-correlation-design.md`
|
||||||
- **Ward Dashboard APIs** — `GET /encounters` returns paginated `WardEncounterSummary` rows (patient name/MRN, room/bed, department, status, latest NEWS2 score, live qSOFA criteria count from Redis, sepsis bundle status, open alert count, SOFA score/delta, GCS score/classification, attending physician, admitted-at, last observation time); filterable by `status` and `department`; `GET /encounters/:id/qsofa/current` exposes Redis-backed qSOFA state; `GET /sepsis-bundles` lists bundles hospital-wide with optional `status` filter (returns `SepsisBundleSummary` with patient demographics, elements, and deadlines); CORS policy `Dashboard` allows configured origins (default `http://localhost:5173`)
|
- **Ward Dashboard APIs** — `GET /encounters` returns paginated `WardEncounterSummary` rows (patient name/MRN, room/bed, department, status, latest NEWS2 score, live qSOFA criteria count from Redis, sepsis bundle status, open alert count, SOFA score/delta, GCS score/classification, attending physician, admitted-at, last observation time); filterable by `status` and `department`; `GET /encounters/:id/qsofa/current` exposes Redis-backed qSOFA state; `GET /sepsis-bundles` lists bundles hospital-wide with optional `status` filter (returns `SepsisBundleSummary` with patient demographics, elements, and deadlines); CORS policy `Dashboard` allows configured origins (default `http://localhost:5173`)
|
||||||
- **Ward Dashboard Frontend** — Vue 3 SPA (`vigilcare-dashboard/`) with virtual ward table (multi-column sortable, patient search, quick-filters for critical/alerts/sepsis), patient detail (vitals, scores, alerts, orders, sepsis bundle, GCS entry form, SOFA score panel, patient banner with demographics/allergies/emergency contact, encounter timeline, vitals entry form for manual observation recording, discharge summary panel), alert center (global acknowledge/resolve with role-aware modal and acknowledgment note preview), department overview (unit-level snapshot cards with acuity bars, patient/alert/bundle counts per department), sepsis bundle board (real-time countdown timers, on-track/at-risk/overdue urgency sorting), critical alert banner with browser notifications and audible tone, shift handoff report generator (SBAR format with ward summary, exportable via print/PDF), vital sign trend charts with medication administration markers and local replay scrubbing, NEWS2 history chart, SOFA history chart with organ-system breakdown, GCS history chart with component tracking, qSOFA evaluation history, alert reasoning with optional medication context, clinician feedback on every alert, admin panels (threshold management, user management, audit log viewer, reconciliation viewer), gateway operations dashboard with degraded-mode banner, and alert quality analytics with quality charts; role-aware sidebar navigation; polls API every 5–10 s; guides in `docs/dashboard-guide.md` and `docs/clinical-testing-guide.md`
|
- **Ward Dashboard Frontend** — Vue 3 SPA (`vigilcare-dashboard/`) with virtual ward table (multi-column sortable, patient search, quick-filters for critical/alerts/sepsis), patient detail (vitals, scores, alerts, orders, sepsis bundle, GCS entry form, SOFA score panel, patient banner with demographics/allergies/emergency contact, encounter timeline, vitals entry form for manual observation recording, discharge summary panel), alert center (global acknowledge/resolve with role-aware modal and acknowledgment note preview), department overview (unit-level snapshot cards with acuity bars, patient/alert/bundle counts per department), sepsis bundle board (real-time countdown timers, on-track/at-risk/overdue urgency sorting), critical alert banner with browser notifications and audible tone, shift handoff report generator (SBAR format with ward summary, exportable via print/PDF), vital sign trend charts with medication administration markers and local replay scrubbing, NEWS2 history chart, SOFA history chart with organ-system breakdown, GCS history chart with component tracking, qSOFA evaluation history, structured alert reasoning (`AlertReasoning.vue` — score contributors, trend context, medication context, narrative summary from `explanation`), clinician feedback on every alert, admin panels (threshold management, user management, audit log viewer, reconciliation viewer), gateway operations dashboard with degraded-mode banner, and alert quality analytics with quality charts; role-aware sidebar navigation; polls API every 5–10 s; guides in `docs/dashboard-guide.md` and `docs/clinical-testing-guide.md`
|
||||||
- **FHIR R4 Inbound Facade** — `POST /fhir/R4/{Patient,Encounter,Observation,MedicationAdministration}` accepts FHIR R4 JSON resources (`application/fhir+json`); `POST /fhir/R4` processes transaction Bundles (Patient → Encounter → Observation in dependency order); `GET /fhir/R4/metadata` returns a CapabilityStatement; LOINC-to-internal code mapping (19 observation codes + SNOMED CT fallbacks); Fahrenheit-to-Celsius unit conversion; `ExternalResourceIdentifier` table links hospital MRNs and visit numbers to internal UUIDs for idempotent upserts; `FhirApiKeyOrJwtMiddleware` authenticates via JWT bearer or `X-Api-Key` header (supports multiple keys via `Fhir:ApiKeys` array for zero-downtime rotation; constant-time comparison via `CryptographicOperations.FixedTimeEquals`); `FhirExceptionFilter` returns FHIR `OperationOutcome` on errors; configurable identifier systems, department codes, and encounter class mappings via `Fhir` config section; Prometheus `fhir_ingest_total` and `fhir_mapping_errors_total`; integration guide for Mirth Connect HL7v2→FHIR channels in `docs/integration/mirth-fhir-channels.md`
|
- **FHIR R4 Inbound Facade** — `POST /fhir/R4/{Patient,Encounter,Observation,MedicationAdministration}` accepts FHIR R4 JSON resources (`application/fhir+json`); `POST /fhir/R4` processes transaction Bundles (Patient → Encounter → Observation in dependency order); `GET /fhir/R4/metadata` returns a CapabilityStatement; LOINC-to-internal code mapping (19 observation codes + SNOMED CT fallbacks); Fahrenheit-to-Celsius unit conversion; `ExternalResourceIdentifier` table links hospital MRNs and visit numbers to internal UUIDs for idempotent upserts; `FhirApiKeyOrJwtMiddleware` authenticates via JWT bearer or `X-Api-Key` header (supports multiple keys via `Fhir:ApiKeys` array for zero-downtime rotation; constant-time comparison via `CryptographicOperations.FixedTimeEquals`); `FhirExceptionFilter` returns FHIR `OperationOutcome` on errors; configurable identifier systems, department codes, and encounter class mappings via `Fhir` config section; Prometheus `fhir_ingest_total` and `fhir_mapping_errors_total`; integration guide for Mirth Connect HL7v2→FHIR channels in `docs/integration/mirth-fhir-channels.md`
|
||||||
- **FHIR R4 Read/Search** — `GET /fhir/R4/Patient/{id}` reads a Patient by internal ID; `GET /fhir/R4/Patient` searches by `identifier` (system|value) or lists all patients; `GET /fhir/R4/Encounter/{id}` reads an Encounter by internal ID; `GET /fhir/R4/Encounter` searches by `patient` (UUID) and/or `status` (`in-progress`, `finished`, `cancelled`); all return FHIR R4 JSON (`application/fhir+json`); search endpoints return `Bundle.type=searchset`; requires `fhir:read` permission (Admin and Integration roles); internal resources mapped back to FHIR via `PatientFhirMapper.ToFhirResponse` / `EncounterFhirMapper.ToFhirResponse` with hospital identifier resolution; Prometheus `fhir_read_total` counter with `resource_type`, `interaction`, `outcome` labels
|
- **FHIR R4 Read/Search** — `GET /fhir/R4/Patient/{id}` reads a Patient by internal ID; `GET /fhir/R4/Patient` searches by `identifier` (system|value) or lists all patients; `GET /fhir/R4/Encounter/{id}` reads an Encounter by internal ID; `GET /fhir/R4/Encounter` searches by `patient` (UUID) and/or `status` (`in-progress`, `finished`, `cancelled`); all return FHIR R4 JSON (`application/fhir+json`); search endpoints return `Bundle.type=searchset`; requires `fhir:read` permission (Admin and Integration roles); internal resources mapped back to FHIR via `PatientFhirMapper.ToFhirResponse` / `EncounterFhirMapper.ToFhirResponse` with hospital identifier resolution; Prometheus `fhir_read_total` counter with `resource_type`, `interaction`, `outcome` labels
|
||||||
- **Role-Based Access Control (RBAC)** — JWT bearer authentication (`POST /auth/login`); four clinical roles (`Nurse`, `Physician`, `Admin`, `Integration`) with 18 granular permissions (`patients:read`, `alerts:acknowledge`, `alerts:feedback`, `thresholds:write`, `fhir:ingest`, `fhir:read`, `audit:read`, etc.); `AuthorizePermission` attribute on every controller action; `PermissionAuthorizationHandler` resolves role → permission at runtime from `ClinicalRolePermissionMap` and logs authorization failures with structured details (user, role, permission, endpoint) plus `authorization_failures_total` Prometheus counter; `CurrentUserService` exposes authenticated identity (user ID, display name, role, IP address) to services; nurses and physicians get clinical read/write permissions; admins additionally get `thresholds:write`, `fhir:read`, `audit:read`, and `users:admin`; integration accounts get FHIR ingest and read access; FHIR endpoints accept both JWT and `X-Api-Key` authentication via `FhirApiKeyOrJwtMiddleware`; alert `acknowledgedBy` is set from the authenticated user identity, not the request body; startup validates JWT signing key is at least 256 bits (HMAC-SHA256 minimum); four seeded demo users (`nurse.demo`, `physician.demo`, `admin.demo`, `integration.mirth`); frontend login page with `localStorage` token persistence and automatic `Authorization: Bearer` header injection
|
- **Role-Based Access Control (RBAC)** — JWT bearer authentication (`POST /auth/login`); four clinical roles (`Nurse`, `Physician`, `Admin`, `Integration`) with 18 granular permissions (`patients:read`, `alerts:acknowledge`, `alerts:feedback`, `thresholds:write`, `fhir:ingest`, `fhir:read`, `audit:read`, etc.); `AuthorizePermission` attribute on every controller action; `PermissionAuthorizationHandler` resolves role → permission at runtime from `ClinicalRolePermissionMap` and logs authorization failures with structured details (user, role, permission, endpoint) plus `authorization_failures_total` Prometheus counter; `CurrentUserService` exposes authenticated identity (user ID, display name, role, IP address) to services; nurses and physicians get clinical read/write permissions; admins additionally get `thresholds:write`, `fhir:read`, `audit:read`, and `users:admin`; integration accounts get FHIR ingest and read access; FHIR endpoints accept both JWT and `X-Api-Key` authentication via `FhirApiKeyOrJwtMiddleware`; alert `acknowledgedBy` is set from the authenticated user identity, not the request body; startup validates JWT signing key is at least 256 bits (HMAC-SHA256 minimum); four seeded demo users (`nurse.demo`, `physician.demo`, `admin.demo`, `integration.mirth`); frontend login page with `localStorage` token persistence and automatic `Authorization: Bearer` header injection
|
||||||
@@ -88,10 +88,11 @@ An `OutboxEvent` is written in the same transaction as any observation or alert,
|
|||||||
- **FHIR Bundle Transaction Rollback** — `FhirBundleProcessor` wraps all bundle entry processing in a database transaction; on any entry failure, the transaction is rolled back and the response includes the `OperationOutcome` for the failed entry; prevents partial state from orphaned Patient/Encounter records
|
- **FHIR Bundle Transaction Rollback** — `FhirBundleProcessor` wraps all bundle entry processing in a database transaction; on any entry failure, the transaction is rolled back and the response includes the `OperationOutcome` for the failed entry; prevents partial state from orphaned Patient/Encounter records
|
||||||
- **Clinician Feedback Mode** — six quick ratings per alert (useful, too early, too late, false positive, missing context, would act); optional notes; server-side `AlertFeedback` entity persisted per user per alert (`POST /alerts/{id}/feedback`); `alerts:feedback` permission for Nurse, Physician, and Admin roles; Feedback Summary with aggregate stats and JSON/CSV export; client-side persistence for product research
|
- **Clinician Feedback Mode** — six quick ratings per alert (useful, too early, too late, false positive, missing context, would act); optional notes; server-side `AlertFeedback` entity persisted per user per alert (`POST /alerts/{id}/feedback`); `alerts:feedback` permission for Nurse, Physician, and Admin roles; Feedback Summary with aggregate stats and JSON/CSV export; client-side persistence for product research
|
||||||
- **Alert Quality Analytics** — `AlertQualityAggregatorService` periodically computes per-alert-type quality metrics (acknowledgement rate, false positive rate, useful rate, would-act rate, avg seconds to acknowledge/resolve) into `alert_quality_metrics` table; `AlertQualityMetricsController` exposes `GET /alerts/quality-metrics` (time-range filterable, optional alert type) and `GET /alerts/quality-metrics/summary`; Grafana alert quality dashboard (`infra/grafana/dashboards/alert-quality-dashboard.json`); frontend `AlertQualityAnalytics.vue` with `AlertQualityChart.vue`; Prometheus `alert_quality_useful_rate` and `alert_quality_false_positive_rate` gauges
|
- **Alert Quality Analytics** — `AlertQualityAggregatorService` periodically computes per-alert-type quality metrics (acknowledgement rate, false positive rate, useful rate, would-act rate, avg seconds to acknowledge/resolve) into `alert_quality_metrics` table; `AlertQualityMetricsController` exposes `GET /alerts/quality-metrics` (time-range filterable, optional alert type) and `GET /alerts/quality-metrics/summary`; Grafana alert quality dashboard (`infra/grafana/dashboards/alert-quality-dashboard.json`); frontend `AlertQualityAnalytics.vue` with `AlertQualityChart.vue`; Prometheus `alert_quality_useful_rate` and `alert_quality_false_positive_rate` gauges
|
||||||
|
- **Explainable Alerts** — `AlertExplanation` value object (`ScoreContributor`, `TrendContext`, `MedicationContext`, `NarrativeSummary`) serialized as JSONB on `ClinicalAlert.Explanation` at creation time; `AlertExplanationBuilder` and contributor builders (NEWS2, SOFA, GCS, trend) assemble explanation from scoring outputs; `ClinicalAlertFactory` idempotent INSERT with explanation; NEWS2, SOFA, GCS, and `TrendDetector` wire explanation and include `explanation` in `alert.generated` outbox payloads; `AlertResponse` DTO exposes optional `Explanation` on GET/list/acknowledge/resolve; Elasticsearch indexes `NarrativeSummary`; data lake Parquet includes `explanation_json`; ward gateway `LocalClinicalAlert.ExplanationJson` synced via `ClinicalSyncBatchProcessor`; dashboard `AlertReasoning.vue` + `alertExplanation.js` composable render structured reasoning; simulator `ExpectedOutcomeValidator` supports `narrativeContains` on key scenarios; `ExplainableAlertsTests` (10 tests) + `run-phase34-verification.sh`
|
||||||
- **Degraded Operations Visibility** — `GatewayStaleDetectorService` auto-marks gateways OFFLINE when heartbeat exceeds configurable `StaleThresholdMinutes`; `OperationsController` (`GET /operations/gateways`, `GET /operations/gateways/{id}`, `GET /operations/sites/{siteId}/summary`) provides fleet management API; `DischargeSummaryService` with `GET /encounters/{id}/discharge-summary` (info) and `GET /encounters/{id}/discharge-summary/content` (MinIO PDF download); `DischargeSummaryPanel.vue` on patient detail; `DegradedModeBanner.vue` warns when gateways are offline; `GatewayOperations.vue` operations dashboard
|
- **Degraded Operations Visibility** — `GatewayStaleDetectorService` auto-marks gateways OFFLINE when heartbeat exceeds configurable `StaleThresholdMinutes`; `OperationsController` (`GET /operations/gateways`, `GET /operations/gateways/{id}`, `GET /operations/sites/{siteId}/summary`) provides fleet management API; `DischargeSummaryService` with `GET /encounters/{id}/discharge-summary` (info) and `GET /encounters/{id}/discharge-summary/content` (MinIO PDF download); `DischargeSummaryPanel.vue` on patient detail; `DegradedModeBanner.vue` warns when gateways are offline; `GatewayOperations.vue` operations dashboard
|
||||||
- **User Management** — `UsersController` (`GET /users`, `POST /users`, `PATCH /users/{id}`) for admin user account CRUD; `UserService` with BCrypt password hashing; `UserManagementView.vue` with `UserFormModal.vue` (create/edit users, role assignment, active toggle)
|
- **User Management** — `UsersController` (`GET /users`, `POST /users`, `PATCH /users/{id}`) for admin user account CRUD; `UserService` with BCrypt password hashing; `UserManagementView.vue` with `UserFormModal.vue` (create/edit users, role assignment, active toggle)
|
||||||
- **Admin Dashboard Panels** — `ThresholdManagementView.vue` with `ThresholdFormModal.vue` (create/edit alert thresholds); `AuditLogView.vue` (filterable audit log viewer with action/entity/user/date filters); `ReconciliationView.vue` (safety finding viewer); sidebar navigation with role-aware admin section; `CollapsibleSection.vue` and `SeverityBadge.vue` UI components
|
- **Admin Dashboard Panels** — `ThresholdManagementView.vue` with `ThresholdFormModal.vue` (create/edit alert thresholds); `AuditLogView.vue` (filterable audit log viewer with action/entity/user/date filters); `ReconciliationView.vue` (safety finding viewer); sidebar navigation with role-aware admin section; `CollapsibleSection.vue` and `SeverityBadge.vue` UI components
|
||||||
- **Console Replay Simulator** — standalone `VigilCare.Simulator` .NET console app replays JSON scenario files against the live API with configurable speed (`--speed 0` instant, `60` = 60× faster); commands: `replay`, `replay-all`, `validate`, `dry-run`; optional `--poll` shows alerts, NEWS2, GCS, SOFA, and sepsis bundle state during replay; `--gateway` targets the ward gateway (`http://localhost:5081`) with `--encounter-id`, `--skip-setup`, and `--gateway-token`; `alert_ack` events poll for open alerts on central before acknowledging (handles async alert pipeline at `--speed 0`); twelve sample scenarios in `VigilCare.Simulator/Scenarios/List/` (including ward outage reconnect, GCS neurological decline, SOFA sepsis progression, and SpO₂/FiO₂ fallback); user guide in `docs/simulator-guide.md`
|
- **Console Replay Simulator** — standalone `VigilCare.Simulator` .NET console app replays JSON scenario files against the live API with configurable speed (`--speed 0` instant, `60` = 60× faster); commands: `replay`, `replay-all`, `validate`, `dry-run`; optional `--poll` shows alerts, NEWS2, GCS, SOFA, and sepsis bundle state during replay; `--gateway` targets the ward gateway (`http://localhost:5081`) with `--encounter-id`, `--skip-setup`, and `--gateway-token`; `alert_ack` events poll for open alerts on central before acknowledging (handles async alert pipeline at `--speed 0`); `ExpectedOutcomeValidator` validates alert `narrativeContains` on key scenarios; twelve sample scenarios in `VigilCare.Simulator/Scenarios/List/` (including ward outage reconnect, GCS neurological decline, SOFA sepsis progression, and SpO₂/FiO₂ fallback); user guide in `docs/simulator-guide.md`
|
||||||
- **RabbitMQ Notification Workers** — `NotificationPublisherService` reads `alert.generated` from Kafka and publishes paging jobs to `alerts.paging.queue`; `PagingWorkerService` sends the page and waits for acknowledgment; if no ack arrives before timeout it NACKs to `alerts.paging.dlq` with `x-message-ttl = 300000ms`; if the host is stopping, in-flight paging messages are NACKed with `requeue=true` so they are retried after restart and do not false-escalate; `EscalationWorkerService` pages the on-call backup and sets alert status to `escalated`; `DischargeSummaryWorkerService` reads `encounter.status.changed`, generates a discharge summary, and stores it in MinIO under `/discharge-summaries/{encounterId}/summary.pdf`
|
- **RabbitMQ Notification Workers** — `NotificationPublisherService` reads `alert.generated` from Kafka and publishes paging jobs to `alerts.paging.queue`; `PagingWorkerService` sends the page and waits for acknowledgment; if no ack arrives before timeout it NACKs to `alerts.paging.dlq` with `x-message-ttl = 300000ms`; if the host is stopping, in-flight paging messages are NACKed with `requeue=true` so they are retried after restart and do not false-escalate; `EscalationWorkerService` pages the on-call backup and sets alert status to `escalated`; `DischargeSummaryWorkerService` reads `encounter.status.changed`, generates a discharge summary, and stores it in MinIO under `/discharge-summaries/{encounterId}/summary.pdf`
|
||||||
- **Data Lake Writer** — `DataLakeWriterService` (consumer group `data-lake-writer`) buffers `observation.recorded`, `alert.generated`, and `encounter.status.changed` events, flushes date-partitioned Parquet files to MinIO (`/observations/`, `/alerts/`, `/encounters/`), and commits Kafka offsets only for topic-partitions where all uploads succeeded; failed partition buffers are retained in memory and retried on the next flush cycle (prevents data loss from partial upload failures); shutdown flush uses an uncanceled token so MinIO writes complete on Ctrl+C; `kafka_partition` and `kafka_offset` columns provide audit lineage
|
- **Data Lake Writer** — `DataLakeWriterService` (consumer group `data-lake-writer`) buffers `observation.recorded`, `alert.generated`, and `encounter.status.changed` events, flushes date-partitioned Parquet files to MinIO (`/observations/`, `/alerts/`, `/encounters/`), and commits Kafka offsets only for topic-partitions where all uploads succeeded; failed partition buffers are retained in memory and retried on the next flush cycle (prevents data loss from partial upload failures); shutdown flush uses an uncanceled token so MinIO writes complete on Ctrl+C; `kafka_partition` and `kafka_offset` columns provide audit lineage
|
||||||
- **Reconciliation Jobs** — three scheduled checks: (1) unacknowledged CRITICAL alerts older than 30 minutes, (2) pending orders without results after 4 hours, (3) active inpatients with no observation in 2 hours; each finding creates a `reconciliation_alerts` row and publishes to RabbitMQ
|
- **Reconciliation Jobs** — three scheduled checks: (1) unacknowledged CRITICAL alerts older than 30 minutes, (2) pending orders without results after 4 hours, (3) active inpatients with no observation in 2 hours; each finding creates a `reconciliation_alerts` row and publishes to RabbitMQ
|
||||||
@@ -210,7 +211,7 @@ VigilCareClinicalAPI/
|
|||||||
│ ├── QsofaController.cs # Current qSOFA criteria count (Redis-backed) + cursor-paginated evaluation history
|
│ ├── QsofaController.cs # Current qSOFA criteria count (Redis-backed) + cursor-paginated evaluation history
|
||||||
│ ├── ObservationsController.cs # Ingest POST, cursor-paginated GET
|
│ ├── ObservationsController.cs # Ingest POST, cursor-paginated GET
|
||||||
│ ├── AlertThresholdsController.cs # Threshold CRUD + cache invalidation
|
│ ├── AlertThresholdsController.cs # Threshold CRUD + cache invalidation
|
||||||
│ ├── AlertsController.cs # Alert list (global + per-encounter), acknowledge, resolve
|
│ ├── AlertsController.cs # Alert list (global + per-encounter), get by ID, acknowledge, resolve → AlertResponse
|
||||||
│ ├── OrdersController.cs # Order create, list, get, status transition, record result
|
│ ├── OrdersController.cs # Order create, list, get, status transition, record result
|
||||||
│ ├── News2Controller.cs # Current NEWS2 score and cursor-paginated history
|
│ ├── News2Controller.cs # Current NEWS2 score and cursor-paginated history
|
||||||
│ ├── GcsController.cs # Latest GCS score and cursor-paginated history per encounter
|
│ ├── GcsController.cs # Latest GCS score and cursor-paginated history per encounter
|
||||||
@@ -231,7 +232,7 @@ VigilCareClinicalAPI/
|
|||||||
│ │ ├── Encounter.cs # Status machine; SetStatus() enforces transition matrix
|
│ │ ├── Encounter.cs # Status machine; SetStatus() enforces transition matrix
|
||||||
│ │ ├── AlertThreshold.cs
|
│ │ ├── AlertThreshold.cs
|
||||||
│ │ ├── Observation.cs # Append-only; IdempotencyKey; partial unique index
|
│ │ ├── Observation.cs # Append-only; IdempotencyKey; partial unique index
|
||||||
│ │ ├── ClinicalAlert.cs # open → acknowledged → resolved / escalated
|
│ │ ├── ClinicalAlert.cs # open → acknowledged → resolved / escalated; JSONB Explanation snapshot
|
||||||
│ │ ├── Order.cs
|
│ │ ├── Order.cs
|
||||||
│ │ ├── News2Score.cs # Composite score with seven component scores + risk level
|
│ │ ├── News2Score.cs # Composite score with seven component scores + risk level
|
||||||
│ │ ├── GcsScore.cs # Eye/verbal/motor components, total, classification
|
│ │ ├── GcsScore.cs # Eye/verbal/motor components, total, classification
|
||||||
@@ -249,6 +250,8 @@ VigilCareClinicalAPI/
|
|||||||
│ │ ├── ClinicalAuditLog.cs # Append-only audit trail: action, entity, user, before/after JSONB, IP, correlation ID
|
│ │ ├── ClinicalAuditLog.cs # Append-only audit trail: action, entity, user, before/after JSONB, IP, correlation ID
|
||||||
│ │ ├── AlertFeedback.cs # Clinician feedback per alert (one per user per alert)
|
│ │ ├── AlertFeedback.cs # Clinician feedback per alert (one per user per alert)
|
||||||
│ │ └── AlertQualityMetric.cs # Per-alert-type quality metric snapshots (acknowledgement/false-positive/useful rates)
|
│ │ └── AlertQualityMetric.cs # Per-alert-type quality metric snapshots (acknowledgement/false-positive/useful rates)
|
||||||
|
│ ├── ValueObjects/
|
||||||
|
│ │ └── AlertExplanation.cs # ScoreContributor, TrendContext, MedicationContext, NarrativeSummary
|
||||||
│ └── Enums/
|
│ └── Enums/
|
||||||
│ ├── EncounterStatus.cs # Scheduled, Active, Discharged, Cancelled
|
│ ├── EncounterStatus.cs # Scheduled, Active, Discharged, Cancelled
|
||||||
│ ├── ClinicalRole.cs # Nurse, Physician, Admin, Integration
|
│ ├── ClinicalRole.cs # Nurse, Physician, Admin, Integration
|
||||||
@@ -305,7 +308,7 @@ VigilCareClinicalAPI/
|
|||||||
│ ├── AlertThresholdService.cs # CRUD + Redis write-through invalidation
|
│ ├── AlertThresholdService.cs # CRUD + Redis write-through invalidation
|
||||||
│ ├── ObservationService.cs # Ingest transaction: idempotency → plausibility → threshold → alert → outbox; emits Prometheus counters
|
│ ├── ObservationService.cs # Ingest transaction: idempotency → plausibility → threshold → alert → outbox; emits Prometheus counters
|
||||||
│ ├── ObservationQueryService.cs # Cursor-paginated history
|
│ ├── ObservationQueryService.cs # Cursor-paginated history
|
||||||
│ ├── AlertService.cs # Acknowledge (sets suppression), resolve, list
|
│ ├── AlertService.cs # Acknowledge (sets suppression), resolve, list → AlertResponse with optional Explanation
|
||||||
│ ├── AlertSuppressionService.cs # Redis suppress:{enc}:{type} TTL keys
|
│ ├── AlertSuppressionService.cs # Redis suppress:{enc}:{type} TTL keys
|
||||||
│ ├── OrderService.cs # Order lifecycle; status machine; calls SepsisBundleService.OnOrderResultedAsync on result
|
│ ├── OrderService.cs # Order lifecycle; status machine; calls SepsisBundleService.OnOrderResultedAsync on result
|
||||||
│ ├── News2Service.cs # Current score + cursor-paginated history from PostgreSQL
|
│ ├── News2Service.cs # Current score + cursor-paginated history from PostgreSQL
|
||||||
@@ -324,11 +327,14 @@ VigilCareClinicalAPI/
|
|||||||
│ ├── WarningEvaluator.cs # Warning-range evaluation; suppression + medication annotation; idempotent INSERT
|
│ ├── WarningEvaluator.cs # Warning-range evaluation; suppression + medication annotation; idempotent INSERT
|
||||||
│ ├── AnalyticsService.cs # Elasticsearch query wrappers
|
│ ├── AnalyticsService.cs # Elasticsearch query wrappers
|
||||||
│ └── PlausibilityValidator.cs # Per-code numeric range guard
|
│ └── PlausibilityValidator.cs # Per-code numeric range guard
|
||||||
|
├── Alerts/
|
||||||
|
│ ├── ClinicalAlertFactory.cs # Idempotent alert INSERT with explanation JSON; outbox payload serialization
|
||||||
|
│ └── AlertExplanationBuilder.cs # Assembles explanation from contributors, trend, medication context
|
||||||
├── Trend/
|
├── Trend/
|
||||||
│ ├── TrendCalculator.cs # Pure static rate-of-change logic
|
│ ├── TrendCalculator.cs # Pure static rate-of-change logic
|
||||||
│ └── TrendDetector.cs # Redis history + RAPID_DETERIORATION alert creation
|
│ └── TrendDetector.cs # Redis history + RAPID_DETERIORATION alert creation
|
||||||
├── Medication/
|
├── Medication/
|
||||||
│ └── MedicationCorrelationHelper.cs # Appends drug context to warning/NEWS2 alert details
|
│ └── MedicationCorrelationHelper.cs # String annotation on warning details; structured MedicationContext for explanations
|
||||||
├── Validators/ # FluentValidation — RegisterPatient, OpenEncounter, IngestObservation, CreateMedicationAdministration, CreateSiteRequest, RegisterGatewayRequest, GatewayHeartbeatRequest, …
|
├── Validators/ # FluentValidation — RegisterPatient, OpenEncounter, IngestObservation, CreateMedicationAdministration, CreateSiteRequest, RegisterGatewayRequest, GatewayHeartbeatRequest, …
|
||||||
├── Observability/
|
├── Observability/
|
||||||
│ └── Metrics/
|
│ └── Metrics/
|
||||||
@@ -485,7 +491,8 @@ tests/
|
|||||||
├── OperationsApiTests.cs # Operations fleet listing, gateway detail, site summary
|
├── OperationsApiTests.cs # Operations fleet listing, gateway detail, site summary
|
||||||
├── Helpers/GatewayAuthHelper.cs # WithGatewayApiKey extension method for test clients
|
├── Helpers/GatewayAuthHelper.cs # WithGatewayApiKey extension method for test clients
|
||||||
├── Alerts/
|
├── Alerts/
|
||||||
│ └── AlertQualityAnalyticsTests.cs # Alert feedback submission, quality aggregation, metrics API
|
│ ├── AlertQualityAnalyticsTests.cs # Alert feedback submission, quality aggregation, metrics API
|
||||||
|
│ └── ExplainableAlertsTests.cs # Explanation JSONB, AlertResponse mapping, medication context, legacy null
|
||||||
├── Auth/
|
├── Auth/
|
||||||
│ └── RbacTests.cs # RBAC — unauthenticated 401, nurse 403 on threshold write, admin audit log creation
|
│ └── RbacTests.cs # RBAC — unauthenticated 401, nurse 403 on threshold write, admin audit log creation
|
||||||
└── Fhir/
|
└── Fhir/
|
||||||
@@ -562,22 +569,24 @@ VigilCare.WardGateway.Tests/ # Phase 21 — ward gateway i
|
|||||||
├── WardGatewayLocalPathTests.cs # Local observation ingest, warning alerts, buffered sync items
|
├── WardGatewayLocalPathTests.cs # Local observation ingest, warning alerts, buffered sync items
|
||||||
└── WardGatewayPartitionTests.cs # Network partition simulation — offline buffering and sync upload
|
└── WardGatewayPartitionTests.cs # Network partition simulation — offline buffering and sync upload
|
||||||
|
|
||||||
VigilCare.Simulator/ # Phase 16 — console replay simulator (HTTP-only, no direct DB/Kafka)
|
VigilCare.Simulator/ # Phase 16, 29, 34 — console replay simulator (HTTP-only, no direct DB/Kafka)
|
||||||
├── Program.cs # CLI: replay, replay-all, validate, dry-run
|
├── Program.cs # CLI: replay, replay-all, validate, dry-run
|
||||||
├── Commands/ # System.CommandLine command handlers
|
├── Commands/ # System.CommandLine command handlers
|
||||||
├── Client/VigilCareApiClient.cs # Typed HTTP client for all API endpoints (incl. GCS, SOFA)
|
├── Client/
|
||||||
|
│ ├── VigilCareApiClient.cs # Typed HTTP client for all API endpoints (incl. GCS, SOFA)
|
||||||
|
│ └── Models/AlertExplanation.cs # Explanation DTO for poll/validation
|
||||||
├── Engine/ReplayEngine.cs # Scenario replay with speed multiplier + event logging
|
├── Engine/ReplayEngine.cs # Scenario replay with speed multiplier + event logging
|
||||||
├── Output/SimulatorConsole.cs # Colored output with GCS/SOFA score display
|
├── Output/SimulatorConsole.cs # Colored output with GCS/SOFA score + explanation display
|
||||||
├── Polling/ApiPoller.cs # Optional post-event alert/score/bundle/GCS/SOFA polling
|
├── Polling/ApiPoller.cs # Optional post-event alert/score/bundle/GCS/SOFA polling
|
||||||
├── Scenarios/ # schema.json, ScenarioLoader, ScenarioValidator
|
├── Scenarios/ # schema.json, ScenarioLoader, ScenarioValidator, ExpectedOutcomeValidator
|
||||||
└── Scenarios/List/ # Twelve sample scenarios (sepsis, GCS, SOFA, NEWS2, stable, ward outage, …)
|
└── Scenarios/List/ # Twelve sample scenarios (sepsis, GCS, SOFA, NEWS2, stable, ward outage, …)
|
||||||
|
|
||||||
vigilcare-dashboard/ # Phases 17–19, 22, 23, 27–28, 31, 33 — Vue 3 ward dashboard SPA
|
vigilcare-dashboard/ # Phases 17–19, 22, 23, 27–28, 31, 33–34 — Vue 3 ward dashboard SPA
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── api/ # HTTP client (auto Bearer header), encounters, clinical (GCS, SOFA, qSOFA history), alerts, analytics, sepsis, thresholds, users, audit, reconciliation, operations, alertQuality, normalize
|
│ ├── api/ # HTTP client (auto Bearer header), encounters, clinical (GCS, SOFA, qSOFA history), alerts, analytics, sepsis, thresholds, users, audit, reconciliation, operations, alertQuality, normalize
|
||||||
│ ├── components/
|
│ ├── components/
|
||||||
│ │ ├── admin/ # ThresholdFormModal, UserFormModal (admin CRUD modals)
|
│ │ ├── admin/ # ThresholdFormModal, UserFormModal (admin CRUD modals)
|
||||||
│ │ ├── alerts/ # AlertCard, AcknowledgeModal (role-aware), CriticalAlertBanner (browser notifications + audible tone)
|
│ │ ├── alerts/ # AlertCard, AlertReasoning (structured explanation), AcknowledgeModal (role-aware), CriticalAlertBanner (browser notifications + audible tone)
|
||||||
│ │ ├── charts/ # SofaHistory, GcsHistory, QsofaHistory, VitalChart with medication markers, AlertQualityChart
|
│ │ ├── charts/ # SofaHistory, GcsHistory, QsofaHistory, VitalChart with medication markers, AlertQualityChart
|
||||||
│ │ ├── departments/ # DepartmentCard, AcuityBar (unit-level snapshot)
|
│ │ ├── departments/ # DepartmentCard, AcuityBar (unit-level snapshot)
|
||||||
│ │ ├── feedback/ # FeedbackButtons, FeedbackSummary
|
│ │ ├── feedback/ # FeedbackButtons, FeedbackSummary
|
||||||
@@ -587,7 +596,7 @@ vigilcare-dashboard/ # Phases 17–19, 22, 23, 27
|
|||||||
│ │ ├── sepsis/ # SepsisBundleTable, SepsisBundleRow, SepsisBundleCard (countdown timer)
|
│ │ ├── sepsis/ # SepsisBundleTable, SepsisBundleRow, SepsisBundleCard (countdown timer)
|
||||||
│ │ ├── ward/ # WardTable (sortable headers), PatientRow, PatientCard, WardToolbar, SortableHeader, HandoffReport (SBAR + print)
|
│ │ ├── ward/ # WardTable (sortable headers), PatientRow, PatientCard, WardToolbar, SortableHeader, HandoffReport (SBAR + print)
|
||||||
│ │ └── ui/ # Button, Card, Badge, Skeleton, EmptyState, Modal, CollapsibleSection, SeverityBadge, DegradedModeBanner
|
│ │ └── ui/ # Button, Card, Badge, Skeleton, EmptyState, Modal, CollapsibleSection, SeverityBadge, DegradedModeBanner
|
||||||
│ ├── composables/ # useChartData, useReplayControls, usePolling, useFeedback, useGcs, useSofa, useApiMode, useChartTheme, useFocusTrap, chartFormat, patientFormat, timelineFormat, chartMedications, wardSort, wardFilter, departmentFormat, sepsisFormat, alertAcknowledge, criticalAlertDetect, useAlertNotification, useCriticalAlertPolling, handoffReport, vitalsForm, roleAccess, auditFormat, reconciliationFormat, thresholdForm, userForm
|
│ ├── composables/ # useChartData, useReplayControls, usePolling, useFeedback, useGcs, useSofa, useApiMode, useChartTheme, useFocusTrap, chartFormat, patientFormat, timelineFormat, chartMedications, wardSort, wardFilter, departmentFormat, sepsisFormat, alertAcknowledge, alertExplanation, criticalAlertDetect, useAlertNotification, useCriticalAlertPolling, handoffReport, vitalsForm, roleAccess, auditFormat, reconciliationFormat, thresholdForm, userForm
|
||||||
│ ├── plugins/ # medicationMarkerPlugin (Chart.js plugin for medication administration markers on vital charts)
|
│ ├── plugins/ # medicationMarkerPlugin (Chart.js plugin for medication administration markers on vital charts)
|
||||||
│ ├── stores/ # Pinia — ward (sort + filter + search), alerts (banner + polling), settings (sort prefs + sound mute), feedback, scoring, auth, departments, sepsis, operationsStore, alertQuality
|
│ ├── stores/ # Pinia — ward (sort + filter + search), alerts (banner + polling), settings (sort prefs + sound mute), feedback, scoring, auth, departments, sepsis, operationsStore, alertQuality
|
||||||
│ ├── views/ # LoginView, WardDashboard, PatientDetail, AlertCenter, FeedbackSummary, DepartmentOverviewView, SepsisBoardView, ThresholdManagementView, UserManagementView, AuditLogView, ReconciliationView, GatewayOperations, AlertQualityAnalytics
|
│ ├── views/ # LoginView, WardDashboard, PatientDetail, AlertCenter, FeedbackSummary, DepartmentOverviewView, SepsisBoardView, ThresholdManagementView, UserManagementView, AuditLogView, ReconciliationView, GatewayOperations, AlertQualityAnalytics
|
||||||
@@ -623,6 +632,7 @@ scripts/
|
|||||||
├── run-phase31-verification.sh # Phase 31 — RBAC integration tests + JWT login + audit log query
|
├── run-phase31-verification.sh # Phase 31 — RBAC integration tests + JWT login + audit log query
|
||||||
├── run-phase23-verification.sh # Phase 23 — Degraded operations visibility + gateway fleet + admin panels
|
├── run-phase23-verification.sh # Phase 23 — Degraded operations visibility + gateway fleet + admin panels
|
||||||
├── run-phase33-verification.sh # Phase 33 — Alert quality analytics integration tests
|
├── run-phase33-verification.sh # Phase 33 — Alert quality analytics integration tests
|
||||||
|
├── run-phase34-verification.sh # Phase 34 — Explainable alerts integration tests
|
||||||
├── demo-network-partition.sh # Gateway network partition demo script
|
├── demo-network-partition.sh # Gateway network partition demo script
|
||||||
└── mint-gateway-jwt.sh # JWT minting helper for gateway testing
|
└── mint-gateway-jwt.sh # JWT minting helper for gateway testing
|
||||||
|
|
||||||
@@ -914,6 +924,7 @@ With the API running (`dotnet run`) and Docker Compose up:
|
|||||||
./scripts/run-phase31-verification.sh # RBAC integration tests + JWT login + audit log query
|
./scripts/run-phase31-verification.sh # RBAC integration tests + JWT login + audit log query
|
||||||
./scripts/run-phase23-verification.sh # Degraded operations visibility + gateway fleet + admin panels
|
./scripts/run-phase23-verification.sh # Degraded operations visibility + gateway fleet + admin panels
|
||||||
./scripts/run-phase33-verification.sh # Alert quality analytics integration tests
|
./scripts/run-phase33-verification.sh # Alert quality analytics integration tests
|
||||||
|
./scripts/run-phase34-verification.sh # Explainable alerts integration tests
|
||||||
```
|
```
|
||||||
|
|
||||||
Phase 25 — GCS scoring (requires running API + Docker Compose; set an active encounter UUID):
|
Phase 25 — GCS scoring (requires running API + Docker Compose; set an active encounter UUID):
|
||||||
@@ -980,6 +991,12 @@ Phase 33 alert quality analytics tests only:
|
|||||||
dotnet test --filter "FullyQualifiedName~AlertQuality"
|
dotnet test --filter "FullyQualifiedName~AlertQuality"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Phase 34 explainable alerts tests only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test --filter "FullyQualifiedName~ExplainableAlerts"
|
||||||
|
```
|
||||||
|
|
||||||
Per-phase test runners (subset of `dotnet test`):
|
Per-phase test runners (subset of `dotnet test`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -1231,11 +1248,13 @@ Uses cursor pagination on `(recorded_at DESC, id DESC)` — offset pagination wo
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| GET | `/encounters/{id}/alerts` | Paginated alert list for an encounter |
|
| GET | `/encounters/{id}/alerts` | Paginated alert list for an encounter |
|
||||||
| GET | `/alerts` | Global alert list; optional `status`, `severity`, `department` filter |
|
| GET | `/alerts` | Global alert list; optional `status`, `severity`, `department` filter |
|
||||||
| GET | `/alerts/{id}` | Alert detail |
|
| GET | `/alerts/{id}` | Alert detail (`AlertResponse` with optional `explanation`) |
|
||||||
| POST | `/alerts/{id}/acknowledge` | Acknowledge with clinician ID and optional note |
|
| POST | `/alerts/{id}/acknowledge` | Acknowledge with clinician ID and optional note; returns `AlertResponse` |
|
||||||
| POST | `/alerts/{id}/resolve` | Resolve (must be acknowledged first) |
|
| POST | `/alerts/{id}/resolve` | Resolve (must be acknowledged first); returns `AlertResponse` |
|
||||||
| POST | `/alerts/{id}/feedback` | Submit clinician feedback (one per user per alert); requires `alerts:feedback` |
|
| POST | `/alerts/{id}/feedback` | Submit clinician feedback (one per user per alert); requires `alerts:feedback` |
|
||||||
|
|
||||||
|
**Alert response shape:** list, get, acknowledge, and resolve endpoints return `AlertResponse` — alert fields plus optional `explanation` (`scoreContributors`, `trend`, `medicationContext`, `narrativeSummary`). Omitted on legacy and threshold-only alerts.
|
||||||
|
|
||||||
**Alert lifecycle:**
|
**Alert lifecycle:**
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -1656,6 +1675,7 @@ observationId Guid? FK → Observation (null for NEWS2, GCS, SOFA composite
|
|||||||
alertType string e.g. CRITICAL_HEART_RATE, QSOFA_SCREEN, SOFA_SEPSIS, NEWS2_WARNING, NEWS2_EMERGENCY, GCS_CRITICAL
|
alertType string e.g. CRITICAL_HEART_RATE, QSOFA_SCREEN, SOFA_SEPSIS, NEWS2_WARNING, NEWS2_EMERGENCY, GCS_CRITICAL
|
||||||
severity string WARNING | CRITICAL
|
severity string WARNING | CRITICAL
|
||||||
details text required
|
details text required
|
||||||
|
explanation jsonb? immutable structured explanation snapshot (score contributors, trend, medication context, narrative); null on legacy/threshold-only alerts
|
||||||
observationCode string? observation code that triggered this alert (e.g. HEART_RATE) — enables direct lookups without LIKE pattern matching
|
observationCode string? observation code that triggered this alert (e.g. HEART_RATE) — enables direct lookups without LIKE pattern matching
|
||||||
status string open | acknowledged | resolved | escalated (default: open)
|
status string open | acknowledged | resolved | escalated (default: open)
|
||||||
acknowledgedAt DateTimeOffset?
|
acknowledgedAt DateTimeOffset?
|
||||||
@@ -2206,7 +2226,7 @@ Observation history uses cursor pagination on `(recorded_at DESC, id DESC)`. Off
|
|||||||
|
|
||||||
## Implemented Phases
|
## Implemented Phases
|
||||||
|
|
||||||
Thirty-one phases from the project roadmap are implemented and verified, including the **Site & Gateway Registry** (Phase 20), the **Ward Gateway Service** (Phase 21), the **Dashboard Gap Analysis Fixes** (Phase 22), the **Degraded Operations Visibility** (Phase 23), the **Sepsis-3 clinical refactor** (Phases 27–29), the **FHIR R4 Inbound Facade** (Phase 30), **RBAC with clinical audit logging** (Phase 31), the **Alert Quality Analytics** (Phase 33), and the **Enhanced Dashboard** (department overview, sepsis bundle board, critical alert notifications, shift handoff reports, vitals entry, sortable/filterable ward table). Integration tests (`dotnet test`) and per-phase verification scripts cover Phases 8–15, 20–23, 25–31, 33. Phases 17–19 add the Vue dashboard and clinician feedback (Vitest in `vigilcare-dashboard/`).
|
Thirty-two phases from the project roadmap are implemented and verified, including the **Site & Gateway Registry** (Phase 20), the **Ward Gateway Service** (Phase 21), the **Dashboard Gap Analysis Fixes** (Phase 22), the **Degraded Operations Visibility** (Phase 23), the **Sepsis-3 clinical refactor** (Phases 27–29), the **FHIR R4 Inbound Facade** (Phase 30), **RBAC with clinical audit logging** (Phase 31), the **Alert Quality Analytics** (Phase 33), the **Explainable Alerts** (Phase 34), and the **Enhanced Dashboard** (department overview, sepsis bundle board, critical alert notifications, shift handoff reports, vitals entry, sortable/filterable ward table). Integration tests (`dotnet test`) and per-phase verification scripts cover Phases 8–15, 20–23, 25–31, 33–34. Phases 17–19 add the Vue dashboard and clinician feedback (Vitest in `vigilcare-dashboard/`).
|
||||||
|
|
||||||
| Phase | Feature | Status |
|
| Phase | Feature | Status |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -2241,8 +2261,9 @@ Thirty-one phases from the project roadmap are implemented and verified, includi
|
|||||||
| 31 | **RBAC + Clinical Audit Logging** — JWT bearer authentication (`AuthService`, `AuthController`); four clinical roles (`Nurse`, `Physician`, `Admin`, `Integration`) with 18 granular permissions; `AuthorizePermission` attribute on all controller actions; `PermissionAuthorizationHandler` + `PermissionPolicyProvider` resolve `perm:*` policies; `CurrentUserService` extracts identity from JWT claims; `ClinicalUser` entity with BCrypt password hashing; `ClinicalAuditLog` append-only table with before/after JSONB, user identity, IP, and correlation ID; `AuditService` writes log entries on clinical write actions (10 audit actions); `AuditLogsController` admin-only query with filters; `FhirApiKeyOrJwtMiddleware` dual auth for FHIR routes (JWT or X-Api-Key with multi-key rotation); alert `acknowledgedBy` set from authenticated user, not request body; four seeded demo users; frontend `LoginView` + `auth` Pinia store with `localStorage` token persistence; Vue router auth guard; `RbacTests`; `run-phase31-verification.sh` | Done |
|
| 31 | **RBAC + Clinical Audit Logging** — JWT bearer authentication (`AuthService`, `AuthController`); four clinical roles (`Nurse`, `Physician`, `Admin`, `Integration`) with 18 granular permissions; `AuthorizePermission` attribute on all controller actions; `PermissionAuthorizationHandler` + `PermissionPolicyProvider` resolve `perm:*` policies; `CurrentUserService` extracts identity from JWT claims; `ClinicalUser` entity with BCrypt password hashing; `ClinicalAuditLog` append-only table with before/after JSONB, user identity, IP, and correlation ID; `AuditService` writes log entries on clinical write actions (10 audit actions); `AuditLogsController` admin-only query with filters; `FhirApiKeyOrJwtMiddleware` dual auth for FHIR routes (JWT or X-Api-Key with multi-key rotation); alert `acknowledgedBy` set from authenticated user, not request body; four seeded demo users; frontend `LoginView` + `auth` Pinia store with `localStorage` token persistence; Vue router auth guard; `RbacTests`; `run-phase31-verification.sh` | Done |
|
||||||
| 23 | **Degraded Operations Visibility** — `GatewayStaleDetectorService` background service auto-marks gateways OFFLINE when heartbeat exceeds configurable `StaleThresholdMinutes`; `OperationsController` exposes gateway fleet listing (`GET /operations/gateways` with status/site filters), gateway detail (`GET /operations/gateways/{id}`), and site summary (`GET /operations/sites/{siteId}/summary`); `DischargeSummaryService` with `GET /encounters/{id}/discharge-summary` (info) and `GET /encounters/{id}/discharge-summary/content` (MinIO PDF download); `UsersController` (`GET /users`, `POST /users`, `PATCH /users/{id}`) for admin user account management; frontend: `GatewayOperations.vue` operations dashboard, `DegradedModeBanner.vue` warning banner, `DischargeSummaryPanel.vue` on patient detail, `ThresholdManagementView.vue` with `ThresholdFormModal.vue`, `UserManagementView.vue` with `UserFormModal.vue`, `AuditLogView.vue`, `ReconciliationView.vue`; role-aware admin sidebar navigation; `roleAccess.js` composable; `useChartTheme.js`, `useFocusTrap.js`, `useApiMode.js` composables; `CollapsibleSection.vue`, `SeverityBadge.vue` UI components; `OperationsApiTests`; `run-phase23-verification.sh` | Done |
|
| 23 | **Degraded Operations Visibility** — `GatewayStaleDetectorService` background service auto-marks gateways OFFLINE when heartbeat exceeds configurable `StaleThresholdMinutes`; `OperationsController` exposes gateway fleet listing (`GET /operations/gateways` with status/site filters), gateway detail (`GET /operations/gateways/{id}`), and site summary (`GET /operations/sites/{siteId}/summary`); `DischargeSummaryService` with `GET /encounters/{id}/discharge-summary` (info) and `GET /encounters/{id}/discharge-summary/content` (MinIO PDF download); `UsersController` (`GET /users`, `POST /users`, `PATCH /users/{id}`) for admin user account management; frontend: `GatewayOperations.vue` operations dashboard, `DegradedModeBanner.vue` warning banner, `DischargeSummaryPanel.vue` on patient detail, `ThresholdManagementView.vue` with `ThresholdFormModal.vue`, `UserManagementView.vue` with `UserFormModal.vue`, `AuditLogView.vue`, `ReconciliationView.vue`; role-aware admin sidebar navigation; `roleAccess.js` composable; `useChartTheme.js`, `useFocusTrap.js`, `useApiMode.js` composables; `CollapsibleSection.vue`, `SeverityBadge.vue` UI components; `OperationsApiTests`; `run-phase23-verification.sh` | Done |
|
||||||
| 33 | **Alert Quality Analytics** — `AlertFeedback` entity with per-user-per-alert constraint; `POST /alerts/{id}/feedback` server-side feedback submission with `alerts:feedback` permission (Nurse, Physician, Admin); `AlertQualityMetric` entity stores per-alert-type quality snapshots (acknowledgement rate, false positive rate, useful rate, would-act rate, avg seconds to acknowledge/resolve); `AlertQualityAggregatorService` background service computes metrics periodically; `AlertQualityMetricsController` exposes `GET /alerts/quality-metrics` (time-range + alert type filter) and `GET /alerts/quality-metrics/summary`; `AlertFeedbackConfiguration` and `AlertQualityMetricConfiguration` EF Core configs; `SubmitAlertFeedbackRequestValidator`; Prometheus `alert_quality_useful_rate` and `alert_quality_false_positive_rate` gauges; Grafana `alert-quality-dashboard.json`; frontend `AlertQualityAnalytics.vue` with `AlertQualityChart.vue` and `alertQuality` Pinia store; `AlertQualityAnalyticsTests`; `run-phase33-verification.sh` | Done |
|
| 33 | **Alert Quality Analytics** — `AlertFeedback` entity with per-user-per-alert constraint; `POST /alerts/{id}/feedback` server-side feedback submission with `alerts:feedback` permission (Nurse, Physician, Admin); `AlertQualityMetric` entity stores per-alert-type quality snapshots (acknowledgement rate, false positive rate, useful rate, would-act rate, avg seconds to acknowledge/resolve); `AlertQualityAggregatorService` background service computes metrics periodically; `AlertQualityMetricsController` exposes `GET /alerts/quality-metrics` (time-range + alert type filter) and `GET /alerts/quality-metrics/summary`; `AlertFeedbackConfiguration` and `AlertQualityMetricConfiguration` EF Core configs; `SubmitAlertFeedbackRequestValidator`; Prometheus `alert_quality_useful_rate` and `alert_quality_false_positive_rate` gauges; Grafana `alert-quality-dashboard.json`; frontend `AlertQualityAnalytics.vue` with `AlertQualityChart.vue` and `alertQuality` Pinia store; `AlertQualityAnalyticsTests`; `run-phase33-verification.sh` | Done |
|
||||||
|
| 34 | **Explainable Alerts** — `AlertExplanation` value object (`ScoreContributor`, `TrendContext`, `MedicationContext`, `NarrativeSummary`); JSONB `ClinicalAlert.Explanation` column (immutable at creation); contributor builders for NEWS2, SOFA, GCS; `TrendContextBuilder`; `AlertExplanationBuilder` + `ClinicalAlertFactory`; NEWS2, SOFA, GCS, and `TrendDetector` wire explanation and include `explanation` in `alert.generated` outbox; `MedicationCorrelationHelper.TryGetContextAsync()` for structured medication context; `AlertResponse` DTO + `AlertResponseMapper`; GET/list/acknowledge/resolve return `AlertResponse`; ES indexer projects `NarrativeSummary`; data lake Parquet `explanation_json`; ward gateway `LocalClinicalAlert.ExplanationJson` + sync; dashboard `AlertReasoning.vue` + `alertExplanation.js`; simulator `ExpectedOutcomeValidator` with `narrativeContains`; `ExplainableAlertsTests`; `run-phase34-verification.sh` | Done |
|
||||||
|
|
||||||
**Ward dashboard:** backend APIs (`GET /encounters` ward list with extended summary fields including SOFA/GCS/attending/admitted-at, `GET /qsofa/current`, `GET /qsofa/history`, `GET /gcs/history`, `GET /sepsis-bundles` hospital-wide list, `GET /operations/gateways` fleet management, `GET /users` user management, `GET /alerts/quality-metrics` alert quality, CORS) and frontend SPA — `EncountersListTests`, `QsofaCurrentTests`, `GapAnalysisFixTests`, `OperationsApiTests`, `AlertQualityAnalyticsTests`, `vigilcare-dashboard` Vitest suite (replay scrubbing, feedback store, FeedbackButtons, FeedbackSummary, alert components, charts, ward table, ward sort, ward filter, department format, sepsis format, alert acknowledge, critical alert detect, handoff report, vitals form, GCS entry/history, SOFA panel/history, qSOFA history, scores panel, alert labels, PatientBanner, EncounterTimeline, medication chart markers, AcknowledgeModal, CriticalAlertBanner, DepartmentOverviewView, SepsisBoardView, VitalsEntryForm, useAlertStore, useWardStore, roleAccess, ThresholdManagementView, DischargeSummaryPanel, GatewayOperations, alertQuality).
|
**Ward dashboard:** backend APIs (`GET /encounters` ward list with extended summary fields including SOFA/GCS/attending/admitted-at, `GET /qsofa/current`, `GET /qsofa/history`, `GET /gcs/history`, `GET /sepsis-bundles` hospital-wide list, `GET /operations/gateways` fleet management, `GET /users` user management, `GET /alerts/quality-metrics` alert quality, `GET /alerts/{id}` with structured explanation, CORS) and frontend SPA — `EncountersListTests`, `QsofaCurrentTests`, `GapAnalysisFixTests`, `OperationsApiTests`, `AlertQualityAnalyticsTests`, `ExplainableAlertsTests`, `vigilcare-dashboard` Vitest suite (replay scrubbing, feedback store, FeedbackButtons, FeedbackSummary, alert components, AlertReasoning, charts, ward table, ward sort, ward filter, department format, sepsis format, alert acknowledge, critical alert detect, handoff report, vitals form, GCS entry/history, SOFA panel/history, qSOFA history, scores panel, alert labels, PatientBanner, EncounterTimeline, medication chart markers, AcknowledgeModal, CriticalAlertBanner, DepartmentOverviewView, SepsisBoardView, VitalsEntryForm, useAlertStore, useWardStore, roleAccess, ThresholdManagementView, DischargeSummaryPanel, GatewayOperations, alertQuality).
|
||||||
|
|
||||||
**Enhanced Dashboard (post-Phase 22):** Major dashboard feature expansion addressing clinical workflow gaps. **Department Overview** (`/departments`) — unit-level snapshot cards showing patient count, critical/alert/bundle totals per department with acuity distribution bars; click-through to ward filtered by department. **Sepsis Bundle Board** (`/sepsis`) — real-time bundle compliance tracking with countdown timers to 1-hour deadline, urgency-sorted (overdue → at-risk → on-track), live 1-second tick updates. **Critical Alert Notifications** — `CriticalAlertBanner` surfaces new critical alerts from polling cycle with audible 880Hz two-tone alert, browser title flash, and native `Notification` API integration; mute toggle persisted in settings. **Shift Handoff Report** — `HandoffReport.vue` generates SBAR-format (Situation, Background, Assessment, Recommendation) structured reports for all ward patients, enriched with latest vitals, open alerts, pending orders, and sepsis bundle status; ward summary with department stats; print/PDF export. **Vitals Entry Form** — `VitalsEntryForm.vue` on patient detail page enables manual observation recording (7 vital parameters with AVPU dropdown) with client-side plausibility validation matching server-side ranges. **Ward Table Enhancements** — multi-column sorting (room, patient, department, NEWS2, qSOFA, sepsis, alerts) with sortable column headers, debounced patient search (name/MRN), quick-filter toggles (critical, has alerts, active sepsis), clear-all filters. **Acknowledge Modal** — role-aware acknowledgment with clinician identity pre-populated from JWT, role-specific guidance text, and acknowledgment note preview. Backend additions: `GET /sepsis-bundles` paginated hospital-wide list with `SepsisBundleSummary` (patient demographics, elements, deadlines); `WardEncounterSummary` extended with `sofaScore`, `sofaDelta`, `gcsScore`, `gcsClassification`, `lastObservationAt`, `attendingPhysician`, `admittedAt`.
|
**Enhanced Dashboard (post-Phase 22):** Major dashboard feature expansion addressing clinical workflow gaps. **Department Overview** (`/departments`) — unit-level snapshot cards showing patient count, critical/alert/bundle totals per department with acuity distribution bars; click-through to ward filtered by department. **Sepsis Bundle Board** (`/sepsis`) — real-time bundle compliance tracking with countdown timers to 1-hour deadline, urgency-sorted (overdue → at-risk → on-track), live 1-second tick updates. **Critical Alert Notifications** — `CriticalAlertBanner` surfaces new critical alerts from polling cycle with audible 880Hz two-tone alert, browser title flash, and native `Notification` API integration; mute toggle persisted in settings. **Shift Handoff Report** — `HandoffReport.vue` generates SBAR-format (Situation, Background, Assessment, Recommendation) structured reports for all ward patients, enriched with latest vitals, open alerts, pending orders, and sepsis bundle status; ward summary with department stats; print/PDF export. **Vitals Entry Form** — `VitalsEntryForm.vue` on patient detail page enables manual observation recording (7 vital parameters with AVPU dropdown) with client-side plausibility validation matching server-side ranges. **Ward Table Enhancements** — multi-column sorting (room, patient, department, NEWS2, qSOFA, sepsis, alerts) with sortable column headers, debounced patient search (name/MRN), quick-filter toggles (critical, has alerts, active sepsis), clear-all filters. **Acknowledge Modal** — role-aware acknowledgment with clinician identity pre-populated from JWT, role-specific guidance text, and acknowledgment note preview. Backend additions: `GET /sepsis-bundles` paginated hospital-wide list with `SepsisBundleSummary` (patient demographics, elements, deadlines); `WardEncounterSummary` extended with `sofaScore`, `sofaDelta`, `gcsScore`, `gcsClassification`, `lastObservationAt`, `attendingPhysician`, `admittedAt`.
|
||||||
|
|
||||||
@@ -2264,6 +2285,8 @@ Thirty-one phases from the project roadmap are implemented and verified, includi
|
|||||||
|
|
||||||
**Alert Quality Analytics (Phase 33):** Server-side clinician feedback persisted as `AlertFeedback` entities (one per user per alert, six feedback types). `AlertQualityAggregatorService` periodically computes per-alert-type quality metrics (acknowledgement rate, false positive rate, useful rate, would-act rate, response times). REST API exposes quality metric snapshots and aggregate summaries. Grafana dashboard visualizes alert quality trends. Frontend analytics view with quality charts.
|
**Alert Quality Analytics (Phase 33):** Server-side clinician feedback persisted as `AlertFeedback` entities (one per user per alert, six feedback types). `AlertQualityAggregatorService` periodically computes per-alert-type quality metrics (acknowledgement rate, false positive rate, useful rate, would-act rate, response times). REST API exposes quality metric snapshots and aggregate summaries. Grafana dashboard visualizes alert quality trends. Frontend analytics view with quality charts.
|
||||||
|
|
||||||
|
**Explainable Alerts (Phase 34):** Composite alerts (NEWS2, SOFA, GCS, rapid deterioration) carry an immutable JSONB `explanation` snapshot at creation — score contributors with raw values and normal ranges, trend context (percent change, duration, direction), structured medication context, and a bedside `NarrativeSummary`. `AlertResponse` exposes explanation on GET/list/acknowledge/resolve. Downstream consumers (Elasticsearch indexer, data lake Parquet, ward gateway sync, Kafka `alert.generated`) propagate explanation without breaking legacy consumers. Dashboard `AlertReasoning.vue` renders structured reasoning. Simulator validates `narrativeContains` on key scenarios.
|
||||||
|
|
||||||
**Post-phase hardening (after Phase 31):**
|
**Post-phase hardening (after Phase 31):**
|
||||||
- **FHIR R4 read/search** — `FhirReadController` adds `GET /fhir/R4/Patient/{id}`, `GET /fhir/R4/Patient` (search by `identifier`), `GET /fhir/R4/Encounter/{id}`, `GET /fhir/R4/Encounter` (search by `patient`/`status`); new `fhir:read` permission for Admin and Integration roles; CapabilityStatement updated to advertise `read` and `searchType` interactions for Patient and Encounter
|
- **FHIR R4 read/search** — `FhirReadController` adds `GET /fhir/R4/Patient/{id}`, `GET /fhir/R4/Patient` (search by `identifier`), `GET /fhir/R4/Encounter/{id}`, `GET /fhir/R4/Encounter` (search by `patient`/`status`); new `fhir:read` permission for Admin and Integration roles; CapabilityStatement updated to advertise `read` and `searchType` interactions for Patient and Encounter
|
||||||
- **Alert threshold deletion** — `DELETE /alert-thresholds/{id}` with `THRESHOLD_DELETED` audit action and Redis cache invalidation
|
- **Alert threshold deletion** — `DELETE /alert-thresholds/{id}` with `THRESHOLD_DELETED` audit action and Redis cache invalidation
|
||||||
|
|||||||
@@ -243,27 +243,55 @@ Grafana dashboard JSON template for alert quality overview.
|
|||||||
| Clinical Usage | 10 |
|
| Clinical Usage | 10 |
|
||||||
| Composite | 9.0 |
|
| Composite | 9.0 |
|
||||||
|
|
||||||
|
> **Status:** Complete — see [phase-34-plan.md](plans/phase-34-plan.md) for full implementation detail, tests, and file inventory.
|
||||||
|
|
||||||
Architecture: Each alert carries a structured explanation payload alongside the existing `Details` string. A new `AlertExplanation` value object captures score contributors, trend context, and medication context at alert creation time. Scoring consumers (NEWS2, SOFA, GCS) emit contributor breakdowns. The `TrendDetector` attaches trend summaries. `MedicationCorrelationHelper` attaches recent medication context. All explanation data is serialized as JSONB on the `ClinicalAlert` row so the explanation is immutable — it reflects the state at alert time, not query time.
|
Architecture: Each alert carries a structured explanation payload alongside the existing `Details` string. A new `AlertExplanation` value object captures score contributors, trend context, and medication context at alert creation time. Scoring consumers (NEWS2, SOFA, GCS) emit contributor breakdowns. The `TrendDetector` attaches trend summaries. `MedicationCorrelationHelper` attaches recent medication context. All explanation data is serialized as JSONB on the `ClinicalAlert` row so the explanation is immutable — it reflects the state at alert time, not query time.
|
||||||
|
|
||||||
> **Prerequisite:** Phase 33 for feedback loop. No hard technical dependency but sequencing allows feedback data to inform which explanations clinicians value.
|
> **Prerequisite:** Phase 33 for feedback loop. No hard technical dependency but sequencing allows feedback data to inform which explanations clinicians value.
|
||||||
|
|
||||||
### What exists
|
### What exists
|
||||||
|
|
||||||
After Phase 33:
|
After Phase 34 (implemented):
|
||||||
|
|
||||||
- `ClinicalAlert.Details` is a free-text string, sometimes containing score values
|
- `AlertExplanation` value object with `ScoreContributor`, `TrendContext`, `MedicationContext`, and `NarrativeSummary`
|
||||||
- `TrendDetector` returns `TrendOutcome` enum but no narrative description
|
- `ClinicalAlert.Explanation` — nullable JSONB column, immutable at alert creation
|
||||||
- `MedicationCorrelationHelper.TryAnnotateDetailsAsync()` appends drug info to the details string
|
- NEWS2, SOFA, GCS, and Trend detectors assemble explanation at alert creation; `alert.generated` outbox/Kafka payloads include `explanation`
|
||||||
- NEWS2, SOFA, GCS scoring returns aggregate scores but not per-component breakdowns to the alert layer
|
- `MedicationCorrelationHelper.TryGetContextAsync()` — structured medication context (replaces string-append to `Details`)
|
||||||
- No structured explanation model
|
- Scoring results carry contributors: `News2Result`, `SofaScoringResult`, `GcsResult`, `TrendResult`
|
||||||
|
- `AlertResponse` DTO with `Explanation`; GET/list/acknowledge/resolve endpoints return it
|
||||||
|
- Dashboard `AlertReasoning.vue` and alert surfaces consume structured explanation; legacy alerts fall back to `Details`
|
||||||
|
- Simulator `AlertResponse.Explanation` + `ExpectedOutcomeValidator` with `narrativeContains` on key scenarios
|
||||||
|
- Elasticsearch indexes `NarrativeSummary`; data lake Parquet includes `explanation_json`; ward gateway sync forwards explanation
|
||||||
|
- `ExplainableAlertsTests` (10 tests) + `scripts/run-phase34-verification.sh`
|
||||||
|
- `Details` string unchanged for backward compatibility with threshold-only and legacy alerts
|
||||||
|
|
||||||
### What needs to be built
|
### What needs to be built
|
||||||
|
|
||||||
Six steps, in order.
|
Nothing — Phase 34 is complete. **Next:** [Phase 35 — Alert Lifecycle Analytics](#phase-35--alert-lifecycle-analytics).
|
||||||
|
|
||||||
|
### Verification Checklist (Phase 34)
|
||||||
|
|
||||||
|
- [x] NEWS2 alert includes per-component score contributors in explanation
|
||||||
|
- [x] SOFA alert includes per-organ-system contributors
|
||||||
|
- [x] GCS alert includes Eye/Verbal/Motor breakdown
|
||||||
|
- [x] Trend-triggered alerts include trend context with percent change and duration
|
||||||
|
- [x] Medication-correlated alerts include drug context
|
||||||
|
- [x] Narrative summary is human-readable and accurate
|
||||||
|
- [x] Existing alerts with null explanation still serialize correctly
|
||||||
|
- [x] Alert GET endpoints return explanation object
|
||||||
|
- [x] No change to scoring algorithm outputs (same scores, same thresholds)
|
||||||
|
- [x] Migration applies cleanly on existing data
|
||||||
|
- [x] Dashboard and simulator consume explanation
|
||||||
|
- [x] Downstream consumers (ES, data lake, gateway sync) include explanation fields
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### Step 1 — Explanation Value Objects
|
#### Implementation reference (Steps 1–7)
|
||||||
|
|
||||||
|
The step-by-step design below is retained for interview prep and onboarding. All steps are implemented; see [phase-34-plan.md](plans/phase-34-plan.md) for code paths and tests.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Original step-by-step design (Steps 1–6)</summary>
|
||||||
|
|
||||||
**`Domains/ValueObjects/AlertExplanation.cs`** (NEW):
|
**`Domains/ValueObjects/AlertExplanation.cs`** (NEW):
|
||||||
|
|
||||||
@@ -380,20 +408,7 @@ Extend alert GET endpoints to include the `Explanation` object. The explanation
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
</details>
|
||||||
|
|
||||||
### Verification Checklist (Phase 34)
|
|
||||||
|
|
||||||
- [ ] NEWS2 alert includes per-component score contributors in explanation
|
|
||||||
- [ ] SOFA alert includes per-organ-system contributors
|
|
||||||
- [ ] GCS alert includes Eye/Verbal/Motor breakdown
|
|
||||||
- [ ] Trend-triggered alerts include trend context with percent change and duration
|
|
||||||
- [ ] Medication-correlated alerts include drug context
|
|
||||||
- [ ] Narrative summary is human-readable and accurate
|
|
||||||
- [ ] Existing alerts with null explanation still serialize correctly
|
|
||||||
- [ ] Alert GET endpoints return explanation object
|
|
||||||
- [ ] No change to scoring algorithm outputs (same scores, same thresholds)
|
|
||||||
- [ ] Migration applies cleanly on existing data
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -415,6 +430,8 @@ After Phase 34:
|
|||||||
|
|
||||||
- `ClinicalAlert` has `Status` transitions: Open → Acknowledged → Resolved, Open → Escalated → Acknowledged → Resolved
|
- `ClinicalAlert` has `Status` transitions: Open → Acknowledged → Resolved, Open → Escalated → Acknowledged → Resolved
|
||||||
- `AcknowledgedAt`, `ResolvedAt` timestamps exist on the entity
|
- `AcknowledgedAt`, `ResolvedAt` timestamps exist on the entity
|
||||||
|
- `ClinicalAlert.Explanation` JSONB with immutable score contributors, trend, and medication context at alert time
|
||||||
|
- `AlertResponse` exposes explanation to dashboard and simulator; `Details` retained for legacy consumers
|
||||||
- `ClinicalAuditLog` captures some transitions but is not structured for time-series analytics
|
- `ClinicalAuditLog` captures some transitions but is not structured for time-series analytics
|
||||||
- No dedicated lifecycle event log
|
- No dedicated lifecycle event log
|
||||||
- No computed lifecycle metrics (median ack time, escalation rate)
|
- No computed lifecycle metrics (median ack time, escalation rate)
|
||||||
@@ -673,7 +690,7 @@ Authorized for `Admin` role only. PUT upserts a rule and invalidates the Redis c
|
|||||||
|
|
||||||
Architecture: A correlation engine groups alerts that fire within a configurable time window for the same patient into a single clinical narrative. Correlated alerts are linked by a shared `CorrelationGroupId`. The first alert in a group becomes the primary; subsequent alerts within the window attach as secondary. The group carries a composite explanation built from Phase 34 individual explanations. Clinicians see one bundled notification with the full picture instead of multiple independent alerts.
|
Architecture: A correlation engine groups alerts that fire within a configurable time window for the same patient into a single clinical narrative. Correlated alerts are linked by a shared `CorrelationGroupId`. The first alert in a group becomes the primary; subsequent alerts within the window attach as secondary. The group carries a composite explanation built from Phase 34 individual explanations. Clinicians see one bundled notification with the full picture instead of multiple independent alerts.
|
||||||
|
|
||||||
> **Prerequisite:** Phase 34 (Explainable Alerts) for structured explanations to compose into bundles.
|
> **Prerequisite:** Phase 34 (Explainable Alerts) **complete** — structured explanations compose into bundle narratives.
|
||||||
|
|
||||||
### What exists
|
### What exists
|
||||||
|
|
||||||
@@ -681,9 +698,9 @@ After Phase 36:
|
|||||||
|
|
||||||
- Alerts fire independently per scoring consumer and trend detector
|
- Alerts fire independently per scoring consumer and trend detector
|
||||||
- No correlation between simultaneous alerts for the same patient
|
- No correlation between simultaneous alerts for the same patient
|
||||||
- Phase 34 `AlertExplanation` provides structured per-alert context
|
- Phase 34 `AlertExplanation` **shipped** — per-alert `NarrativeSummary`, score contributors, trend, and medication context available on `AlertResponse`
|
||||||
- `AlertSuppressionService` prevents duplicate alert types but not cross-type bundling
|
- `AlertSuppressionService` prevents duplicate alert types but not cross-type bundling
|
||||||
- Medication correlation annotates individual alerts but does not group them
|
- Medication correlation attaches structured context to individual alerts but does not group them
|
||||||
|
|
||||||
### What needs to be built
|
### What needs to be built
|
||||||
|
|
||||||
@@ -953,7 +970,7 @@ After Phase 38:
|
|||||||
- qSOFA scoring implementation in dedicated service
|
- qSOFA scoring implementation in dedicated service
|
||||||
- GCS scoring implementation in dedicated service
|
- GCS scoring implementation in dedicated service
|
||||||
- Each has different input shapes, output shapes, and integration points
|
- Each has different input shapes, output shapes, and integration points
|
||||||
- Phase 34 added contributor extraction but each scoring system returns it differently
|
- Phase 34 added contributor extraction but each scoring system returns it differently (**implemented** — unify via `ScoreResult.Contributors` in Phase 39)
|
||||||
|
|
||||||
### What needs to be built
|
### What needs to be built
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
A production-quality clinical backend built with ASP.NET Core 8, PostgreSQL, Apache Kafka, RabbitMQ, Elasticsearch, Redis, and MinIO. The system models patient encounters, continuous observation ingest, and real-time clinical alerting with composite scoring engines (NEWS2, GCS, SOFA), Sepsis-3 two-tier detection (qSOFA screening → SOFA confirmation → treatment bundle), trend analysis, and alert suppression. It streams vital signs and lab results through Kafka, fans urgent notifications to clinicians through RabbitMQ with DLQ-based escalation, and maintains a searchable CQRS projection in Elasticsearch for ward dashboards and population analytics. A Vue 3 ward dashboard provides real-time clinical views. Ward gateway edge nodes buffer observations locally during connectivity loss. A FHIR R4 facade enables EHR integration. JWT-based RBAC with clinical audit logging gates every endpoint. Long-term data is archived as Parquet files in MinIO — a regulatory requirement in healthcare that has no equivalent in most other domains. Twenty-nine phases are implemented and verified.
|
A production-quality clinical backend built with ASP.NET Core 8, PostgreSQL, Apache Kafka, RabbitMQ, Elasticsearch, Redis, and MinIO. The system models patient encounters, continuous observation ingest, and real-time clinical alerting with composite scoring engines (NEWS2, GCS, SOFA), Sepsis-3 two-tier detection (qSOFA screening → SOFA confirmation → treatment bundle), trend analysis, alert suppression, and structured explainable alert payloads. It streams vital signs and lab results through Kafka, fans urgent notifications to clinicians through RabbitMQ with DLQ-based escalation, and maintains a searchable CQRS projection in Elasticsearch for ward dashboards and population analytics. A Vue 3 ward dashboard provides real-time clinical views with structured alert reasoning. Ward gateway edge nodes buffer observations locally during connectivity loss. A FHIR R4 facade enables EHR integration. JWT-based RBAC with clinical audit logging gates every endpoint. Long-term data is archived as Parquet files in MinIO — a regulatory requirement in healthcare that has no equivalent in most other domains. Thirty-two phases are implemented and verified.
|
||||||
|
|
||||||
The domain is deliberately different from the Digital Wallet API. Both projects use Kafka, RabbitMQ, and Elasticsearch, but the trade-off conversations are entirely different. In fintech the core question is "did the money move correctly?" In healthcare the core question is "did the right person get the right alert at the right time?" That distinction — correctness vs timeliness — produces different architectural decisions at every layer.
|
The domain is deliberately different from the Digital Wallet API. Both projects use Kafka, RabbitMQ, and Elasticsearch, but the trade-off conversations are entirely different. In fintech the core question is "did the money move correctly?" In healthcare the core question is "did the right person get the right alert at the right time?" That distinction — correctness vs timeliness — produces different architectural decisions at every layer.
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ docker compose up -d
|
|||||||
- Enable EHR integration via a FHIR R4 inbound facade with LOINC/SNOMED code mapping
|
- Enable EHR integration via a FHIR R4 inbound facade with LOINC/SNOMED code mapping
|
||||||
- Support edge deployment via ward gateway nodes with offline buffering and central sync
|
- Support edge deployment via ward gateway nodes with offline buffering and central sync
|
||||||
- Secure all endpoints with JWT-based RBAC and maintain an append-only clinical audit trail
|
- Secure all endpoints with JWT-based RBAC and maintain an append-only clinical audit trail
|
||||||
|
- Provide structured, immutable alert explanations (score contributors, trend context, medication context) so clinicians understand why composite alerts fired
|
||||||
- Produce a project that supports senior trade-off conversations in healthcare, medtech, and any domain where real-time alerting and long-term archival coexist
|
- Produce a project that supports senior trade-off conversations in healthcare, medtech, and any domain where real-time alerting and long-term archival coexist
|
||||||
|
|
||||||
## Non-Goals
|
## Non-Goals
|
||||||
@@ -127,7 +128,7 @@ An **alert threshold** defines the numeric boundaries that trigger a clinical al
|
|||||||
|
|
||||||
An **observation** is a single recorded measurement: a vital sign (heart rate, temperature, blood pressure), a lab value (potassium, glucose, white blood cell count), or a pulse oximetry reading. Observations are append-only. They are never updated or deleted. The observation stream is the primary input to both the alert engine and the Kafka pipeline.
|
An **observation** is a single recorded measurement: a vital sign (heart rate, temperature, blood pressure), a lab value (potassium, glucose, white blood cell count), or a pulse oximetry reading. Observations are append-only. They are never updated or deleted. The observation stream is the primary input to both the alert engine and the Kafka pipeline.
|
||||||
|
|
||||||
A **clinical alert** is generated when an observation breaches a threshold or when the sepsis detection engine identifies a pattern across multiple recent observations. An alert has a lifecycle: `open` → `acknowledged` → `resolved`. An unacknowledged `CRITICAL` alert triggers the RabbitMQ escalation after five minutes.
|
A **clinical alert** is generated when an observation breaches a threshold or when the sepsis detection engine identifies a pattern across multiple recent observations. Composite alerts from NEWS2, SOFA, GCS, and trend detection carry an immutable JSONB `explanation` snapshot — score contributors, trend context, medication context, and a bedside narrative — frozen at alert creation time. An alert has a lifecycle: `open` → `acknowledged` → `resolved`. An unacknowledged `CRITICAL` alert triggers the RabbitMQ escalation after five minutes.
|
||||||
|
|
||||||
An **order** is a clinician's instruction: run this lab test, administer this medication, perform this imaging study. Orders have a status lifecycle and a `resulted_at` timestamp. The reconciliation job uses pending orders to detect cases where a result was never returned.
|
An **order** is a clinician's instruction: run this lab test, administer this medication, perform this imaging study. Orders have a status lifecycle and a `resulted_at` timestamp. The reconciliation job uses pending orders to detect cases where a result was never returned.
|
||||||
|
|
||||||
@@ -223,14 +224,17 @@ A `discharged` encounter triggers a RabbitMQ job to generate a discharge summary
|
|||||||
|
|
||||||
### 4. Clinical Alert Lifecycle
|
### 4. Clinical Alert Lifecycle
|
||||||
|
|
||||||
**Description:** Alerts are the patient safety core of the system. Every open `CRITICAL` alert must be acknowledged by a clinician within five minutes or it escalates. Every alert has a documented audit trail: who acknowledged it, when, and with what note.
|
**Description:** Alerts are the patient safety core of the system. Every open `CRITICAL` alert must be acknowledged by a clinician within five minutes or it escalates. Every alert has a documented audit trail: who acknowledged it, when, and with what note. Composite alerts expose a structured `explanation` alongside the human-readable `details` string.
|
||||||
|
|
||||||
**Endpoints:**
|
**Endpoints:**
|
||||||
- `GET /api/v1/encounters/:id/alerts` — paginated alert list for an encounter
|
- `GET /api/v1/encounters/:id/alerts` — paginated alert list for an encounter (`AlertResponse`)
|
||||||
- `GET /api/v1/alerts` — global alert list filterable by status, severity, department
|
- `GET /api/v1/alerts` — global alert list filterable by status, severity, department
|
||||||
- `GET /api/v1/alerts/:id` — alert detail
|
- `GET /api/v1/alerts/:id` — alert detail with optional `explanation`
|
||||||
- `POST /api/v1/alerts/:id/acknowledge` — acknowledge with clinician ID and optional note
|
- `POST /api/v1/alerts/:id/acknowledge` — acknowledge with clinician ID and optional note; returns `AlertResponse`
|
||||||
- `POST /api/v1/alerts/:id/resolve` — resolve (must be acknowledged first)
|
- `POST /api/v1/alerts/:id/resolve` — resolve (must be acknowledged first); returns `AlertResponse`
|
||||||
|
- `POST /api/v1/alerts/:id/feedback` — submit clinician feedback (Phase 33)
|
||||||
|
|
||||||
|
**Alert response shape:** optional `explanation` with `scoreContributors`, `trend`, `medicationContext`, and `narrativeSummary`. Omitted on legacy and threshold-only alerts.
|
||||||
|
|
||||||
**Alert lifecycle:**
|
**Alert lifecycle:**
|
||||||
```
|
```
|
||||||
@@ -529,9 +533,9 @@ Alert suppression windows prevent warning fatigue. Acknowledging a suppressible
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 15. Medication Administration and Correlation (Phase 15)
|
### 15. Medication Administration and Correlation (Phase 15, 34)
|
||||||
|
|
||||||
**Description:** Records drug administrations per encounter. `MedicationCorrelationHelper` annotates warning and NEWS2 alert details when a correlated drug was administered within a configurable window (default 90 min). Annotations provide clinical context — e.g. `— note: metoprolol 25mg (PO) administered 45 min ago` — but never suppress alerts.
|
**Description:** Records drug administrations per encounter. `MedicationCorrelationHelper` appends medication context to warning alert `details` when a correlated drug was administered within a configurable window (default 90 min). Explainable alerts (NEWS2, SOFA, GCS, rapid deterioration) receive structured `MedicationContext` in the JSONB `explanation` via `TryGetContextAsync()`. Annotations provide clinical context — e.g. `"metoprolol 25mg (PO) administered 45 min ago"` — but never suppress alerts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -565,19 +569,36 @@ Append-only `clinical_audit_logs` table records write actions with user identity
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 20. Ward Dashboard (Phases 17–19, 22, 28)
|
### 20. Ward Dashboard (Phases 17–19, 22, 28, 34)
|
||||||
|
|
||||||
**Description:** Vue 3 SPA (`vigilcare-dashboard/`) with Vite, Pinia, Tailwind CSS v4, and Chart.js. Virtual ward table (NEWS2-sorted, department filter), patient detail view (vitals, scores, alerts, orders, sepsis bundle, GCS entry form, SOFA score panel, patient banner with demographics, encounter timeline), and alert center (global acknowledge/resolve).
|
**Description:** Vue 3 SPA (`vigilcare-dashboard/`) with Vite, Pinia, Tailwind CSS v4, and Chart.js. Virtual ward table (NEWS2-sorted, department filter), patient detail view (vitals, scores, alerts, orders, sepsis bundle, GCS entry form, SOFA score panel, patient banner with demographics, encounter timeline), and alert center (global acknowledge/resolve).
|
||||||
|
|
||||||
Chart components: five vital sign trend charts with medication administration markers, NEWS2 history, SOFA history with organ-system breakdown, GCS history with component tracking, qSOFA evaluation history. Local replay scrubbing, alert reasoning with medication context, and clinician feedback mode (six ratings per alert, Feedback Summary with JSON/CSV export).
|
Chart components: five vital sign trend charts with medication administration markers, NEWS2 history, SOFA history with organ-system breakdown, GCS history with component tracking, qSOFA evaluation history. Local replay scrubbing, structured alert reasoning (`AlertReasoning.vue` — score contributors, trend context, medication context, narrative summary from `explanation`), and clinician feedback mode (six ratings per alert, Feedback Summary with JSON/CSV export).
|
||||||
|
|
||||||
Phase 22 gap analysis fixes: `SofaHistory.vue`, `GcsHistory.vue`, `QsofaHistory.vue`, `PatientBanner.vue`, `EncounterTimeline.vue`, medication marker Chart.js plugin. Backend additions: `GET /gcs/history`, `GET /qsofa/history` APIs, `qsofa_evaluations` table.
|
Phase 22 gap analysis fixes: `SofaHistory.vue`, `GcsHistory.vue`, `QsofaHistory.vue`, `PatientBanner.vue`, `EncounterTimeline.vue`, medication marker Chart.js plugin. Backend additions: `GET /gcs/history`, `GET /qsofa/history` APIs, `qsofa_evaluations` table.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 21. Console Replay Simulator (Phase 16, 29)
|
### 21. Console Replay Simulator (Phase 16, 29, 34)
|
||||||
|
|
||||||
**Description:** Standalone `VigilCare.Simulator` .NET console app replays JSON scenario files against the live API with configurable speed. Commands: `replay`, `replay-all`, `validate`, `dry-run`. Optional `--poll` shows alerts, NEWS2, GCS, SOFA, and sepsis bundle state during replay. Eleven sample scenarios including GCS neurological decline, SOFA sepsis progression, and SpO₂/FiO₂ fallback.
|
**Description:** Standalone `VigilCare.Simulator` .NET console app replays JSON scenario files against the live API with configurable speed. Commands: `replay`, `replay-all`, `validate`, `dry-run`. Optional `--poll` shows alerts, NEWS2, GCS, SOFA, and sepsis bundle state during replay. `ExpectedOutcomeValidator` validates alert `narrativeContains` on key scenarios. Eleven sample scenarios including GCS neurological decline, SOFA sepsis progression, and SpO₂/FiO₂ fallback.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 22. Explainable Alerts (Phase 34)
|
||||||
|
|
||||||
|
**Description:** Each composite alert carries a structured explanation payload alongside the existing `details` string. An `AlertExplanation` value object captures score contributors, trend context, and medication context at alert creation time. Scoring consumers (NEWS2, SOFA, GCS) emit contributor breakdowns. `TrendDetector` attaches trend summaries. `MedicationCorrelationHelper.TryGetContextAsync()` attaches recent medication context. Explanation data is serialized as JSONB on the `ClinicalAlert` row — immutable, reflecting state at alert time, not query time.
|
||||||
|
|
||||||
|
**Key components:**
|
||||||
|
- `AlertExplanation`, `ScoreContributor`, `TrendContext`, `MedicationContext` value objects
|
||||||
|
- Contributor builders: `News2ContributorBuilder`, `SofaContributorBuilder`, `GcsContributorBuilder`, `TrendContextBuilder`
|
||||||
|
- `AlertExplanationBuilder` + `ClinicalAlertFactory` — idempotent INSERT with explanation JSON
|
||||||
|
- `AlertResponse` DTO with optional `Explanation`; exposed on GET/list/acknowledge/resolve
|
||||||
|
- `alert.generated` outbox/Kafka payload includes optional `explanation` field
|
||||||
|
- Elasticsearch indexes `NarrativeSummary`; data lake Parquet includes `explanation_json`
|
||||||
|
- Ward gateway `LocalClinicalAlert.ExplanationJson` synced via `ClinicalSyncBatchProcessor`
|
||||||
|
|
||||||
|
**Concepts practiced:** Explainable AI / CDS transparency, immutable clinical snapshots, human factors / alert usability (`NarrativeSummary` for bedside readability), backward-compatible API evolution (legacy alerts return null explanation).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -657,6 +678,7 @@ CREATE TABLE clinical_alerts (
|
|||||||
alert_type VARCHAR(50) NOT NULL,
|
alert_type VARCHAR(50) NOT NULL,
|
||||||
severity VARCHAR(20) NOT NULL,
|
severity VARCHAR(20) NOT NULL,
|
||||||
details TEXT NOT NULL,
|
details TEXT NOT NULL,
|
||||||
|
explanation JSONB NULL, -- Phase 34: immutable structured explanation snapshot
|
||||||
observation_code VARCHAR(50) NULL, -- enables direct lookups without LIKE pattern matching
|
observation_code VARCHAR(50) NULL, -- enables direct lookups without LIKE pattern matching
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'open',
|
status VARCHAR(20) NOT NULL DEFAULT 'open',
|
||||||
acknowledged_at TIMESTAMPTZ NULL,
|
acknowledged_at TIMESTAMPTZ NULL,
|
||||||
@@ -713,7 +735,7 @@ CREATE TABLE reconciliation_alerts (
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Tables added in Phases 10–31** (managed by EF Core migrations — see `README.md` Data Models for full column definitions):
|
**Tables added in Phases 10–34** (managed by EF Core migrations — see `README.md` Data Models for full column definitions):
|
||||||
|
|
||||||
| Table | Phase | Purpose |
|
| Table | Phase | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -729,6 +751,10 @@ CREATE TABLE reconciliation_alerts (
|
|||||||
| `clinical_audit_logs` | 31 | Append-only audit trail: action, entity, user, before/after JSONB, IP, correlation ID |
|
| `clinical_audit_logs` | 31 | Append-only audit trail: action, entity, user, before/after JSONB, IP, correlation ID |
|
||||||
| `clinical_sites` | 20 | Hospital sites with site code, name, address |
|
| `clinical_sites` | 20 | Hospital sites with site code, name, address |
|
||||||
| `ward_gateways` | 20 | Ward edge nodes with status, buffer depth, heartbeat, sync timestamps |
|
| `ward_gateways` | 20 | Ward edge nodes with status, buffer depth, heartbeat, sync timestamps |
|
||||||
|
| `alert_feedback` | 33 | Clinician feedback per user per alert (six feedback types) |
|
||||||
|
| `alert_quality_metrics` | 33 | Per-alert-type quality metric snapshots (acknowledgement/false-positive/useful rates) |
|
||||||
|
|
||||||
|
**Column additions:** `clinical_alerts.explanation` (JSONB, Phase 34) — immutable structured explanation on composite alerts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -810,6 +836,9 @@ Observations, alerts, and orders belong to an encounter, not directly to a patie
|
|||||||
| 29 | Simulator scenario expansion + clinical validation (end-to-end qSOFA → SOFA → bundle) | Done |
|
| 29 | Simulator scenario expansion + clinical validation (end-to-end qSOFA → SOFA → bundle) | Done |
|
||||||
| 30 | FHIR R4 Inbound Facade — per-resource ingest, transaction Bundles, read/search, LOINC mapping | Done |
|
| 30 | FHIR R4 Inbound Facade — per-resource ingest, transaction Bundles, read/search, LOINC mapping | Done |
|
||||||
| 31 | RBAC + Clinical Audit Logging — JWT auth, four roles, 17 permissions, append-only audit trail | Done |
|
| 31 | RBAC + Clinical Audit Logging — JWT auth, four roles, 17 permissions, append-only audit trail | Done |
|
||||||
|
| 23 | Degraded Operations Visibility — gateway fleet ops, stale detection, discharge summary, admin panels | Done |
|
||||||
|
| 33 | Alert Quality Analytics — server-side feedback, quality metrics API, Grafana dashboard | Done |
|
||||||
|
| 34 | Explainable Alerts — JSONB explanation, contributor builders, AlertResponse DTO, dashboard reasoning | Done |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -928,4 +957,4 @@ Follow the same Prometheus/Grafana and MinIO/Parquet approach as described in th
|
|||||||
|
|
||||||
### Phases 10–31 — Extended Feature Phases
|
### Phases 10–31 — Extended Feature Phases
|
||||||
|
|
||||||
See the [Build Order](#build-order) table for all implemented phases and the [Features](#features) section (items 12–21) for detailed descriptions. Per-phase implementation plans and verification guides are in `docs/plans/`. Integration tests and verification scripts cover all phases — see `README.md` for the full test table and script listing.
|
See the [Build Order](#build-order) table for all implemented phases and the [Features](#features) section (items 12–22) for detailed descriptions. Per-phase implementation plans and verification guides are in `docs/plans/`. Integration tests and verification scripts cover all phases — see `README.md` for the full test table and script listing.
|
||||||
|
|||||||
Reference in New Issue
Block a user