Files
vigilcare-clinical/docs/gap-analysis.md
T

74 KiB

Thorough Survey: VigilCare Clinical Domain Models and Entity Classes Project Overview The VigilCareClinical project is a clinical alert and patient monitoring system with comprehensive domain modeling focused on sepsis detection, clinical scoring systems, and alert management. The architecture uses Entity Framework Core with PostgreSQL and follows domain-driven design with configuration classes.

ENTITIES (17 total) Core Domain Entities

  1. Patient (/Domains/Entities/Patient.cs)

Key Properties: Id (Guid, PK) Mrn (string, max 20) - Medical Record Number, unique index FirstName, LastName (string, max 100) DateOfBirth (DateOnly) Gender (string, max 10) BloodType (enum: nullable) Allergies (nullable string) EmergencyContactName, EmergencyContactPhone (nullable) Status (string, default "active") CreatedAt (DateTimeOffset, default NOW()) Relationships: 1:N with Encounter (inverse: Encounters collection) Constraints & Indexes: Unique index on MRN for O(log n) lookups Configuration: /Data/Configurations/PatientConfiguration.cs (lines 4-33) Auditing: CreatedAt only, no soft delete pattern 2. Encounter (/Domains/Entities/Encounter.cs)

Key Properties: Id (Guid, PK) PatientId (Guid, FK) EncounterType (enum: Inpatient, Outpatient, Emergency) Status (enum: Scheduled, Active, Discharged, Cancelled; default Scheduled) Department (enum: ICU, GeneralMedicine, Emergency, Cardiology, Surgery, Pediatrics) AttendingPhysician (string, max 200, required) RoomBed (nullable, max 20) AdmissionReason, DischargeDiagnosis (nullable, max 500) AdmittedAt (DateTimeOffset, default NOW()) DischargedAt (nullable DateTimeOffset) CreatedAt (DateTimeOffset, default NOW()) Relationships: N:1 with Patient (FK: PatientId, delete behavior: Restrict) 1:N with Observation, ClinicalAlert, Order Constraints & Indexes: CHECK constraints on encounter_type, status, department Composite index: (PatientId, AdmittedAt) Filtered index: (Status, AdmittedAt) WHERE status='ACTIVE' Configuration: /Data/Configurations/EncounterConfiguration.cs (lines 4-59) 3. Observation (/Domains/Entities/Observation.cs)

Key Properties: Id (Guid, PK) EncounterId (Guid, FK) ObservationCode (string, max 50) - clinical code (HEART_RATE, TEMP_C, etc.) Value (decimal, 10,3 precision) Unit (string, max 20) Source (enum: Manual, Device, Lab; default Manual) IdempotencyKey (nullable, max 100) - for device deduplication RecordedAt (DateTimeOffset) CreatedAt (DateTimeOffset, default NOW()) Relationships: N:1 with Encounter (FK: EncounterId, delete behavior: Restrict) Constraints & Indexes: CHECK constraint on source Unique partial index on IdempotencyKey (WHERE idempotency_key IS NOT NULL) Composite index: (EncounterId, ObservationCode, RecordedAt) Configuration: /Data/Configurations/ObservationConfiguration.cs (lines 4-44) 4. ClinicalAlert (/Domains/Entities/ClinicalAlert.cs)

Key Properties: Id (Guid, PK) EncounterId (Guid, FK) PatientId (Guid) ObservationId (nullable, Guid) - FK to triggering observation (not enforced in config) AlertType (enum: extensive list - see below) Severity (enum: Warning, Critical) Details (string, required) Status (enum: Open, Acknowledged, Resolved, Escalated; default Open) AcknowledgedAt (nullable DateTimeOffset) AcknowledgedBy (nullable, max 200) ResolvedAt (nullable DateTimeOffset) TriggeredAt (DateTimeOffset, default NOW()) Relationships: N:1 with Encounter (FK: EncounterId, delete behavior: Restrict) Constraints & Indexes: CHECK constraints on severity, status, alert_type (comprehensive list) Composite indexes: (EncounterId, TriggeredAt), (PatientId, TriggeredAt) Filtered index: (Severity, TriggeredAt) WHERE status='OPEN' Configuration: /Data/Configurations/ClinicalAlertConfiguration.cs (lines 4-77) Alert Types: (53 total) Critical threshold: CriticalHeartRate, CriticalTempC, CriticalPotassiumMeqL, CriticalSpo2, CriticalRespRate, CriticalWbcKUl, CriticalSystolicBp, CriticalDiastolicBp, CriticalLactateMmolL, CriticalAvpu, CriticalGlucoseMgDl, CriticalPao2MmHg, CriticalPlateletKUl, CriticalBilirubinMgDl, CriticalCreatinineMgDl Warning threshold: WarningHeartRate, WarningTempC, WarningPotassiumMeqL, WarningSpo2, WarningRespRate, WarningWbcKUl, WarningSystolicBp, WarningDiastolicBp, WarningLactateMmolL, WarningGlucoseMgDl, WarningPao2MmHg, WarningPlateletKUl, WarningBilirubinMgDl, WarningCreatinineMgDl Scoring: News2Warning, News2Emergency, GcsCritical, GcsWarning Trend: RapidDeterioration Sepsis: SepsisWarning (obsolete), SofaSepsis, SofaWarning Screening: QsofaWarning (obsolete), QsofaScreen Scoring & Clinical Evaluation Entities 5. News2Score (/Domains/Entities/News2Score.cs)

Key Properties: Id, EncounterId, PatientId (Guids) TotalScore (int) RiskLevel (string: LOW, LOW_MEDIUM, MEDIUM, HIGH) Component scores: RespRateScore, Spo2Score, SystolicBpScore, HeartRateScore, ConsciousnessScore, TemperatureScore, SupplementalO2Score (all int) HasSingleParamThree (bool) CalculatedAt (DateTimeOffset) Relationships: N:1 with Encounter (FK: EncounterId, delete behavior: Restrict) Constraints & Indexes: CHECK constraint on risk_level Composite indexes: (EncounterId, CalculatedAt), (PatientId, CalculatedAt) Configuration: /Data/Configurations/News2ScoreConfiguration.cs (lines 4-37) 6. SofaScore (/Domains/Entities/SofaScore.cs)

