chore: update readme

This commit is contained in:
voltsrage
2026-06-28 01:59:31 +08:00
parent c26ef22670
commit 68a8fe8af1
+55 -20
View File
@@ -2,7 +2,7 @@
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:** Phases 111 are complete. Phase 7 delivers the digitization workstation UI (`vigilcare-records-web`) with role-specific views for intake, entry, verification, clinical approval, live capture, patient history, and supervisor dashboard. Phase 8 adds Prometheus metrics, supervisor work-queue overview, cursor-paginated batch audit trails, promotion deferral with exponential backoff retry, and a Docker monitoring stack (Prometheus on **9095**, Grafana on **3013**). Phase 9 adds extended demo seed data (10 batches across all types, tracks, and statuses), the E2E verification script (`scripts/run-vigilcare-records-verification-p9.sh`), and clinical scenario documentation ([digitization-workstation-guide.md](docs/digitization-workstation-guide.md)). Phase 10 adds barcode/QR cover sheets for high-volume backfill: generate printable cover pages with encoded batch type, track, optional patient, and optional entry-clerk pre-assignment; barcode-assisted upload auto-creates batches and skips manual classification. Phase 11 adds an HL7 FHIR R4 read-only API for promoted clinical data (`Patient`, `Encounter`, `Observation`) with LOINC code mapping, search bundles with pagination links, `$everything`, and an administrator FHIR Explorer view at `/fhir-explorer`. Post-phase hardening includes health check endpoints, user management APIs, auth rate limiting, document access audit events, batch cancellation, list/queue sorting (`sortBy`/`sortDirection`), unified promotion retry logic, normalized patient deduplication, and assignment-time `IN_ENTRY` transitions. Phase 12 (backend-driven batch-type field requirements) is planned next — see [docs/plans/phase-12-plan.md](docs/plans/phase-12-plan.md). See [Implemented Phases](#implemented-phases) and the [gap analysis](docs/vigilcare-records-gap-analysis.md) for remaining work.
**Implementation status:** Phases 113 are complete. Phase 7 delivers the digitization workstation UI (`vigilcare-records-web`) with role-specific views for intake, entry, verification, clinical approval, live capture, patient history, and supervisor dashboard. Phase 8 adds Prometheus metrics, supervisor work-queue overview, cursor-paginated batch audit trails, promotion deferral with exponential backoff retry, and a Docker monitoring stack (Prometheus on **9095**, Grafana on **3013**). Phase 9 adds extended demo seed data (10 batches across all types, tracks, and statuses), the E2E verification script (`scripts/run-vigilcare-records-verification-p9.sh`), and clinical scenario documentation ([digitization-workstation-guide.md](docs/digitization-workstation-guide.md)). Phase 10 adds barcode/QR cover sheets for high-volume backfill: generate printable cover pages with encoded batch type, track, optional patient, and optional entry-clerk pre-assignment; barcode-assisted upload auto-creates batches and skips manual classification. Phase 11 adds an HL7 FHIR R4 read-only API for promoted clinical data (`Patient`, `Encounter`, `Observation`) with LOINC code mapping, search bundles with pagination links, `$everything`, and an administrator FHIR Explorer view at `/fhir-explorer`. Phase 12 makes the backend the single source of truth for batch-type field requirements — `fieldRequirements` metadata on batch and draft responses drives which entry/verification form sections render (allergies, medications, encounter summary, observations). Phase 13 adds optional OCR-assisted draft pre-fill (`Ocr:Enabled`, Azure Document Intelligence or self-hosted Tesseract), confidence scoring on draft fields, and UI confidence indicators; disabled by default. Post-phase hardening includes health check endpoints, user management APIs, auth rate limiting, document access audit events, batch cancellation, list/queue sorting (`sortBy`/`sortDirection`), unified promotion retry logic, normalized patient deduplication, assignment-time `IN_ENTRY` transitions, API-proxied document streaming for the scan viewer, and CORS for production frontend origins. See [Implemented Phases](#implemented-phases) and the [gap analysis](docs/vigilcare-records-gap-analysis.md) for remaining work.
## Domain Model — How It Maps to a Real Clinical System
@@ -23,7 +23,7 @@ The unit of work for one digitization effort — typically one scanned document
### DraftPatient
Structured patient fields extracted from paper — full name, date of birth, sex, blood type, allergies (JSON), emergency contact, medications (JSON for `MEDICATION_LIST` batches). On approval of a `PATIENT_REGISTRATION` or `ALLERGY_UPDATE` batch, merges into VigilCareClinical's live `Patient` record.
Structured patient fields extracted from paper — full name, date of birth, sex, blood type, allergies, emergency contact, medications. Stored in PostgreSQL as JSON columns (`allergies_json`, `medications_json`); the draft API exposes them as `allergies` and `medications` string arrays on read/write. On approval of a `PATIENT_REGISTRATION` or `ALLERGY_UPDATE` batch, merges into VigilCareClinical's live `Patient` record.
### DraftEncounter
@@ -45,7 +45,7 @@ Append-only audit log entry for every state transition, field-level correction,
## Features
- **Document Upload and Batch Creation** — multipart upload (PDF, JPEG, PNG; max 25 MB) with MinIO storage, SHA-256 integrity hash, presigned GET URLs (15-minute expiry); duplicate detection per patient within 24 hours by SHA-256 (`409 DUPLICATE_DOCUMENT`); cross-patient duplicate scans allowed; optional `supersedesBatchId` creates a correction batch linked to a promoted batch; batch created in `UPLOADED` status with `DigitizationEvent` audit trail; optional `coverSheetCode` looks up a cover sheet barcode, applies encoded batch type/track/patient, redeems the cover sheet on success (`409 COVER_SHEET_ALREADY_USED` on reuse), and auto-assigns the batch when the cover sheet has `assignToUserId` set
- **Document Upload and Batch Creation** — multipart upload (PDF, JPEG, PNG; max 25 MB) with MinIO storage (object key `scans/{year}/{month}/{batchId}/{sha256}.{ext}`), SHA-256 integrity hash, presigned GET URLs (15-minute expiry on batch detail); duplicate detection per patient within 24 hours by SHA-256 (`409 DUPLICATE_DOCUMENT`); cross-patient duplicate scans allowed; optional `supersedesBatchId` creates a correction batch linked to a promoted batch; batch created in `UPLOADED` status with `DigitizationEvent` audit trail; optional `coverSheetCode` looks up a cover sheet barcode, applies encoded batch type/track/patient, redeems the cover sheet on success (`409 COVER_SHEET_ALREADY_USED` on reuse), and auto-assigns the batch when the cover sheet has `assignToUserId` set
- **Cover Sheet System** — `POST /cover-sheets/generate` creates 1100 cover sheets with unique `VCR-CS-{8-hex}` codes encoding batch type, track, optional patient, and optional entry-clerk pre-assignment; `GET /cover-sheets/lookup/{code}` resolves a barcode for intake auto-fill; `GET /cover-sheets` lists sheets with `isUsed`/`patientId` filters; `POST /cover-sheets/{id}/pdf` and `POST /cover-sheets/batch-pdf` produce printable PDFs with QR codes (QRCoder); cover sheets are single-use and linked to the batch they create via `batchId`
- **Batch Assignment** — `PATCH /digitization-batches/:id/assign` assigns an entry clerk with a Redis lock (`SET batch:assign:{id} NX EX 3600`) to prevent double-assignment; transitions `UPLOADED → IN_ENTRY` immediately and writes an `entry_started` audit event; only `UPLOADED` batches can be assigned; `409 BATCH_ALREADY_ASSIGNED` on conflict
- **Batch Cancellation** — `POST /digitization-batches/:id/cancel` (administrator only) permanently cancels batches in `UPLOADED`, `IN_ENTRY`, or `REJECTED` status with a mandatory reason (min 5 characters); releases the Redis assignment lock; `CANCELLED` is terminal
@@ -61,8 +61,10 @@ Append-only audit log entry for every state transition, field-level correction,
- **Work Queues** — `GET /work-queue/verification` (batches in `PENDING_VERIFICATION`); `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); all queue and batch list endpoints support `sortBy` and `sortDirection`; role-restricted access
- **Batch Audit Trail API** — `GET /digitization-batches/:id/events` returns cursor-paginated digitization events with actor username and full name; accessible by administrator, verifier, and clinical approver roles
- **User Directory and Management** — `GET /users?role=` lists active users for batch assignment; administrators can `POST /users` (create), `PATCH /users/:id` (update name, role, active flag), `POST /users/:id/reset-password`, and any authenticated user can `POST /users/me/change-password` with current-password verification
- **Document Access Audit** — `GET /digitization-batches/:id` writes a `document_accessed` digitization event (deduplicated per user within 5 minutes) whenever a presigned scan URL is issued
- **Digitization Workstation UI** — Vue 3 SPA at `vigilcare-records-web`: role-based routing and nav (intake, cover sheets, entry, verification, clinical approval, live capture, patient history, supervisor dashboard, FHIR Explorer); split-pane scan viewer with zoom/pan/rotate; cover sheet management view (generate, list, print PDF); barcode-assisted intake (scan/type cover sheet code to auto-fill batch type, track, patient, then upload); draft entry with auto-save and batch-type-aware fields (allergies, medications, discharge diagnosis); field-level verification checkboxes; clinical approval queue with scan review, approve/reject, and retroactive alert toggle; live capture form for new or existing encounters with attestation and password confirm; patient history timeline with correction chain and audit trail; presigned URL refresh for long sessions; JWT refresh interceptor and toast notifications
- **Batch-Type Field Requirements** — `BatchTypeFieldRequirements` metadata on `GET /digitization-batches/{id}` and `GET /digitization-batches/{id}/draft` tells the workstation which form sections to show per batch type (patient demographics, encounter context, encounter summary fields, observations, allergies, medications); entry and verification forms read this metadata instead of hardcoding batch-type rules
- **Document Access Audit** — `GET /digitization-batches/:id` and `GET /digitization-batches/:id/document` write a `document_accessed` digitization event (deduplicated per user within 5 minutes) whenever a scan is retrieved
- **Digitization Workstation UI** — Vue 3 SPA at `vigilcare-records-web` (dev server port **3028**): role-based routing and nav (intake, cover sheets, entry, verification, clinical approval, live capture, patient history, supervisor dashboard, FHIR Explorer); split-pane scan viewer with zoom/pan/rotate (loads scans via authenticated `GET /digitization-batches/:id/document` blob URLs — avoids cross-origin MinIO iframe issues); cover sheet management view (generate, list, print PDF); barcode-assisted intake (scan/type cover sheet code to auto-fill batch type, track, patient, then upload); draft entry with auto-save and backend-driven field visibility (allergies, medications, discharge diagnosis); optional OCR confidence indicators when Phase 13 OCR is enabled; field-level verification checkboxes; clinical approval queue with scan review, approve/reject, and retroactive alert toggle; live capture form for new or existing encounters with attestation and password confirm; patient history timeline with correction chain and audit trail; JWT refresh interceptor and toast notifications
- **Optional OCR-Assisted Pre-Fill (Phase 13)** — background `OcrProcessingService` polls uploaded batches when `Ocr:Enabled` is true; extracts text via Azure Document Intelligence or self-hosted Tesseract; pre-fills draft patient/encounter/observation fields with per-field confidence scores returned as `ocrConfidence` on the draft payload; entry clerks review and correct — OCR does not skip verification; disabled by default in `appsettings.json`
- **HL7 FHIR R4 Read API** — read-only FHIR endpoints at `/fhir` for promoted clinical data: `GET /fhir/metadata` (anonymous `CapabilityStatement`); authenticated read and search for `Patient`, `Encounter`, and `Observation`; MRN search via `Patient?identifier=`; LOINC bidirectional mapping for observation codes (e.g. `HEART_RATE``8867-4`); vital-signs category search; date filtering on `recordedAt`; `GET /fhir/Patient/{id}/$everything` composite bundle; search bundles with `self`/`next` pagination links; `404` responses as FHIR `OperationOutcome`; `application/fhir+json` content negotiation via `FhirJsonOutputFormatter`
- **FHIR Explorer UI** — administrator-only Vue view at `/fhir-explorer`: browse Patient/Encounter/Observation resources, run FHIR searches, inspect raw JSON, load Patient `$everything`, and open the CapabilityStatement metadata link; dev proxy at `/fhir` → API port 5217
- **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`
@@ -105,7 +107,8 @@ HTTP request
├── CoverSheetService (generate, lookup, redeem, list cover sheets)
├── CoverSheetPdfGenerator (printable PDF with QR codes)
├── FhirService (FHIR R4 read/search/$everything over promoted clinical tables)
├── DocumentStorageService (MinIO upload, SHA-256, presigned URLs)
├── OcrProcessingService + IOcrService (optional Azure/Tesseract draft pre-fill when enabled)
├── DocumentStorageService (MinIO upload, SHA-256, presigned URLs, download stream)
├── PlausibilityValidator (per-code numeric range guard)
├── PostgreSQL (EF Core — writes, keyed reads, clinical schema for live tables)
├── Redis (batch assignment locks)
@@ -161,13 +164,13 @@ HTTP request
VigilCareRecords/
├── VigilCareRecordsAPI/
│ ├── Program.cs # Service registration, middleware, seed on startup
│ ├── appsettings.json # Connection strings, Redis, MinIO, Seq, JWT, site config
│ ├── appsettings.json # Connection strings, Redis, MinIO, Seq, JWT, site config, OCR (disabled by default)
│ ├── Controllers/
│ │ ├── ApprovalController.cs # Batch approval, promotion deferral (202), promotion to live tables
│ │ ├── AuthController.cs # JWT login, token refresh, logout, authenticated user profile
│ │ ├── CoverSheetController.cs # Cover sheet generate, lookup, list, PDF export
│ │ ├── Fhir/ # FHIR R4 read/search controllers (Patient, Encounter, Observation, metadata)
│ │ ├── DigitizationBatchesController.cs # Batch CRUD, document upload, assignment, events, promote
│ │ ├── DigitizationBatchesController.cs # Batch CRUD, document upload/stream, assignment, events, promote
│ │ ├── DraftController.cs # Draft data entry: patient, encounter, observations, submit
│ │ ├── PatientsController.cs # Patient search and digitization history
│ │ ├── UsersController.cs # User directory and admin user management
@@ -189,11 +192,11 @@ VigilCareRecords/
│ │ ├── router/index.ts # Role-based routes and navigation guards
│ │ ├── views/ # Login, Intake, CoverSheets, Entry, Verification, Approval, LiveCapture, PatientHistory, QueueDashboard, FhirExplorer
│ │ ├── components/ # ScanViewer, EntryForm, VerificationForm, BatchList, PatientSearch, AppHeader, ToastContainer, AssignClerkDialog
│ │ ├── composables/ # usePresignedUrl (URL refresh), useToast (notifications)
│ │ ├── composables/ # usePresignedUrl (document blob load), useOcrFieldConfidence, useToast
│ │ └── types/index.ts # TypeScript interfaces matching API response shapes
│ ├── vite.config.ts # Dev server on port 3028; proxies /api and /fhir → localhost:5217
│ └── tailwind.config.js # Clinical color palette and layout component classes
├── VigilCareRecordsAPI.Tests/ # Integration tests (Phases 111)
├── VigilCareRecordsAPI.Tests/ # Integration tests (Phases 113)
├── prometheus.yml # Prometheus scrape config (API on host.docker.internal:5217)
├── docker-compose.yml # PostgreSQL, Redis, Seq, MinIO, Prometheus, Grafana
├── scripts/
@@ -206,7 +209,9 @@ VigilCareRecords/
│ ├── run-vigilcare-records-phase-8-verification.sh # Prometheus metrics, overview, events, retry
│ ├── run-vigilcare-records-verification-p9.sh # E2E workflow + work queue overview smoke test
│ ├── run-vigilcare-records-phase-10-verification.sh # Cover sheets, barcode upload, PDF, auto-assign
── run-vigilcare-records-phase-11-verification.sh # FHIR metadata, read/search, $everything, LOINC mapping
── run-vigilcare-records-phase-11-verification.sh # FHIR metadata, read/search, $everything, LOINC mapping
│ ├── run-vigilcare-records-phase-13-verification.sh # OCR config, ocrConfidence API, optional live OCR polling
│ └── fixtures/test-scan.pdf # Sample PDF for upload verification scripts
└── docs/
├── plans/ # Phase 113 implementation guides
├── digitization-workstation-guide.md # Clinical scenarios and clerk workflow reference
@@ -399,6 +404,8 @@ npm run build # output in dist/
For production deployment where the UI and API are on different origins, add the frontend URL to `Cors:AllowedOrigins` in `appsettings.json` (default: `http://localhost:3028` for local dev).
To enable optional OCR pre-fill (Phase 13), set `Ocr:Enabled` to `true` in `appsettings.json` or via environment variable (`Ocr__Enabled=true`), configure Azure credentials or install Tesseract data (`Ocr:Tesseract:DataPath`, default `/usr/share/tessdata`), then restart the API. Run `./scripts/run-vigilcare-records-phase-13-verification.sh` to verify; set `VIGILCARE_OCR_LIVE=1` for live OCR polling tests.
### Run Tests
```bash
@@ -513,7 +520,8 @@ Error response:
|---|---|---|
| POST | `/digitization-batches` | Upload a scanned document and create a batch (multipart/form-data) |
| GET | `/digitization-batches` | List batches; optional `status`, `batchType`, `assignedTo`, `track` filters; paginated and sortable (`sortBy`, `sortDirection`; default `createdAt desc`) |
| GET | `/digitization-batches/{id}` | Batch detail with presigned document URL (15-minute expiry); audits `document_accessed` |
| GET | `/digitization-batches/{id}` | Batch detail with presigned document URL (15-minute expiry) and `fieldRequirements`; audits `document_accessed` |
| GET | `/digitization-batches/{id}/document` | Stream scanned document (PDF/JPEG/PNG) for in-app viewing; same auth and audit as batch detail |
| PATCH | `/digitization-batches/{id}/assign` | Assign batch to an entry clerk (Redis lock; transitions to `IN_ENTRY`) |
| POST | `/digitization-batches/{id}/cancel` | Cancel batch permanently (administrator only; `UPLOADED`, `IN_ENTRY`, or `REJECTED`) |
@@ -580,7 +588,7 @@ Error response:
| Method | Path | Description |
|---|---|---|
| GET | `/digitization-batches/{id}/draft` | Full draft payload: patient, encounter, observations |
| GET | `/digitization-batches/{id}/draft` | Full draft payload: `fieldRequirements`, `ocrConfidence`, patient, encounter, observations |
| PUT | `/digitization-batches/{id}/draft/patient` | Upsert draft patient demographics |
| PUT | `/digitization-batches/{id}/draft/encounter` | Upsert draft encounter fields |
| POST | `/digitization-batches/{id}/draft/observations` | Add an observation row |
@@ -588,6 +596,32 @@ Error response:
| DELETE | `/digitization-batches/{id}/draft/observations/{obsId}` | Remove an observation from draft |
| POST | `/digitization-batches/{id}/submit-for-verification` | Validate completeness and transition to `PENDING_VERIFICATION` |
**PUT `/digitization-batches/{id}/draft/patient` body:**
| Field | Type | Required | Description |
|---|---|---|---|
| `fullName` | string | no | Patient full name |
| `dateOfBirth` | string (ISO date) | no | Date of birth; omit or send `null` when unknown — do not send empty string |
| `sex` | string | no | `male`, `female`, `other`, or `unknown` |
| `bloodType` | string | no | `A+`, `A-`, `B+`, `B-`, `AB+`, `AB-`, `O+`, `O-` |
| `emergencyContact` | string | no | Emergency contact info |
| `allergies` | string[] | no | Allergy list; omit or `null` when `noKnownAllergies` is true |
| `noKnownAllergies` | bool | no | Explicit NKA flag |
| `medications` | string[] | no | Medication list; omit or `null` when `noActiveMedications` is true |
| `noActiveMedications` | bool | no | Explicit no-medications flag |
**Entry form visibility by batch type (`fieldRequirements` on draft/batch detail):**
| batchType | Allergies section | Medications section | Observations | Encounter summary fields |
|---|---|---|---|---|
| `PATIENT_REGISTRATION` | — | — | — | — |
| `VITALS_SHEET` | — | — | yes | — |
| `LAB_RESULTS` | — | — | yes | — |
| `ALLERGY_UPDATE` | yes | — | — | — |
| `ENCOUNTER_SUMMARY` | — | — | — | yes |
| `MEDICATION_LIST` | — | yes | — | — |
| `MIXED` | yes | yes | yes | yes |
**Observation request body:**
| Field | Type | Required | Description |
@@ -606,7 +640,7 @@ Error response:
| `ENCOUNTER_SUMMARY` | Linked patient; encounter with admission date, department, admission reason |
| `VITALS_SHEET` | Linked patient, encounter context, at least one observation with `recordedAt` |
| `LAB_RESULTS` | Linked patient, encounter, at least one lab observation code, `recordedAt` (correction batches with `supersedesBatchId` require observations only — patient and encounter are inherited) |
| `MEDICATION_LIST` | Linked patient; `medicationsJson` with at least one entry or explicit `noActiveMedications: true` |
| `MEDICATION_LIST` | Linked patient; medications list with at least one entry or explicit `noActiveMedications: true` |
| `ALLERGY_UPDATE` | Linked patient, allergies list (may be empty with explicit `noKnownAllergies: true`) |
| `MIXED` | Linked patient, encounter context, and at least one of: observation with `recordedAt`, or complete encounter summary |
@@ -953,9 +987,9 @@ dateOfBirth DateOnly? required for patient_registration on submit
sex string? required for patient_registration on submit
bloodType string? A+ | A- | B+ | B- | AB+ | AB- | O+ | O-
emergencyContact string?
allergiesJson string? JSON array
allergiesJson string? JSON array (DB column; API read/write uses `allergies` string[])
noKnownAllergies bool
medicationsJson string? JSON array (for medication_list batches)
medicationsJson string? JSON array (DB column; API read/write uses `medications` string[])
noActiveMedications bool
createdAt DateTimeOffset
updatedAt DateTimeOffset
@@ -1239,7 +1273,7 @@ Response shape:
## Implemented Phases
Phases 111 are fully implemented and verified via integration tests and per-phase scripts. Post-phase hardening (health checks, user management, promotion retry unification, patient dedup normalization, assignment-time status transitions, document access audit, batch cancellation) is also in place. Phase 12 (backend-driven batch-type field requirements for entry/verification forms) is planned — see [docs/plans/phase-12-plan.md](docs/plans/phase-12-plan.md). See [docs/vigilcare-records-gap-analysis.md](docs/vigilcare-records-gap-analysis.md) for the remaining backlog.
Phases 113 are fully implemented and verified via integration tests and per-phase scripts. Post-phase hardening (health checks, user management, promotion retry unification, patient dedup normalization, assignment-time status transitions, document access audit, batch cancellation, API-proxied document streaming) is also in place. See [docs/vigilcare-records-gap-analysis.md](docs/vigilcare-records-gap-analysis.md) for the remaining backlog.
| Phase | Feature | Status |
|---|---|---|
@@ -1249,10 +1283,11 @@ Phases 111 are fully implemented and verified via integration tests and per-p
| 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 normalized 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 (`vigilcare-records-web`): Vue 3 + Pinia + Tailwind, role-based routing and nav, split-pane scan viewer, batch-type-aware draft entry (allergies, medications, discharge diagnosis), verification checkboxes, clinical approval view, live capture view (new/existing encounter), patient history timeline, patient search, assign-clerk dialog, presigned URL refresh, supervisor dashboard, toast notifications | Done |
| 7 | Digitization workstation UI (`vigilcare-records-web`): Vue 3 + Pinia + Tailwind, role-based routing and nav, split-pane scan viewer (API-proxied document stream), backend-driven draft entry field visibility, verification checkboxes, clinical approval view, live capture view (new/existing encounter), patient history timeline, patient search, assign-clerk dialog, supervisor dashboard, toast notifications | Done |
| 8 | Prometheus metrics (`GET /metrics`), custom digitization gauges/histograms/counters, `MetricsCollectorService`, supervisor `GET /work-queue/overview`, cursor-paginated `GET /digitization-batches/:id/events`, promotion deferral (202 / `PROMOTION_DEFERRED`), `PromotionRetryService` with exponential backoff, Docker Prometheus (9095) + Grafana (3013), Phase 8 verification script | Done |
| 9 | Extended seed data (10 demo batches across all types/tracks/statuses), E2E verification script (`run-vigilcare-records-verification-p9.sh`), clinical scenario docs (`docs/digitization-workstation-guide.md`), `UserManagementTests` | Done |
| 10 | Barcode/QR cover sheet system: `CoverSheet` entity, generate/lookup/list/PDF APIs, `coverSheetCode` on batch upload with redeem and auto-assign, printable PDF with QRCoder, `/cover-sheets` and barcode-assisted `/intake` UI views, `CoverSheetBatchTests`, `CoverSheetPdfTests`, Phase 10 verification script | Done |
| 11 | HL7 FHIR R4 read-only API: `FhirService`, Patient/Encounter/Observation mappers with LOINC mapping, read/search/`$everything` controllers, `CapabilityStatement` metadata, bundle pagination links, `FhirJsonOutputFormatter`, `FhirIntegrationTests`, administrator FHIR Explorer UI (`/fhir-explorer`), Phase 11 verification script | Done |
| | Health probes (`/health/live`, `/health/ready`, `/health/startup`), user management CRUD + password endpoints, auth rate limiting, document access audit, batch cancellation, list/queue sorting, unified `ExecutePromotionCoreAsync` for approve + retry promote, assignment-time `IN_ENTRY` transition, CORS policy for production frontend origins | Done |
| 12 | Backend as single source of truth for batch-type field requirements (entry/verification form visibility metadata) | Planned |
| 12 | Backend-driven batch-type field requirements: `BatchTypeFieldRequirements` on batch/draft responses, entry and verification forms consume `fieldRequirements` metadata instead of hardcoded batch-type switches | Done |
| 13 | Optional OCR-assisted draft pre-fill: `OcrProcessingService`, Azure/Tesseract providers, `ocrConfidence` on draft payload, UI confidence indicators, `OcrResult` entity, Phase 13 verification script (`run-vigilcare-records-phase-13-verification.sh`); disabled by default (`Ocr:Enabled=false`) | Done |
| — | Health probes (`/health/live`, `/health/ready`, `/health/startup`), user management CRUD + password endpoints, auth rate limiting, document access audit, batch cancellation, list/queue sorting, unified `ExecutePromotionCoreAsync` for approve + retry promote, assignment-time `IN_ENTRY` transition, CORS policy for production frontend origins, `GET /digitization-batches/:id/document` scan streaming for workstation viewer | Done |