feature: Digitization Workstation UI

This commit is contained in:
voltsrage
2026-06-27 12:15:43 +08:00
parent 88e70b3dbe
commit e22d33b654
55 changed files with 6411 additions and 143 deletions
+122 -138
View File
@@ -1,8 +1,8 @@
# VigilCare Records API
# VigilCare Records
A clinical records intake backend built with ASP.NET Core 8, PostgreSQL, MinIO, and Redis. The domain models the scan-to-approved lifecycle at the center of any paper chart digitization system: scanned document upload, human data entry, dual-human verification with separation of duties, site-configurable clinical approval routing, atomic promotion to live clinical tables, and governed correction via supersession.
A clinical records digitization platform built with ASP.NET Core 8, PostgreSQL, MinIO, Redis, and a Vue 3 workstation UI. The domain models the scan-to-approved lifecycle at the center of any paper chart digitization system: scanned document upload, human data entry, dual-human verification with separation of duties, site-configurable clinical approval routing, atomic promotion to live clinical tables, and governed correction via supersession.
**Implementation status:** Six planned phases are complete through Phase 6 — from schema, authentication, and batch CRUD through draft data entry, verification/rejection with separation of duties, approval with atomic promotion to VigilCareClinical live tables, correction batches that supersede erroneous promoted observations without silent edits, and Track B live capture with clinician attestation and synchronous critical alerting. See [Implemented Phases](#implemented-phases) for the full breakdown.
**Implementation status:** Phases 16 are complete (API core through Track B live capture). Phase 7 delivers the digitization workstation UI (`vigilcare-records-web`). Phase 8 is partially implemented — work queue overview endpoint and supervisor dashboard UI are in place; Prometheus metrics and promotion retry remain planned. See [Implemented Phases](#implemented-phases) for the full breakdown.
## Domain Model — How It Maps to a Real Clinical System
@@ -55,7 +55,10 @@ Append-only audit log entry for every state transition, field-level correction,
- **Promotion Result Query** — `GET /digitization-batches/:id/promotion-result` returns live entity IDs (patient, MRN, encounter, observations) created during promotion
- **Corrections and Supersession** — approved live observations are never silently edited; a correction uploads a new batch with `supersedesBatchId`, goes through the full entry → verify → approve cycle, and on promotion marks the original batch's `live_observations` rows as `is_superseded` (append-only — never deleted); `422 SUPERSEDED_BATCH_NOT_PROMOTED`, `409 BATCH_ALREADY_SUPERSEDED`, and `404 SUPERSEDED_BATCH_NOT_FOUND` guard invalid supersession targets; correction batches inherit patient context and reuse the original promotion encounter
- **Patient Digitization History** — `GET /patients/:id/digitization-history` returns all batches for a patient with correction chain metadata (`isCorrection`, `hasBeenSuperseded`, `supersededByBatchId`), live vs superseded observation counts, summary totals, and per-batch audit trails; `404 PATIENT_HISTORY_NOT_FOUND` when no batches exist for the patient
- **Work Queues** — `GET /work-queue/verification` (batches in `PENDING_VERIFICATION`, sorted by submission time ASC); `GET /work-queue/entry` (batches in `UPLOADED`, `IN_ENTRY`, or `REJECTED`); `GET /work-queue/clinical-approval` (batches in `AWAITING_CLINICAL_APPROVAL`); role-restricted access
- **Patient Registry Search** — `GET /patients/search?q=` searches live patients by MRN or full name (minimum 2 characters); used by the intake workstation to link uploads to existing patients
- **Work Queues** — `GET /work-queue/verification` (batches in `PENDING_VERIFICATION`, sorted by submission time ASC); `GET /work-queue/entry` (batches in `UPLOADED`, `IN_ENTRY`, or `REJECTED`); `GET /work-queue/clinical-approval` (batches in `AWAITING_CLINICAL_APPROVAL`); `GET /work-queue/overview` (aggregate status counts, average queue age, reject rate, oldest pending verification — administrator only); role-restricted access
- **User Directory** — `GET /users?role=` lists active users for batch assignment (intake clerks assign entry clerks via the workstation UI)
- **Digitization Workstation UI** — Vue 3 SPA at `vigilcare-records-web`: role-based routing (intake, entry, verification, supervisor dashboard), split-pane scan viewer with zoom/pan/rotate, draft entry with auto-save, field-level verification checkboxes, presigned URL refresh for long sessions, JWT refresh interceptor
- **Live Capture (Track B)** — `POST /live-capture/encounters/{encounterId}/observations` and `POST /live-capture/encounters` for credentialed clinicians entering vitals at bedside; clinician attestation + password re-confirm replaces the dual-human verification gate; observations promote synchronously to live tables in a single transaction with `source = live_capture`; critical threshold evaluation runs before the response returns, with inline `criticalAlert` per observation, committed `ClinicalAlert` rows, and `observation.recorded` / `alert.generated` outbox events; `422 ATTESTATION_REQUIRED`, `422 PASSWORD_CONFIRM_INVALID`, `422 EMPTY_OBSERVATIONS`, `409 ENCOUNTER_NOT_ACTIVE`, `409 ACTIVE_ENCOUNTER_EXISTS`
- **Batch Status Machine** — explicit transition matrix enforced in the service layer; illegal transitions return `409`; `PROMOTED` is terminal — corrections require a new batch with `supersedesBatchId`
- **JWT Authentication** — `POST /auth/login` returns access token (15 min) and refresh token (7 days); `POST /auth/refresh` rotates tokens; `POST /auth/logout` revokes server-side; `GET /auth/me` returns authenticated user profile; BCrypt password hashing
@@ -85,7 +88,9 @@ HTTP request
├── DigitizationHistoryService (patient digitization history with correction chain and audit trails)
├── IdempotencyService (Idempotency-Key storage + replay for safe promotion retries)
├── MrnGenerator (PostgreSQL sequence-backed MRN generation: VCR-000001)
├── WorkQueueService (verification, entry, clinical approval queues)
├── WorkQueueService (verification, entry, clinical approval queues, supervisor overview metrics)
├── PatientRegistryService (live patient search by MRN or name)
├── UserDirectoryService (active user listing for batch assignment)
├── AttestationService (clinician role + password re-confirm for live capture)
├── LiveCaptureService (Track B synchronous promotion + critical alert evaluation)
├── DocumentStorageService (MinIO upload, SHA-256, presigned URLs)
@@ -122,6 +127,7 @@ HTTP request
| Layer | Technology |
|---|---|
| Server | ASP.NET Core 8 (.NET 8.0) |
| Frontend | Vue 3, Vite, Pinia, Vue Router, Axios, Tailwind CSS, VueUse |
| Database | PostgreSQL 16 with EF Core 8 (code-first migrations) |
| Cache / locking | Redis 7 (batch assignment locks, alert threshold cache for live capture) |
| Object storage | MinIO (scanned documents — PDF, JPEG, PNG) |
@@ -136,134 +142,50 @@ HTTP request
## Project Structure
```
VigilCareRecordsAPI/
├── Program.cs # Service registration, middleware, seed on startup
├── appsettings.json # Connection strings, Redis, MinIO, Seq, JWT, site config
├── Controllers/
│ ├── ApprovalController.cs # Batch approval and promotion to live clinical tables
│ ├── AuthController.cs # JWT login, token refresh, logout, authenticated user profile
│ ├── DigitizationBatchesController.cs # Batch CRUD, document upload, assignment
│ ├── DraftController.cs # Draft data entry: patient, encounter, observations, submit
│ ├── PatientsController.cs # Patient digitization history with correction chain
│ ├── LiveCaptureController.cs # Track B live capture: record vitals, open encounter + vitals
│ ├── VerificationController.cs # Batch verification and rejection with separation of duties
└── WorkQueueController.cs # Work queues: verification, entry, clinical approval
├── Domain/
├── Entities/
│ ├── Clinical/
├── Patient.cs # Live patient record with MRN (promoted from draft)
│ │ ├── Encounter.cs # Live encounter (promoted from draft)
├── Observation.cs # Live observation with source traceability (batchId, draftObsId)
├── OutboxEvent.cs # Transactional outbox for downstream consumers (eventType, aggregateType, payloadJson)
│ │ │ ├── AlertThreshold.cs # Critical/warning bounds per observation code (live capture alerting)
│ │ ├── ClinicalAlert.cs # Synchronous critical alerts (AlertType, Severity, Details)
│ │ │ └── IdempotencyRecord.cs # Idempotency-Key storage for safe promotion retries
│ │ ├── Draft/
│ │ │ ├── DraftPatient.cs # Structured patient demographics from paper chart
│ │ │ ├── DraftEncounter.cs # Encounter context: admission, department, room/bed
│ │ │ └── DraftObservation.cs # Single measurement: code, value, unit, recordedAt
│ │ ├── DigitizationBatch.cs # Central workflow entity with status machine
│ │ ── LiveEncounter.cs # Live encounter mirror for supersession / history queries
│ ├── LiveObservation.cs # Live observation with supersession flags (is_superseded, superseded_by_batch_id)
│ ├── ScannedDocument.cs # MinIO object reference with SHA-256 integrity hash
│ │ ├── DigitizationEvent.cs # Append-only audit trail for every status transition
│ ├── User.cs # Username, BCrypt hash, full name, role, active flag
│ │ ├── RefreshToken.cs # Opaque refresh token with user FK, expiry, revocation
│ └── AuthAuditEvent.cs # Auth event audit: login, logout, refresh, failed attempts
── Enums/
├── BatchStatus.cs # Uploaded → InEntry → PendingVerification → Verified/AwaitingClinicalApproval → Approved → Promoted
├── BatchType.cs # PatientRegistration, VitalsSheet, LabResults, EncounterSummary, MedicationList, AllergyUpdate, Mixed
├── BatchTrack.cs # Backfill (Track A) or LiveCapture (Track B)
├── UserRole.cs # IntakeClerk, DataEntryClerk, Verifier, ClinicalApprover, Clinician, Administrator
├── DigitizationEventType.cs # 18 event types covering full lifecycle + corrections + promotion retry
│ ├── BloodType.cs # A+, A-, B+, B-, AB+, AB-, O+, O-
├── Department.cs # Clinical departments
├── AlertType.cs # Critical/warning alert types (ported from VigilCareClinical)
── AlertSeverity.cs # WARNING, CRITICAL
│ └── AlertStatus.cs # OPEN, ACKNOWLEDGED, RESOLVED, ESCALATED
├── Services/
│ ├── Interfaces/ # IAuthService, IBatchService, IDraftService, IVerificationService, IWorkQueueService, IDocumentStorageService, IPromotionService, IIdempotencyService, IMrnGenerator, IDigitizationHistoryService, ILiveCaptureService, IAttestationService
│ ├── AuthService.cs # Login (BCrypt verify), JWT generation, refresh rotation, logout, audit logging
│ ├── BatchService.cs # Batch CRUD, status machine transitions, Redis assignment lock, duplicate detection, supersession validation on create
│ ├── DraftService.cs # Draft CRUD with plausibility validation, batch-type completeness on submit (relaxed for correction batches)
│ ├── VerificationService.cs # Verify/reject with separation of duties, site-config clinical approval routing
│ ├── PromotionService.cs # Atomic approve + promote: draft → live tables in single transaction; supersede original observations on correction promotion
│ ├── DigitizationHistoryService.cs # Patient history with correction chain, observation counts, audit trails
│ ├── IdempotencyService.cs # Idempotency-Key record storage + replay (24h TTL)
│ ├── MrnGenerator.cs # PostgreSQL sequence-backed MRN generation (VCR-000001)
│ ├── WorkQueueService.cs # Verification, entry, and clinical approval work queues
│ ├── DocumentStorageService.cs # MinIO upload with SHA-256, presigned URL generation
│ ├── AttestationService.cs # Clinician role + password re-confirm for live capture
│ ├── Interfaces/LiveCaptureService.cs # Track B synchronous promotion + critical alert evaluation
│ └── PlausibilityValidator.cs # Per-code numeric range guard (reused from VigilCareClinical)
├── Configurations/
│ ├── JwtOptions.cs # Issuer, audience, signing key, access/refresh token expiration
│ ├── MinioOptions.cs # Endpoint, credentials, bucket, presigned URL expiry
│ └── SiteConfigOptions.cs # ClinicalApprovalRequired map per batch type
├── Models/Records/
│ ├── Auth/ # LoginRequest, LoginResponse, RefreshRequest, LogoutRequest, TokenResponse, UserProfileResponse
│ ├── Batch/ # CreateBatchForm, CreateBatchRequest, AssignBatchRequest, BatchDetailResponse, SupersessionInfo, PatientDigitizationHistoryResponse, DigitizationHistoryEntry, ...
│ ├── Promotion/ # ApproveRequest, PromotionResultResponse
│ ├── DraftPatient/ # DraftPatientDto, UpsertDraftPatientRequest
│ ├── DraftEncounter/ # DraftEncounterDto, UpsertDraftEncounterRequest
│ ├── DraftObservation/ # DraftObservationDto, CreateDraftObservationRequest, UpdateDraftObservationRequest
│ ├── WorkQueue/ # WorkQueueResponse, WorkQueueItemResponse
│ ├── LiveCapture/ # LiveCaptureResponse, RecordObservationsRequest, ThresholdCacheEntry, ...
│ └── Common/ # PagedResult
├── Data/
│ ├── AppDbContext.cs # EF Core context — entity configs, indexes, constraints
│ ├── Configurations/
│ │ ├── Clinical/ # PatientConfiguration, EncounterConfiguration, ObservationConfiguration, OutboxEventConfiguration, AlertThresholdConfiguration, ClinicalAlertConfiguration, IdempotencyRecordConfiguration
│ │ ├── Draft/ # DraftPatientConfiguration, DraftEncounterConfiguration, DraftObservationConfiguration
│ │ ├── LiveEncounterConfiguration.cs # live_encounters table mapping
│ │ ├── LiveObservationConfiguration.cs # live_observations with supersession columns and partial index
│ │ └── ... # DigitizationBatch, DigitizationEvent, ScannedDocument, User, RefreshToken, AuthAuditEvent configs
│ ├── Seed/
│ │ └── DataSeeder.cs # Seeds 12 demo users (two per role) on startup
│ └── Migrations/ # InitialCreate through AddAlertThresholdsAndClinicalAlerts
├── Common/
│ ├── ApiResponse.cs # { success, statusCode, data, error } envelope
│ └── Exceptions/
│ ├── NotFoundException.cs
│ ├── ConflictException.cs # Status machine, separation of duties, assignment conflicts
│ ├── BadRequestException.cs
│ ├── DomainException.cs
│ └── ValidationException.cs
├── Middleware/
│ ├── CorrelationIdMiddleware.cs # Per-request correlation IDs
│ └── ExceptionHandlerMiddleware.cs # Consistent error responses
└── Infrastructure/
└── OpenApi/
└── SwaggerServiceCollectionExtensions.cs
tests/
└── VigilCareRecordsAPI.Tests/
├── DraftEntryTests.cs # Draft CRUD, plausibility validation, submit-for-verification completeness
├── VerificationTests.cs # Verification, rejection, separation of duties, clinical approval routing
├── PromotionTests.cs # Approval, atomic promotion, idempotency, separation of duties, retroactive alerts, patient dedup
├── CorrectionSupersessionTests.cs # Correction batch supersession, validation guards, patient digitization history
├── LiveCaptureIntegrationTests.cs # Track B attestation, synchronous promotion, critical alerts, open encounter workflow
├── Fixtures/
│ ├── ApiFixture.cs # WebApplicationFactory with PostgreSQL, Redis, MinIO containers
│ └── DatabaseCollection.cs # Shared test collection
└── Helpers/
├── AuthHelper.cs # JWT token generation for test users
├── BatchSeedHelper.cs # Creates seeded batches at various lifecycle stages
├── BatchPipelineHelper.cs # End-to-end batch pipeline: upload → entry → verify → ready for approval
├── CorrectionPipelineHelper.cs # Lab correction pipeline: promote original → promote correction batch
└── DbResetHelper.cs # Database cleanup between tests
scripts/
├── run-vigilcare-records-verification.sh # Phase 1 — schema, auth, roles, batch CRUD, MinIO, status machine
├── run-vigilcare-records-phase-2-verification.sh # Phase 2 — draft entry, plausibility, submit-for-verification
├── run-vigilcare-records-phase-3-verification.sh # Phase 3 — verification, rejection, separation of duties, work queues
├── run-vigilcare-records-phase-4-verification.sh # Phase 4 — approval, atomic promotion, idempotency, patient dedup, outbox events
├── run-vigilcare-records-phase-5-verification.sh # Phase 5 — corrections, supersession, digitization history, audit trail
└── run-vigilcare-records-phase-6-verification.sh # Phase 6 — live capture, attestation, synchronous alerts, outbox events
docs/
├── plans/ # Phase 19 implementation and verification guides
└── vigilcare-records-prd.md # Product requirements and phase roadmap
VigilCareRecords/
├── VigilCareRecordsAPI/
│ ├── Program.cs # Service registration, middleware, seed on startup
│ ├── appsettings.json # Connection strings, Redis, MinIO, Seq, JWT, site config
│ ├── Controllers/
│ ├── ApprovalController.cs # Batch approval and promotion to live clinical tables
│ ├── AuthController.cs # JWT login, token refresh, logout, authenticated user profile
│ ├── DigitizationBatchesController.cs # Batch CRUD, document upload, assignment
│ ├── DraftController.cs # Draft data entry: patient, encounter, observations, submit
│ ├── PatientsController.cs # Patient search and digitization history
│ ├── UsersController.cs # User directory for batch assignment
│ ├── LiveCaptureController.cs # Track B live capture: record vitals, open encounter + vitals
│ │ ├── VerificationController.cs # Batch verification and rejection with separation of duties
│ └── WorkQueueController.cs # Work queues and supervisor overview
├── Domain/ … # Entities, enums (batch, draft, clinical, user)
├── Services/ … # Auth, batch, draft, verification, promotion, work queue, patient registry, user directory, live capture
├── Models/Records/ … # Request/response DTOs
├── Data/ … # EF Core context, configurations, migrations, seed
└── … # Middleware, Common, Infrastructure
├── vigilcare-records-web/ # Vue 3 digitization workstation UI
├── src/
│ │ ├── api/client.ts # Axios client with JWT interceptor and proactive refresh
│ │ ├── stores/ # Pinia: auth (login, roles, routing), batches (CRUD, draft, verify)
│ │ ├── router/index.ts # Role-based routes and navigation guards
│ │ ├── views/ # Login, Intake, Entry, Verification, QueueDashboard
│ │ ├── components/ # ScanViewer, EntryForm, VerificationForm, BatchList, PatientSearch, AppHeader, AssignClerkDialog
│ │ ├── composables/usePresignedUrl.ts # Refreshes presigned document URLs before 15-minute expiry
│ │ ── types/index.ts # TypeScript interfaces matching API response shapes
│ ├── vite.config.ts # Dev server on port 3028; proxies /api → localhost:5217
└── tailwind.config.js # Clinical color palette and layout component classes
├── tests/
└── VigilCareRecordsAPI.Tests/ # Integration tests (Phases 16)
├── scripts/
├── run-vigilcare-records-verification.sh # Phase 1
── run-vigilcare-records-phase-2-verification.sh
├── run-vigilcare-records-phase-3-verification.sh
├── run-vigilcare-records-phase-4-verification.sh
├── run-vigilcare-records-phase-5-verification.sh
├── run-vigilcare-records-phase-6-verification.sh
└── run-vigilcare-records-verification-p9.sh # E2E workflow + work queue overview smoke test
└── docs/
├── plans/ # Phase 19 implementation guides
├── digitization-workstation-guide.md # Clerk workflow and UI reference
── vigilcare-records-prd.md # Product requirements and phase roadmap
```
---
@@ -378,6 +300,7 @@ Approved live observations are never mutated or deleted. When a transcription er
### Prerequisites
- .NET 8 SDK
- Node.js 20+ and npm (for the workstation UI)
- Docker and Docker Compose
### Start Infrastructure
@@ -407,6 +330,34 @@ On startup the application:
Swagger UI is available at `http://localhost:5217/swagger` in Development.
### Run the Workstation UI
With the API running:
```bash
cd vigilcare-records-web
npm install
npm run dev
```
Open `http://localhost:3028`. The Vite dev server proxies `/api` requests to the API on port 5217.
| Username | Password | Default route |
|---|---|---|
| `intake1` | `password` | `/intake` — upload scans, assign entry clerks |
| `entry1` | `password` | `/entry` — data entry queue and split-pane form |
| `verifier1` | `password` | `/verification` — field-level verification |
| `admin1` | `password` | `/dashboard` — supervisor queue overview |
See [docs/digitization-workstation-guide.md](docs/digitization-workstation-guide.md) for the full clerk workflow.
Production build:
```bash
cd vigilcare-records-web
npm run build # output in dist/
```
### Run Tests
```bash
@@ -434,6 +385,7 @@ With the API running (`dotnet run`) and Docker Compose up:
./scripts/run-vigilcare-records-phase-4-verification.sh # Phase 4 — approval, promotion, idempotency, patient dedup
./scripts/run-vigilcare-records-phase-5-verification.sh # Phase 5 — corrections, supersession, digitization history
./scripts/run-vigilcare-records-phase-6-verification.sh # Phase 6 — live capture, attestation, critical alerts
./scripts/run-vigilcare-records-verification-p9.sh # E2E workflow + work queue overview smoke test
```
---
@@ -657,8 +609,40 @@ Error response:
| GET | `/work-queue/verification` | Verifier, Clinical Approver, Administrator | Batches in `PENDING_VERIFICATION`, sorted by submission time |
| GET | `/work-queue/entry` | Data Entry Clerk, Administrator | Batches awaiting or in entry (`UPLOADED`, `IN_ENTRY`, `REJECTED`) |
| GET | `/work-queue/clinical-approval` | Clinical Approver, Administrator | Batches in `AWAITING_CLINICAL_APPROVAL` |
| GET | `/work-queue/overview` | Administrator | Aggregate metrics: status counts, average queue age, 24h reject rate, oldest pending verification |
All work queue endpoints support pagination via `?page=1&pageSize=20`.
All work queue endpoints support pagination via `?page=1&pageSize=20` (except `overview`).
**GET `/work-queue/overview` response:**
| Field | Type | Description |
|---|---|---|
| `statusCounts` | object | Batch count per status (all 8 statuses present) |
| `averageTimeInQueueMinutes` | number | Average age of batches in `PENDING_VERIFICATION` |
| `rejectRate` | number | Rejections / (rejections + verifications) over the last 24 hours (0.01.0) |
| `oldestPendingVerificationMinutes` | number | Age of the oldest batch in `PENDING_VERIFICATION` |
### Patient Registry
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | `/patients/search?q=` | Intake Clerk, Administrator | Search live patients by MRN or full name (min 2 characters); returns up to 20 matches |
**Search result object:**
| Field | Type | Description |
|---|---|---|
| `id` | Guid | Patient ID |
| `fullName` | string | Patient full name |
| `mrn` | string | Medical record number |
### User Directory
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | `/users?role=` | Intake Clerk, Administrator | List active users; optional `role` filter (e.g. `DATA_ENTRY_CLERK`) |
Used by the intake workstation assign-clerk dialog.
### Patient Digitization History
@@ -1034,7 +1018,7 @@ Response shape:
## Implemented Phases
Six phases from the project roadmap are implemented and verified. Integration tests (`dotnet test`) and per-phase verification scripts cover Phases 16.
Phases 16 are fully implemented and verified via integration tests and per-phase scripts. Phase 7 (workstation UI) and parts of Phase 8 (supervisor overview) are implemented.
| Phase | Feature | Status |
|---|---|---|
@@ -1044,6 +1028,6 @@ Six phases from the project roadmap are implemented and verified. Integration te
| 4 | Approval with three-person separation of duties (entry ≠ verifier ≠ approver), atomic promotion to live `patients`/`encounters`/`observations` tables in a single PostgreSQL transaction, PostgreSQL sequence-backed MRN generation (`VCR-NNNNNN`), patient dedup by name + DOB, encounter matching by patient + department + active status, transactional outbox (`observation.created` events), retroactive alert policy per batch, `Idempotency-Key` header with 24h TTL for safe retries, `PromotionTests` integration tests, Phase 4 verification script | Done |
| 5 | Correction batches via `supersedesBatchId`, supersession validation on create (`422`/`404`/`409`), append-only `live_observations` supersession flags (`is_superseded`, `superseded_by_batch_id`, `superseded_at`), correction promotion reuses original encounter and linked patient, `correction_uploaded`/`correction_promoted`/`superseded` audit events, `GET /patients/:id/digitization-history` with correction chain and per-batch audit trails, `CorrectionSupersessionTests` integration tests, Phase 5 verification script | Done |
| 6 | Track B live capture: `LiveCaptureController` with clinician-only endpoints, `AttestationService` (role + password re-confirm), synchronous promotion via `LiveCaptureService`, Redis-backed critical threshold evaluation, inline critical alerts + committed `ClinicalAlert` rows, `observation.recorded` and `alert.generated` outbox events, open-encounter + vitals outpatient workflow, `LiveCaptureIntegrationTests`, Phase 6 verification script | Done |
| 7 | Digitization workstation UI (Vue 3 side-by-side scan viewer + entry form) | Planned |
| 8 | Prometheus metrics, supervisor dashboard, promotion retry job | Planned |
| 9 | Seed data, E2E verification script, clinical scenario documentation | Planned |
| 7 | Digitization workstation UI (`vigilcare-records-web`): Vue 3 + Pinia + Tailwind, role-based routing, split-pane scan viewer, draft entry with auto-save, verification checkboxes, patient search, assign-clerk dialog, presigned URL refresh, supervisor dashboard shell | Done |
| 8 | `GET /work-queue/overview`, supervisor dashboard UI, patient search API, user directory API | Partial — Prometheus metrics and promotion retry job planned |
| 9 | E2E verification script (`run-vigilcare-records-verification-p9.sh`), clinical scenario documentation | Partial |