Key Properties: Id, EncounterId, PatientId (Guids) TotalScore (int) Component scores: RespiratoryScore, CoagulationScore, LiverScore, CardiovascularScore, CnsScore, RenalScore (all int) IsBaseline (bool) - tracks if this is baseline SOFA for the encounter DeltaFromBaseline (nullable int) StalenessFlags (nullable string, stored as JSONB) - tracks which components are stale CalculatedAt, CreatedAt (DateTimeOffset) Relationships: N:1 with Encounter (FK: EncounterId, delete behavior: Restrict) Constraints & Indexes: Composite index: (EncounterId, CalculatedAt) Filtered index: (EncounterId) WHERE is_baseline=true Configuration: /Data/Configurations/SofaScoreConfiguration.cs (lines 4-36) Design Note: Implements SOFA (Sequential Organ Failure Assessment) for sepsis detection 7. GcsScore (/Domains/Entities/GcsScore.cs)

Key Properties: Id, EncounterId, PatientId (Guids) Component scores: EyeScore, VerbalScore, MotorScore (all int) TotalScore (int) Classification (string: MILD, MODERATE, SEVERE; max 16) CalculatedAt, CreatedAt (DateTimeOffset) Relationships: N:1 with Encounter (FK: EncounterId, delete behavior: Restrict) Constraints & Indexes: CHECK constraint on classification Composite index: (EncounterId, CalculatedAt) Configuration: /Data/Configurations/GcsScoreConfiguration.cs (lines 4-32) Design Note: GCS = Glasgow Coma Scale for neurological assessment Sepsis Management Entities 8. SepsisBundle (/Domains/Entities/SepsisBundle.cs)

Key Properties: Id (Guid, PK) EncounterId, TriggeringAlertId (Guid, FK) TriggeringAlertType (string, max 50) - human-readable alert type RecognizedAt (DateTimeOffset) - when sepsis was recognized DeadlineAt (DateTimeOffset) - compliance deadline ComplianceStatus (enum: InProgress, Compliant, NonCompliant; default InProgress) CompletedAt (nullable DateTimeOffset) Relationships: N:1 with Encounter (FK: EncounterId, delete behavior: Restrict) N:1 with ClinicalAlert via TriggeringAlert (delete behavior: Restrict) 1:N with SepsisBundleElement Constraints & Indexes: CHECK constraint on compliance_status Composite indexes: (EncounterId, RecognizedAt), (ComplianceStatus) Configuration: /Data/Configurations/SepsisBundleConfiguration.cs (lines 4-47) Design Pattern: Implements Sepsis 3-hour bundle tracking 9. SepsisBundleElement (/Domains/Entities/SepsisBundleElement.cs)

Key Properties: Id (Guid, PK) BundleId (Guid, FK) ElementType (enum: BloodCultures, SerumLactate, BroadSpectrumAntibiotics, IvFluidResuscitation) Status (enum: Pending, Completed; default Pending) OrderId (nullable, Guid, FK to Order) CompletedAt (nullable DateTimeOffset) Relationships: N:1 with SepsisBundle (FK: BundleId, delete behavior: Cascade) N:1 with Order, optional (FK: OrderId, delete behavior: SetNull) Constraints & Indexes: CHECK constraints on element_type, status Unique composite index: (BundleId, ElementType) Configuration: /Data/Configurations/SepsisBundleElementConfiguration.cs (lines 4-50) Medication & Order Management 10. Order (/Domains/Entities/Order.cs)

Key Properties: Id (Guid, PK) EncounterId (Guid, FK) OrderType (enum: Lab, Imaging, Medication, Procedure) Description (string, required) OrderedBy (string, max 200, required) Status (enum: Pending, InProgress, Resulted, Cancelled; default Pending) OrderedAt (DateTimeOffset, default NOW()) ResultedAt (nullable DateTimeOffset) ResultSummary (nullable string) Relationships: N:1 with Encounter (FK: EncounterId, delete behavior: Restrict) Constraints & Indexes: CHECK constraints on order_type, status Composite indexes: (EncounterId, OrderedAt) Filtered index: (Status, OrderedAt) WHERE status IN ('PENDING', 'IN_PROGRESS') Configuration: /Data/Configurations/OrderConfiguration.cs (lines 4-49) 11. MedicationAdministration (/Domains/Entities/MedicationAdministration.cs)

Key Properties: Id (Guid, PK) EncounterId (Guid, FK) DrugName (string, max 200, required) Dose (decimal, 10,4 precision, required) DoseUnit (string, max 20, required) Route (string, max 50, required) AdministeredAt (DateTimeOffset, required) AdministeredBy (string, max 100, required) Relationships: N:1 with Encounter (FK: EncounterId, delete behavior: Restrict) Constraints & Indexes: Composite indexes: (EncounterId, AdministeredAt), (EncounterId, DrugName) Configuration: /Data/Configurations/MedicationAdministrationConfiguration.cs (lines 4-27) Auditing: No explicit audit trail beyond CreatedAt Configuration & Support Entities 12. AlertThreshold (/Domains/Entities/AlertThreshold.cs)

Key Properties: Id (Guid, PK) ObservationCode (string, max 50) - links to Observation.ObservationCode DisplayName (string, max 200) Unit (string, max 20) Thresholds: CriticalLow, WarningLow, WarningHigh, CriticalHigh (all nullable decimal, 10,3) SuppressionWindowMinutes (nullable int) - alert suppression window CreatedAt (DateTimeOffset, default NOW()) Relationships: None (reference data) Constraints & Indexes: Unique index on ObservationCode Configuration: /Data/Configurations/AlertThresholdConfiguration.cs (lines 4-23) Design Note: Master configuration for threshold-based alerts 13. ReconciliationAlert (/Domains/Entities/ReconciliationAlert.cs)

Key Properties: Id (Guid, PK) CheckType (enum: UnacknowledgedCriticalAlert, PendingOrderNoResult, ActiveInpatientNoObservation) EncounterId (nullable, Guid, FK) PatientId (nullable, Guid, FK) Details (string, required) ResolvedAt (nullable DateTimeOffset) CreatedAt (DateTimeOffset, default NOW()) Relationships: N:1 with Encounter, optional (FK: EncounterId, delete behavior: SetNull) N:1 with Patient, optional (FK: PatientId, delete behavior: SetNull) Constraints & Indexes: CHECK constraint on check_type Filtered composite index: (CheckType, EncounterId) WHERE resolved_at IS NULL Configuration: /Data/Configurations/ReconciliationAlertConfiguration.cs (lines 4-40) Design Note: Data quality checks for consistency gaps System & Audit Entities 14. ClinicalUser (/Domains/Entities/ClinicalUser.cs)

Key Properties: Id (Guid, PK) Username (string, max 100, required) PasswordHash (string, max 500, required) DisplayName (string, max 200, required) Role (enum: Nurse, Physician, Admin, Integration) IsActive (bool, default true) CreatedAt (DateTimeOffset, default NOW()) LastLoginAt (nullable DateTimeOffset) Relationships: None Constraints & Indexes: Unique index on Username Configuration: /Data/Configurations/ClinicalUserConfiguration.cs (lines 4-21) Security Note: PasswordHash only, no plaintext storage 15. ClinicalAuditLog (/Domains/Entities/ClinicalAuditLog.cs)

Key Properties: Id (Guid, PK) Action (enum: ThresholdCreated, ThresholdUpdated, AlertAcknowledged, AlertResolved, EncounterStatusChanged, PatientRegistered, SuppressionWindowSet, UserLogin) EntityType (string, max 100) - type of audited entity EntityId (Guid) - ID of audited entity UserId (nullable Guid) - user who performed action UserDisplayName (nullable, max 200) PreviousValueJson (nullable string, JSONB) NewValueJson (nullable string, JSONB) Reason (nullable string) IpAddress (nullable, max 45) CorrelationId (nullable, max 100) CreatedAt (DateTimeOffset, default NOW()) Relationships: None Constraints & Indexes: Indexes on: EntityType, EntityId, UserId, CreatedAt Configuration: /Data/Configurations/ClinicalAuditLogConfiguration.cs (lines 4-29) Audit Pattern: Append-only; no UPDATE/DELETE from application code (comment at line 24) 16. OutboxEvent (/Domains/Entities/OutboxEvent.cs)

Key Properties: Id (Guid, PK) Topic (string, max 200) - event topic/type Payload (string, JSONB) - JSON event payload PartitionKey (nullable, max 36) - encounter_id for all clinical events CreatedAt (DateTimeOffset, default NOW()) ProcessedAt (nullable DateTimeOffset) - when published to message broker Relationships: None Constraints & Indexes: Filtered index on CreatedAt WHERE processed_at IS NULL (unprocessed events) Configuration: /Data/Configurations/OutboxEventConfiguration.cs (lines 4-20) Pattern: Outbox pattern for event publishing 17. ExternalResourceIdentifier (/Domains/Entities/ExternalResourceIdentifier.cs)

Key Properties: Id (Guid, PK) ResourceType (enum: Patient, Encounter) InternalId (Guid) - ID in this system System (string, max 500) - external system identifier (e.g., FHIR system URL) Value (string, max 200) - external resource ID CreatedAt (DateTimeOffset, default NOW()) Relationships: None (reference mapping only) Constraints & Indexes: Unique composite index: (ResourceType, System, Value) Composite index: (ResourceType, InternalId) Configuration: /Data/Configurations/ExternalResourceIdentifierConfiguration.cs (lines 4-20) Pattern: Maps internal IDs to external FHIR/EHR identifiers ENUMS (22 total) Located in /Domains/Enums/:

Enum Name Values Notes AlertType (53 values) SepsisWarning (obsolete), Critical*, Warning*, News2Warning, News2Emergency, RapidDeterioration, QsofaWarning (obsolete), QsofaScreen, GcsCritical, GcsWarning, SofaSepsis, SofaWarning With ToDbString/FromDbString extensions; extensive mapping to observation codes AlertStatus Open, Acknowledged, Resolved, Escalated With ToDbString/FromDbString extensions AlertSeverity Warning, Critical With ToDbString/FromDbString extensions EncounterStatus Scheduled, Active, Discharged, Cancelled With ToDbString/FromDbString extensions EncounterType Inpatient, Outpatient, Emergency With ToDbString/FromDbString extensions Department ICU, GeneralMedicine, Emergency, Cardiology, Surgery, Pediatrics With ToDbString/FromDbString extensions OrderStatus Pending, InProgress, Resulted, Cancelled With ToDbString/FromDbString extensions OrderType Lab, Imaging, Medication, Procedure With ToDbString/FromDbString extensions BloodType APositive, ANegative, BPositive, BNegative, AbPositive, AbNegative, OPositive, ONegative With ToDbString/FromDbString extensions ObservationSource Manual, Device, Lab With ToDbString/FromDbString extensions ClinicalRole Nurse, Physician, Admin, Integration With ToDbString/FromDbString extensions AuditAction ThresholdCreated, ThresholdUpdated, AlertAcknowledged, AlertResolved, EncounterStatusChanged, PatientRegistered, SuppressionWindowSet, UserLogin With ToDbString/FromDbString extensions SepsisBundleComplianceStatus InProgress, Compliant, NonCompliant With ToDbString/FromDbString extensions SepsisBundleElementType BloodCultures, SerumLactate, BroadSpectrumAntibiotics, IvFluidResuscitation With ToDbString/FromDbString extensions SepsisBundleElementStatus Pending, Completed With ToDbString/FromDbString extensions ReconciliationCheckType UnacknowledgedCriticalAlert, PendingOrderNoResult, ActiveInpatientNoObservation With ToDbString/FromDbString extensions News2Outcome NotNews2Code, IncompleteParameters, ScoreComputed No extensions GcsOutcome NotGcsCode, IncompleteComponents, ScoreComputed No extensions SofaOutcome NotSofaTrigger, EncounterNotFound, ScoreComputed No extensions TrendOutcome NotTrendCode, InsufficientHistory, Stable, RapidDeterioration, AlertAlreadyOpen No extensions ExternalResourceType Patient, Encounter With ToDbString/FromDbString extensions SofaValueStatus Current, Stale, Expired No extensions DATABASE CONTEXT File: /Data/AppDbContext.cs (lines 1-29)

DbSets: All 17 entities registered via public properties Configuration: Uses ApplyConfigurationsFromAssembly() pattern with individual IEntityTypeConfiguration implementations Constraints: All enums have database-level CHECK constraints (see configurations) Indexes: Composite, unique, and filtered indexes extensively used for query optimization RELATIONSHIPS SUMMARY

Patient (1) ──←─── (N) Encounter ──←─── (N) Observation │ ├───── (N) ClinicalAlert ├───── (N) Order ──←─── (N) SepsisBundleElement └───── (N) News2Score, SofaScore, GcsScore, MedicationAdministration

SepsisBundle ──→ ClinicalAlert (FK to TriggeringAlert) │ └───← (N) SepsisBundleElement

ReconciliationAlert ──→ Encounter (optional) ──→ Patient (optional)

ExternalResourceIdentifier (standalone reference mapping) ClinicalUser (standalone) ClinicalAuditLog (append-only audit trail) OutboxEvent (event publishing pattern) AlertThreshold (reference data) KEY PATTERNS & DESIGN DECISIONS

  1. Auditing ClinicalAuditLog captures all structural changes (action, entity type, before/after JSON) Append-only; no deletes from application code Tracks: user, IP, correlation ID, timestamp, reason File: /Data/Configurations/ClinicalAuditLogConfiguration.cs (line 24 comment)
  2. Soft Delete No explicit soft delete pattern (no IsDeleted, DeletedAt fields) All deletes use foreign key constraints (Restrict, Cascade, SetNull) ClinicalAuditLog provides audit trail for deleted records
  3. Event Publishing OutboxEvent table implements Outbox pattern for reliable event publishing ProcessedAt field tracks publication status PartitionKey ensures ordering (encounter_id for clinical events) File: /Data/Configurations/OutboxEventConfiguration.cs (lines 4-20)
  4. Idempotency Observation uses IdempotencyKey (unique partial index) for device deduplication File: /Data/Configurations/ObservationConfiguration.cs (line 38-40)
  5. Clinical Scoring SOFA (SofaScore): Full 6-component organ dysfunction scoring with baseline tracking NEWS2 (News2Score): 8-parameter early warning system GCS (GcsScore): Glasgow Coma Scale with neurological classification Each stores component scores + total + calculated timestamp Enables trend analysis and historical comparison
  6. Alert Management 53 AlertType values with legacy obsolete entries marked AlertThreshold supports configurable suppression windows AlertStatus tracks lifecycle: Open → Acknowledged → Resolved/Escalated Separate severity (Warning/Critical) from type
  7. Sepsis Protocol SepsisBundle tracks 3-hour compliance bundle SepsisBundleElement maps to 4 required interventions TriggeringAlertType stored for audit trail (alert type may be obsolete) ComplianceStatus: InProgress → Compliant/NonCompliant
  8. External Integration ExternalResourceIdentifier maps internal IDs to FHIR/external system IDs Supports Patient and Encounter types Unique on (ResourceType, System, Value) for lookup VALIDATION & CONSTRAINTS Entity-Level Database CHECK constraints on all enum fields (see configurations) Column type constraints: varchar max lengths, decimal precision Foreign key constraints with specified DeleteBehavior Application-Level (inferred from usage) MaxLength attributes on string properties Required (!) markers on non-nullable properties Enum conversions via extension methods (ToDbString/FromDbString) MISSING/INCOMPLETE PATTERNS Identified Gaps: No explicit validation attributes (DataAnnotations) visible in entity definitions No soft delete implementation - relies entirely on audit log for record history ObservationId in ClinicalAlert - no foreign key constraint configured, may be loose reference No entity-level optimistic concurrency (Timestamp/RowVersion) - potential conflict in high-concurrency scenarios MedicationAdministration - no audit trail beyond creation; changes to medication records not captured ClinicalUser.PasswordHash - no hash validation/strength enforcement visible ReconciliationAlert - uses optional FKs (encounter/patient can be null), may indicate loose relationships API COVERAGE Controllers with service implementations:

PatientsController, EncountersController, ObservationsController AlertsController, AlertThresholdsController, AuditLogsController OrdersController, MedicationsController News2Controller, SofaController, GcsController, QsofaController SepsisBundlesController FhirIngestController, FhirMetadataController, AnalyticsController, AuthController No explicit controller/service identified for:

ReconciliationAlert (backend-only data quality checks?) OutboxEvent (internal event publishing system) ExternalResourceIdentifier (internal FHIR mapping) FILE LOCATIONS SUMMARY Component Files Entities /Domains/Entities/.cs (17 files) Configurations /Data/Configurations/.cs (17 files) Enums /Domains/Enums/.cs (22 files) DbContext /Data/AppDbContext.cs Controllers /Controllers/.cs (17 files) Services /Services/*.cs (41 files) This is a well-architected clinical domain model with comprehensive relational design, supporting sepsis detection, clinical scoring, alert management, and audit compliance.

Models/entities survey is back too. Still waiting on controllers/API, services/business logic, and infrastructure agents.

Excellent! Now I have enough comprehensive data. Let me create a thorough survey report:

COMPREHENSIVE SERVICE SURVEY: VigilCareClinical Based on my thorough analysis of all service classes and business logic in the project, here's a detailed survey organized by your focus areas:

  1. ALL SERVICE FILES Services Directory (23 services):

AlertService.cs (line 5-203): Alert lifecycle management - acknowledgment, resolution, suppression window resolution ObservationService.cs (line 7-245): Core ingest pipeline - validation, idempotency, critical alert creation, outbox events WarningEvaluator.cs (line 5-152): Warning threshold detection via idempotent SQL INSERT SepsisBundleService.cs (line 4-164): Sepsis bundle creation, element tracking, compliance status tracking QsofaService.cs (line 4-55): qSOFA screening retrieval from Redis cache SofaService.cs (line 3-45): SOFA score history retrieval from database GcsService.cs (line 3-17): GCS score retrieval (minimal wrapper) News2Service.cs (line 3-50): NEWS2 score history with cursor-based pagination MedicationService.cs (line 4-102): Medication administration recording and correlation lookup OrderService.cs (line 3-121): Order lifecycle management - Pending → InProgress → Resulted → Cancelled PatientService.cs (line 4-202): Patient registration, encounter opening, external identifier linking EncounterService.cs (line 4-298): Encounter status transitions, timeline aggregation, FHIR upsert AlertThresholdService.cs (line 4-120): Threshold CRUD and Redis cache invalidation AlertSuppressionService.cs (line 3-31): Suppression window enforcement via Redis PlausibilityValidator.cs (line 1-49): Static range validation (catches device malfunction) MapCalculator.cs (line 1-6): Mean arterial pressure calculation ExternalIdentifierService.cs (line 3-70): FHIR identifier linking/resolution ObservationQueryService.cs (line 3-59): Keyset pagination for observation history AuditService.cs (line 3-47): Audit log persistence with correlation ID tracking CurrentUserService.cs, AuthService.cs, AnalyticsService.cs: Supporting services Clinical Scoring Directories:

Sepsis/ (4 files):

QsofaCalculator.cs (line 8-36): Criteria definition (RR≥22, SBP≤100, AVPU≥1/GCS<15) QsofaDetector.cs (line 6-183): Redis-based criterion tracking, TTL=1800s, screen alert generation AlertCreationGuard.cs (line 1-8): Prevents deprecated SEPSIS_WARNING type SepsisAlertHandler.cs (line 1-30): Triggers bundle creation on SOFA_SEPSIS alert only Sofa/ (3 files):

SofaCalculator.cs (line 1-160): 6 organ systems scoring (Resp, Coag, Liver, CV, CNS, Renal) SofaDetector.cs (line 8-349): Lab caching, baseline resolution, delta computation, alert creation SofaLabCache.cs (line 5-65): Redis caching with TTL, staleness classification Gcs/ (2 files):

GcsCalculator.cs (line 3-60): Total computation, NEWS2/qSOFA mapping GcsDetector.cs (line 6-237): Score persistence, alert creation (severe≤8, moderate≤12), qSOFA re-sync News2/ (2 files):

News2Calculator.cs (line 4-100): 7-parameter scoring with risk levels (HIGH≥7, MEDIUM≥5) News2Detector.cs (line 6-284): GCS-first/AVPU-fallback consciousness resolution, alert creation Medication/ (1 file):

MedicationCorrelationHelper.cs (line 3-39): Annotates alert details with recent vasopressor/relevant drug info Trend/ (2 files):

TrendCalculator.cs (line 3-62): Rate computation, threshold checking (negative for SpO2/SBP) TrendDetector.cs (line 7-152): History tracking in Redis, rapid deterioration alert creation 2. CLINICAL SCORING SYSTEMS - COMPLETENESS ASSESSMENT System Status Notes qSOFA ✓ COMPLETE 3 criteria in Redis, TTL=1800s, screen alert generates but NOT bundle trigger (Phase 27 Step 4) SOFA ✓ COMPLETE 6 organs, baseline resolution, delta tracking, lab staleness flags, triggers bundle on delta≥2 NEWS2 ✓ COMPLETE 7 parameters, GCS-first consciousness, risk stratification (4 levels) GCS ✓ COMPLETE 3 components, severity classification, CNS score mapping, qSOFA/NEWS2 re-sync Key Clinical Logic:

Line 69 (QsofaDetector): if (activeCount < 2) screen fires at ≥2 criteria Line 178-189 (SofaDetector): Delta ≥2 → Critical SOFA_SEPSIS; Delta=1 → Warning SOFA_WARNING Line 111-122 (News2Detector): Risk levels assigned via DetermineRiskLevel(totalScore, hasSingleParamThree) Line 26-29 (GcsCalculator): Severity mapping (≤8=SEVERE, ≤12=MODERATE, ≥13=MILD) 3. ALERT CREATION, LIFECYCLE, AND NOTIFICATION PIPELINE Alert Lifecycle States: Open → Acknowledged → Resolved | Open → Escalated (via RabbitMQ timer, Phase 6)

Alert Creation Paths:

Critical Threshold Breach (Line 104-117 ObservationService):

Synchronous in ingest transaction Outbox event alert.generated for relay Database exception → rollback handled at line 174-184 Warning Threshold Breach (WarningEvaluator, Line 74-143):

Async via Kafka consumer WarningAlertService Idempotent SQL INSERT with WHERE NOT EXISTS (Line 97-108) Transaction-scoped with rollback on affected == 0 qSOFA Screen (QsofaDetector, Line 93-171):

Fires at ≥2 active criteria Does NOT trigger bundle per Line 169 comment Idempotent WHERE NOT EXISTS insertion SOFA Sepsis/Warning (SofaDetector, Line 267-348):

Delta-driven: delta≥2 → CRITICAL, delta=1 → WARNING Calls SepsisAlertHandler.OnSepsisAlertCreatedAsync() for SOFA_SEPSIS only (Line 338-342) Creates bundle on success NEWS2 (News2Detector, Line 186-275):

Risk-driven: HIGH → CRITICAL, MEDIUM/LOW_MEDIUM → WARNING Suppression check for warnings (Line 193-200) GCS/Trend (GcsDetector, TrendDetector):

GCS: severe/moderate thresholds Trend: rate-of-change for 5 vital signs Suppression Pipeline:

Line 17-134 (AlertService.AcknowledgeAsync): Sets suppression window in Redis on acknowledgment Line 40 (WarningEvaluator): Checks suppression before creating alert Line 39-45 (AlertSuppressionService): Redis-based TTL enforcement Outbox/Notification:

Line 108-122 (AlertService): Alert acknowledged event → Kafka → RabbitMQ timer cancellation Line 120-133 (ObservationService): alert.generated outbox event Line 314-333 (SofaDetector): alert.generated for SOFA alerts 4. SEPSIS DETECTION AND BUNDLE COMPLIANCE WORKFLOW Sepsis Detection:

qSOFA (≥2 criteria) → Screen alert (does NOT create bundle) SOFA delta≥2 → SOFA_SEPSIS alert → Bundle creation (Line 17-87 SepsisBundleService) Bundle Lifecycle (SepsisBundleService.cs):

Creation (Line 17-87): Triggered ONLY by SOFA_SEPSIS alert type Elements (Line 43-63): 4 required elements (Blood cultures, Serum lactate, Antibiotics, IV fluid) Deadline (Line 39): recognizedAt.AddHours(1) - HARDCODED TO 1 HOUR Completion Tracking (Line 114-162): OnOrderResultedAsync monitors element completion Compliance Status (Line 134-136): Compliant if all elements complete ≤ deadline NonCompliant if any element incomplete after deadline Bundle Monitoring (SepsisBundleMonitorService, Line 4-75):

Scan Interval: HARDCODED TO 5 MINUTES (Line 5) Marks overdue bundles as NON_COMPLIANT Logs incomplete element count Issues Found:

No explicit transaction boundary around bundle creation + order creation No retry logic if order service fails Deadline hardcoded to 1 hour (configurable via SepsisOptions?) 5. OBSERVATION INGEST PIPELINE Validation → Storage → Scoring Pipeline:

  1. VALIDATION (Lines 28-62): ✓ Encounter exists and is Active ✓ Idempotency key pre-check (avoids exception path) ✓ Plausibility check (PlausibilityValidator.IsPlausible) ✓ Threshold exists (falls back Redis→PostgreSQL)

  2. TRANSACTION BOUNDARY (Line 69): BEGIN TRANSACTION

  3. OBSERVATION INSERT (Line 73-85):

    • IdempotencyKey for deduplication
    • RecordedAt provided by client
  4. CRITICAL ALERT CREATION (Line 98-133): Synchronous if threshold breached

    • Outbox event for relay
  5. OUTBOX EVENTS (Line 120-149):

    • alert.generated (if critical)
    • observation.recorded (always - for async consumers)
  6. COMMIT (Line 150-152): COMMIT TRANSACTION

  7. RACE CONDITION HANDLING (Line 174-184): Catch DbUpdateException → unique violation → rollback → retry SELECT to return existing row

  8. ASYNC CONSUMERS (Kafka):

    • WarningAlertService: threshold evaluation
    • SepsisEngineService: qSOFA processing
    • News2ScoringService: NEWS2 calculation
    • GcsScoringService: GCS calculation
    • SofaScoringService: SOFA rescoring
    • TrendAnalyzerService: rapid deterioration Database-Level Protection:

Line 38-40 (ObservationConfiguration): Partial unique index on idempotency_key (only non-NULL values checked) Allows devices without keys to send duplicates (intentional per comment) Error Handling:

Line 174-184: DbUpdateException handling for concurrent duplicates Line 186-190: Generic exception rollback Gaps:

Redis cache miss fallback doesn't log metric (Line 217-219 only debug log) No monitoring on idempotency key collision rate PlausibilityValidator failures not audited 6. TREND DETECTION AND ANALYSIS Trend Code Support:

Line 6-9 (TrendCalculator): HEART_RATE, RESP_RATE, SYSTOLIC_BP, TEMP_C, SPO2 Processing Pipeline:

History Tracking (Line 42-64 TrendDetector):

Append new entry to Redis list Trim to max entries, evict outside window TTL hardcoded (Line 64, via options) Rate Computation (Line 69-72):

Only if ≥2 entries and within window Velocity = (newest - oldest) / deltaMinutes Threshold Detection (Line 76-77):

SPO2/SBP: checks negative rate (decline) Line 49 Others: checks positive rate (rise) HARDCODED THRESHOLDS via options Alert Creation (Line 80-81):

Idempotent SQL INSERT with LIKE pattern match (Line 102-113) UNSAFE: LIKE '%{code}%' could match wrong alerts Issues Found:

Line 111: LIKE {$"%{observationCode}%"} is fragile - "HEART" could match "HEART_RATE" and "HEART_RATE_TREND" Should use exact observation code or JSON field comparison 7. WARNING EVALUATION LOGIC Warning Threshold Detection (WarningEvaluator.cs):

Line 21-49: Check threshold, reject if critical, check suppression, create alert Line 51-57: IsWarningBreach computes from threshold ranges Idempotent Alert Creation (Line 74-143):

Prevents duplicate warning alerts for same encounter+type Unlike critical (one per encounter), warnings can recur if acknowledged/resolved SQL: WHERE NOT EXISTS (... status IN ('OPEN', 'ACKNOWLEDGED')) Key Design:

Line 40: Suppression check BEFORE alert creation attempt Line 87-108: Nested transaction with rollback on affected == 0 Line 134-136: SaveChangesAsync within transaction scope Missing:

No logging of suppression hits vs. attempts No metrics for alert dedupe success rate Medication correlation happens AFTER threshold check (should be in details generation) 8. MEDICATION ADMINISTRATION AND CORRELATION Medication Service (MedicationService.cs):

Line 17-44: CreateAsync validates active encounter, stores administration Line 59-81: GetRecentForEncounterAsync filters by observation code using correlation mapping Correlation Lookup (MedicationCorrelationHelper.cs):

Line 20-38: TryAnnotateDetailsAsync appends medication context to alert details Queries recent administrations within configurable window Returns most recent correlated drug Vasopressor Resolver (SofaVasopressorResolver.cs):

Line 37-53: Caches vasopressor from MedicationAdministration Line 82-89: Normalizes dose to µg/kg/min (standardizes units) Line 55-80: GetActiveVasopressorAsync: Redis first, fallback to DB lookup HARDCODED WINDOW (Line 52): configurable via SofaOptions.VasopressorWindowHours Issues Found:

Line 78 (MedicationService): String comparison is case-insensitive but .ToLower() on stored value only Line 67-69: Drug mapping configuration is external, no validation of codes 9. PATIENT AND ENCOUNTER LIFECYCLE MANAGEMENT Patient Registration (PatientService.cs, Line 20-46):

Auto-generates MRN: MRN-{count+1:D6} HARDCODED PATTERN No validation of FirstName/LastName length Encounter Opening (Line 84-139):

Checks for duplicate active encounter of same type (Line 90-98) Creates outbox event for status change No check for max active encounters per patient (possible resource exhaustion) Encounter Status Transitions (EncounterService.cs, Line 8-14):

Explicit state machine: Scheduled → Active/Cancelled; Active → Discharged/Cancelled Line 139-142: Enforces allowed transitions or throws ConflictException Line 147-151: Discharge event includes diagnosis FHIR Upsert Operations:

PatientService.RegisterOrUpdateByIdentifierAsync (Line 142-195) EncounterService.OpenOrUpdateByIdentifierAsync (Line 214-297) Both use ExternalIdentifierService for linking Issues Found:

Line 197-200 (PatientService): MRN generation race condition - sequential counter without locking Line 231-234 (EncounterService): Status transition within upsert can fail mid-flight without clear recovery No soft-delete support, discharged encounters remain queryable 10. ORDER MANAGEMENT WORKFLOW Order Lifecycle (OrderService.cs):

States: Pending → InProgress/Cancelled → Resulted → (terminal) State Machine (Line 5-11): Explicit allowed transitions Order Creation (Line 22-46):

Validates active encounter Creates order with Pending status and OrderedAt timestamp No idempotency key support Status Transitions (Line 81-98):

TransitionStatusAsync enforces state machine (Line 87) Sets ResultedAt on Resulted transition Result Recording (Line 100-120):

Line 106-110: Checks not already resulted/cancelled Line 117-118: CRITICAL: Calls bundleService.OnOrderResultedAsync(order.Id) This completes bundle elements Sepsis Bundle Integration (SepsisBundleService.OnOrderResultedAsync, Line 114-162):

Line 116-121: Finds element by order ID, marks Completed Line 127-129: Checks if all elements complete Line 134-136: Marks bundle Compliant/NonCompliant based on deadline Issues Found:

No transaction spanning order result → bundle update OnOrderResultedAsync catches silently if element not found (Line 120) No retry if bundle service fails ERROR HANDLING GAPS File Issue Severity ObservationService:204 Redis cache miss only debug logs, no metrics Medium SepsisBundleService:53-63 Order creation not in bundle transaction HIGH SepsisBundleService:120 Silent no-op if element not found Medium WarningEvaluator:110-114 Rollback on affected==0 but no retry logic Low SofaDetector:73-79 Encounter not found only logs warning Medium MedicationService:78 String comparison sensitivity inconsistent Low PatientService:197-200 MRN counter race condition HIGH TrendDetector:102-113 LIKE pattern fragile for alert matching HIGH ExternalIdentifierService:25-37 Linking check not atomic Medium EncounterService:231-234 Nested status transition in upsert Medium CONCURRENCY CONCERNS Alert Creation Race Conditions:

Line 97-108 (WarningEvaluator): WHERE NOT EXISTS protects against duplicate warnings Line 115-126 (QsofaDetector): Same pattern for screen alerts Line 295-305 (SofaDetector): Same pattern for SOFA alerts ✓ All use idempotent SQL INSERT - database handles serialization Observation Ingest Race:

Line 174-184 (ObservationService): Handles concurrent retries via unique index ✓ Pre-check (Line 45-58) avoids common retry path ✓ Partial unique index allows devices without keys Sepsis Bundle Creation:

Line 24-29 (SepsisBundleService): AnyAsync check for existing in-progress bundle Time-of-check-to-time-of-use: two observations arriving simultaneously could both pass check and create bundles Should use WHERE NOT EXISTS in INSERT like other alert types Medication Correlation:

Line 26-27 (MedicationService): Database query has no lock Concurrent medication entry while alert being created could miss correlation SOFA Lab Cache:

Line 21-27 (SofaLabCache): Concurrent stores to same Redis key via MSET/SET ✓ Redis atomicity handles this, but TTL window could be lost if records arrive out-of-order TRANSACTION BOUNDARIES Well-Defined Transactions:

ObservationService.IngestAsync (Line 69-152): Observation + Alert + OutboxEvents in single transaction ✓ WarningEvaluator.TryCreateWarningAlertAsync (Line 86-136): Alert + OutboxEvent ✓ QsofaDetector.TryCreateScreenAlertAsync (Line 105-154): Screen alert + OutboxEvent ✓ SofaDetector (multiple): Score persistence separate from alert creation ✓ TrendDetector.TryCreateAlertAsync (Line 96-144): Trend alert + OutboxEvent ✓ Missing Transaction Boundaries:

SepsisBundleService: Order creation (OrderService) happens OUTSIDE bundle transaction If OrderService fails after bundle created, bundle element references missing orders OrderService.RecordResultAsync → bundleService.OnOrderResultedAsync: No spanning transaction PatientService.RegisterOrUpdateByIdentifierAsync → EncounterService.OpenOrUpdateByIdentifierAsync: Chained async calls HARDCODED VALUES (Should Be Configurable) Value Location Recommendation 1 hour sepsis bundle deadline SepsisBundleService:39 Move to SepsisOptions 5 minute bundle monitor scan SepsisBundleMonitorService:5 Move to SepsisOptions 1800s qSOFA TTL QsofaDetector:8 Move to QsofaOptions 14400s (4h) GCS/NEWS2 TTL GcsDetector:8, News2Detector:8 Move to VitalSignsOptions MRN pattern MRN-{count:D6} PatientService:200 Move to PatientOptions Vasopressor window hours SofaVasopressorResolver:52 ✓ Already in SofaOptions Trend history max entries/TTL TrendDetector:58, Line 64 ✓ In TrendDetectionOptions Medication correlation window MedicationCorrelationHelper:27 ✓ In MedicationCorrelationOptions KEY ARCHITECTURAL OBSERVATIONS Strong Points:

✓ Observation ingest is robust with idempotency and transaction safety ✓ Alert creation uses idempotent SQL patterns consistently ✓ Outbox pattern correctly implements event sourcing ✓ Sepsis bundle compliance tracking is auditable ✓ SOFA scoring includes staleness metadata for clinical context Weaknesses:

Sepsis bundle creation race condition (concurrent observations) MRN generation race condition (no locking on counter) Medication correlation happens too late (after alert created) Trend alert matching via LIKE is fragile Order→Bundle integration lacks spanning transaction No monitoring on idempotency collisions or suppression effectiveness Exception handling in background services only logs and delays (no dead-letter queue) FHIR upsert operations chain multiple writes without compensation logic SPECIFIC FILE PATHS FOR KEY FINDINGS Sepsis Bundle Race Condition: /VigilCareClinicalAPI/Services/SepsisBundleService.cs Line 24-29 MRN Race Condition: /VigilCareClinicalAPI/Services/PatientService.cs Line 197-200 Trend Alert Fragile Matching: /VigilCareClinicalAPI/Trend/TrendDetector.cs Line 102-113 Observation Idempotency Index: /VigilCareClinicalAPI/Data/Configurations/ObservationConfiguration.cs Line 36-40 Alert Deduplication Patterns: /VigilCareClinicalAPI/Services/WarningEvaluator.cs Line 97-108 Outbox Event Relay: /VigilCareClinicalAPI/BackgroundServices/OutboxRelayService.cs Line 58-139 Sepsis Bundle Monitoring: /VigilCareClinicalAPI/BackgroundServices/SepsisBundleMonitorService.cs Line 40-74 SOFA Lab Staleness: /VigilCareClinicalAPI/Sofa/SofaDetector.cs Line 82-154 Clinical Scoring Mapping: /VigilCareClinicalAPI/Gcs/GcsCalculator.cs Line 41-59 (NEWS2/qSOFA mappings) This completes the thorough service survey as requested.

Services/business logic survey is complete. Still waiting on the controllers/API and infrastructure agents.

Let me now compile a comprehensive analysis. Let me create a final summary with specific line numbers:

Based on my thorough examination of the VigilCareClinical API controllers, here is a comprehensive survey:

CONTROLLER INVENTORY Total Controllers: 17

GcsController AlertThresholdsController SepsisBundlesController FhirIngestController EncountersController AuditLogsController MedicationsController SofaController OrdersController News2Controller FhirMetadataController QsofaController AlertsController ObservationsController AnalyticsController PatientsController AuthController CRITICAL FINDINGS

  1. MISSING DELETE ENDPOINTS (No HttpDelete Methods) Severity: HIGH - All controllers claim CRUD but none support DELETE operations

AlertThresholdsController: Has C(reate), R(ead), U(pdate) but no DELETE MedicationsController: No delete endpoint OrdersController: No delete endpoint Other controllers: No delete operations defined Path: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Controllers/AlertThresholdsController.cs (line 8 claims "CRUD")

  1. MISSING INPUT VALIDATORS Severity: MEDIUM - Four request types lack corresponding validators:

TransitionStatusRequest (used in EncountersController)

Path: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Models/Records/Encounter/TransitionStatusRequest.cs Used at: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Controllers/EncountersController.cs (line 98) Missing validator for DischargeDiagnosis length validation RecordOrderResultRequest (used in OrdersController)

Path: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Models/Records/Order/RecordOrderResultRequest.cs (line 1) Used at: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Controllers/OrdersController.cs (line 103) No validator exists FhirPatientUpsertRequest (used in FhirIngestController)

Path: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Models/Records/Fhir/FhirPatientUpsertRequest.cs Used at: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Controllers/FhirIngestController.cs (line 75) FhirEncounterUpsertRequest (used in FhirIngestController)

Path: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Models/Records/Fhir/FhirEncounterUpsertRequest.cs Used at: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Controllers/FhirIngestController.cs (line 100) Existing validators (9 total):

AlertThresholdRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/AlertThresholdRequestValidator.cs (thorough threshold ordering validation) AcknowledgeAlertRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/AcknowledgeAlertRequestValidator.cs RegisterPatientRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/RegisterPatientRequestValidator.cs OpenEncounterRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/OpenEncounterRequestValidator.cs IngestObservationRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/IngestObservationRequestValidator.cs TransitionOrderStatusRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/TransitionOrderStatusRequestValidator.cs CreateOrderRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/CreateOrderRequestValidator.cs LoginRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/LoginRequestValidator.cs CreateMedicationAdministrationRequestValidator: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Validators/CreateMedicationAdministrationRequestValidator.cs 3. INCONSISTENT RESPONSE PATTERNS FOR FHIR RESOURCES Severity: MEDIUM - FHIR endpoints use different serialization than rest of API

FhirMetadataController returns Content() with raw serialized FHIR JSON:

Path: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Controllers/FhirMetadataController.cs (line 62) Returns: Content(new FhirJsonSerializer()...) FhirIngestController uses Serialize() helper method:

Path: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Controllers/FhirIngestController.cs (lines 201-207) Custom Serialize() method wraps FHIR resources All other controllers use ApiResponse wrapper:

Standard pattern: Ok(ApiResponse.Ok(data)) Consistent across 15 other controllers SofaController inconsistently uses Serialize() method internally:

Path: /home/sevia/Documents/Programming/Git/VigilCareClinical/VigilCareClinicalAPI/Controllers/SofaController.cs (line 62-78) But returns ApiResponse