Laboratory Service — User Stories
Service: laboratory-service Story prefix: LAB-US Last updated: 2026-04-18
Stories
LAB-US-001 — Manage test catalog with LOINC mapping
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab admin creates and maintains test catalog items with optional LOINC codes |
| Epic link | LAB-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:laboratory, type:backend, slice:S0 |
| Components | catalog |
| FR references | FR-LAB-001 |
| Legacy FR refs | FR-LIS-001 |
| Dependencies | cross-service: TERM-US-001 |
User story: As a lab administrator, when I configure the lab system, I want to create test catalog items with LOINC codes, specimen types, and reference ranges so that technologists have consistent test definitions to work from.
Acceptance criteria (Gherkin):
- Given an admin with
lab:adminscope, when they POST a catalog item with valid LOINC code, then the item is created and the LOINC code is validated against terminology-service. - Given an invalid LOINC code, when the admin submits, then the system returns
LAB_LOINC_INVALID422. - Given a duplicate LOINC code for the same tenant, when submitted, then
LAB_CATALOG_DUPLICATE409 is returned.
Technical notes:
POST /v1/laboratory/catalog; terminology-service call for validation- Catalog items with
tenantId=nullare global; tenant-scoped items visible only to their tenant
Definition of Done:
- Unit + integration tests; coverage ≥ 80%; OpenAPI updated; Pact green; event schema registered; telemetry spans added; docs updated.
LAB-US-002 — Create accession from order
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab clerk opens an accession from a ServiceRequest order |
| Epic link | LAB-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:laboratory, type:backend, slice:S0 |
| Components | accessions |
| FR references | FR-LAB-002 |
| Legacy FR refs | FR-LIS-002 |
| Dependencies | cross-service: ORDERS-US-001 |
User story: As a lab clerk, when I receive a lab order, I want to create an accession and generate a printable label with accession number, patient identifiers, and collection timestamp so that the specimen can be tracked.
Acceptance criteria (Gherkin):
- Given a valid
ServiceRequestorder ID, when I create an accession, then a unique accession number is generated and adiag.laboratory.accession.createdevent is published. - Given the same order ID submitted twice with the same Idempotency-Key, when processed, then only one accession is created.
- Given a non-existent order ID, when submitted, then
LAB_ORDER_NOT_FOUND404 is returned.
Technical notes:
POST /v1/laboratory/accessions;Idempotency-Keyrequired;UNIQUE(tenant_id, order_id)constraint- Accession number format:
{tenantShortCode}-{YYYYMMDD}-{seq6}
Definition of Done:
- Unit + integration tests; coverage ≥ 80%; outbox.spec.ts green; OpenAPI updated; docs updated.
LAB-US-003 — Record specimen collection
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab collector records specimen collection details and updates accession status |
| Epic link | LAB-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:laboratory, type:backend, slice:S0 |
| Components | specimens |
| FR references | FR-LAB-003, FR-LAB-004 |
| Legacy FR refs | FR-LIS-003, FR-LIS-004 |
| Dependencies | LAB-US-002 |
User story: As a phlebotomist, when I collect a specimen, I want to record collection time, my identity, and specimen condition so that the chain of custody is documented.
Acceptance criteria (Gherkin):
- Given an accession in
orderedstate, when I record collection, then specimen status becomescollectedand accession transitions tocollected. - Given specimen marked
unsatisfactory, when submitted, then a note is appended and recollection workflow is triggered. - Given an already-canceled accession, when collection is attempted, then
LAB_INVALID_STATE_TRANSITION409 is returned.
Technical notes:
POST /v1/laboratory/accessions/{id}/specimens; state machine enforced in domain layer
Definition of Done:
- Unit + integration tests; state machine all transitions tested; docs updated.
LAB-US-004 — Receive specimen at lab
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab staff confirms specimen receipt and updates status |
| Epic link | LAB-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 2 |
| Labels | service:laboratory, type:backend, slice:S0 |
| Components | specimens |
| FR references | FR-LAB-003 |
| Legacy FR refs | FR-LIS-003 |
| Dependencies | LAB-US-003 |
User story: As a lab receiving technician, when a specimen arrives at the lab, I want to confirm receipt so that the system shows it is ready for processing.
Acceptance criteria (Gherkin):
- Given a collected specimen, when I mark it received, then specimen status becomes
receivedand accession moves toreceived. - Given a specimen not in
collectedstate, when received is attempted, then state transition error is returned.
Technical notes:
POST /v1/laboratory/specimens/{id}/receive
Definition of Done:
- Integration tests; state machine tests; docs updated.
LAB-US-005 — Enter lab result
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab technologist enters analyte result value, unit, and flags |
| Epic link | LAB-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:laboratory, type:backend, slice:S0 |
| Components | results |
| FR references | FR-LAB-005 |
| Legacy FR refs | FR-LIS-005 |
| Dependencies | LAB-US-003 |
User story: As a lab technologist, when I have a result from the analyzer or manual reading, I want to enter the value, unit, reference range, and flags so that the result is captured accurately.
Acceptance criteria (Gherkin):
- Given an in-process accession, when I enter a result with quantity and unit, then the result is saved with status
draftand abnormal flag calculated. - Given a value below critical threshold, when entered, then
criticalFlag=trueis set automatically. - Given a missing unit on a numeric result, when submitted, then
VALIDATION_FAILED400 is returned.
Technical notes:
POST /v1/laboratory/accessions/{id}/results;CriticalValuePolicyEngine.evaluate()called on entry
Definition of Done:
- Unit tests for flag calculation; integration tests; coverage ≥ 80%; docs updated.
LAB-US-006 — Verify lab result
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Technologist verifies entered result, authorizing it for release |
| Epic link | LAB-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:laboratory, type:backend, slice:S0 |
| Components | verification |
| FR references | FR-LAB-006 |
| Legacy FR refs | FR-LIS-006 |
| Dependencies | LAB-US-005 |
User story: As a lab technologist with verification privileges, when I review a draft result, I want to mark it as verified so that it is ready for pathologist sign-off and release.
Acceptance criteria (Gherkin):
- Given a draft result and a user with
svc:laboratory:verifyscope, when verify is called, then result status becomesverifiedandLabResultVerifiedevent is published. - Given a user without
verifyscope, when verify is attempted, then 403 is returned. - Given an already-verified result, when verify is called again, then idempotent 200 returned.
Technical notes:
POST /v1/laboratory/results/{id}/verify; scope guard enforced at controller layer
Definition of Done:
- RBAC test for scope; integration tests; docs updated.
LAB-US-007 — Release results to chart
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Pathologist releases verified results; FHIR Observation and DiagnosticReport are filed |
| Epic link | LAB-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 8 |
| Labels | service:laboratory, type:backend, slice:S0 |
| Components | results, fhir-publish |
| FR references | FR-LAB-006 |
| Legacy FR refs | FR-LIS-006, FR-RES-001 |
| Dependencies | LAB-US-006, cross-service: INTEROP-US-001 |
User story: As a pathologist, when I sign off on verified results, I want them released to the clinical chart so that clinicians can review them.
Acceptance criteria (Gherkin):
- Given all results in
verifiedstate and no unacked critical flags, when release is triggered, then FHIR Observation and DiagnosticReport are created anddiag.laboratory.result.releasedis published. - Given an unacknowledged critical result, when release is attempted, then
LAB_CRITICAL_ACK_REQUIRED422 is returned. - Given FHIR gateway is temporarily unavailable, when release is triggered, then publish is queued in outbox and retried.
Technical notes:
POST /v1/laboratory/accessions/{id}/release; outbox pattern for FHIR publish
Definition of Done:
- Integration test for full release flow; outbox.spec.ts; FHIR conformance test; docs updated.
LAB-US-008 — Correct a released result
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab technologist submits correction to a released result, creating a new version |
| Epic link | LAB-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:laboratory, type:backend, slice:S1 |
| Components | corrections |
| FR references | FR-LAB-006 |
| Legacy FR refs | FR-LIS-006, FR-RES-030 |
| Dependencies | LAB-US-007 |
User story: As a lab technologist, when I identify an error in a released result, I want to submit a correction with a reason so that the chart reflects the correct value and the correction is auditable.
Acceptance criteria (Gherkin):
- Given a released result, when a correction is submitted with reason, then a new result version is created linked to the prior, and the prior result's status becomes
superseded. - Given a correction, when processed, then
LabResultCorrectedevent is published and the chart service receives the updated observation. - Given a correction without a reason, when submitted, then
VALIDATION_FAILED400 is returned.
Technical notes:
POST /v1/laboratory/results/{id}/correct; FHIR Observation updated with statusamended; prior set toentered-in-error
Definition of Done:
- Unit test for correction chain; integration test; docs updated.
LAB-US-009 — Detect and flag critical values
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | System auto-flags results that cross configured critical thresholds |
| Epic link | LAB-EPIC-03 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:laboratory, type:backend, slice:S1 |
| Components | critical-values |
| FR references | FR-LAB-007 |
| Legacy FR refs | FR-LIS-007 |
| Dependencies | LAB-US-005 |
User story: As a patient safety officer, when a result value crosses a critical threshold, I want the system to flag it immediately so that clinicians are alerted before any harm occurs.
Acceptance criteria (Gherkin):
- Given a critical value policy for potassium with low_critical=2.5, when a result of 2.1 is entered, then
criticalFlag=trueis set anddiag.laboratory.critical.triggeredis published. - Given no policy configured for a test, when a result is entered, then no critical flag is set.
- Given a critical flag on a result, when release is attempted without ack, then
LAB_CRITICAL_ACK_REQUIREDblocks release.
Technical notes:
CriticalValuePolicyEngineevaluates onEnterLabResultCommand; event published via outbox
Definition of Done:
- Unit tests for all threshold combinations; integration test; docs updated.
LAB-US-010 — Critical result acknowledgment by clinician
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician acknowledges a critical result, unblocking release |
| Epic link | LAB-EPIC-03 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:laboratory, type:backend, slice:S1 |
| Components | critical-values, acknowledgment |
| FR references | FR-LAB-007 |
| Legacy FR refs | FR-LIS-007, FR-RES-022 |
| Dependencies | LAB-US-009 |
User story: As a responsible clinician, when I receive a critical value alert, I want to acknowledge it with an action type so that the lab can proceed with release and the system records my response.
Acceptance criteria (Gherkin):
- Given a critical flagged result, when I POST an ack with type
reviewed, thenLabResultAcknowledgedevent is published and release is unblocked. - Given the same ack submitted twice, when processed, then idempotent — no duplicate ack created.
Technical notes:
POST /v1/laboratory/results/{id}/acknowledge; ack unblocks release gating
Definition of Done:
- Integration test; idempotency test; docs updated.
LAB-US-011 — Critical result escalation if unacknowledged
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | System escalates critical result alert if not acknowledged within policy window |
| Epic link | LAB-EPIC-03 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:laboratory, type:backend, slice:S1 |
| Components | critical-values, escalation |
| FR references | FR-LAB-007 |
| Legacy FR refs | FR-LIS-007, FR-RES-023 |
| Dependencies | LAB-US-009, cross-service: COMMS-US-001 |
User story: As a patient safety officer, when a critical result is not acknowledged within the configured window, I want the system to escalate the alert to the next responsible role so that no critical value goes unnoticed.
Acceptance criteria (Gherkin):
- Given a published
diag.laboratory.critical.triggeredevent and no ack after 30 minutes, when the escalation timer fires, then a second alert is dispatched to the escalation role. - Given an ack is received before the escalation window, when checked, then no escalation event is published.
Technical notes:
- Escalation managed by communication-service consuming
diag.laboratory.critical.triggered; laboratory-service trackscritical_ack_attempts
Definition of Done:
- E2E test for escalation flow; integration test with communication-service mock; docs updated.
LAB-US-012 — Lab worklist with priority filtering
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab technologist views filtered worklist by status, priority, and bench |
| Epic link | LAB-EPIC-04 |
| Status | To Do |
| Priority | Should |
| Story points | 3 |
| Labels | service:laboratory, type:backend, slice:S1 |
| Components | worklist |
| FR references | FR-LAB-009 |
| Legacy FR refs | FR-LIS-009 |
| Dependencies | LAB-US-002 |
User story: As a lab technologist, when I start my shift, I want to see a prioritized worklist of pending accessions filtered by bench and urgency so that I work on the most critical samples first.
Acceptance criteria (Gherkin):
- Given accessions with mixed priorities, when I query
?priority=stat, then only stat accessions are returned, sorted bycreatedAtascending. - Given worklist query, when executed, then p95 response time is < 2 s.
Technical notes:
GET /v1/laboratory/accessions?status=in-process&priority=stat&bench=chemistry- Indexed on
(tenant_id, status, priority, created_at)
Definition of Done:
- Performance test; integration tests; docs updated.
LAB-US-013 — TAT reporting for accessions
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab supervisor views turnaround time metrics by priority |
| Epic link | LAB-EPIC-04 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:laboratory, type:backend, slice:S1 |
| Components | reporting |
| FR references | FR-LAB-010 |
| Legacy FR refs | FR-LIS-010 |
| Dependencies | LAB-US-007 |
User story: As a lab supervisor, when I review daily operations, I want to see TAT metrics by priority and test type so that I can identify bottlenecks and enforce SLAs.
Acceptance criteria (Gherkin):
- Given released accessions, when TAT report is queried with date range, then median and p95 TAT by priority are returned.
- Given an accession not yet released, when included, then it is excluded from TAT calculation.
Technical notes:
GET /v1/laboratory/reports/tat?from=&to=&priority=; computed fromcreated_attoreleased_at
Definition of Done:
- Integration test; docs updated.
LAB-US-014 — Receive HL7 v2 ORU results from external analyzer
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | External analyzer results ingest via HL7 v2 ORU through interop-service |
| Epic link | LAB-EPIC-05 |
| Status | To Do |
| Priority | Should |
| Story points | 8 |
| Labels | service:laboratory, type:backend, slice:S2 |
| Components | hl7v2-adapter |
| FR references | FR-LAB-008 |
| Legacy FR refs | FR-LIS-001 (interop), FR-INT-006, FR-INT-007 |
| Dependencies | cross-service: INTEROP-US-005 |
User story: As a lab system integrator, when an analyzer sends HL7 v2 ORU results, I want them automatically ingested into the accession so that manual transcription is eliminated.
Acceptance criteria (Gherkin):
- Given a valid ORU^R01 message, when received via interop-service, then results are created in the matching accession.
- Given a duplicate ORU with same message control ID, when processed, then idempotent — no duplicate result.
- Given an ORU with no matching order, when processed, then result is stored as
unlinkedwith patient ID and logged for admin review.
Technical notes:
- interop-service maps ORU → FHIR Observation and POSTs to laboratory-service FHIR endpoint
Definition of Done:
- Integration test with sample ORU fixture; deduplication test; docs updated.
LAB-US-015 — Cancellation of accession with reason
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Lab supervisor cancels an accession with audit trail |
| Epic link | LAB-EPIC-05 |
| Status | To Do |
| Priority | Must |
| Story points | 2 |
| Labels | service:laboratory, type:backend, slice:S0 |
| Components | accessions |
| FR references | FR-LAB-008 |
| Legacy FR refs | FR-LIS-008 |
| Dependencies | LAB-US-002 |
User story: As a lab supervisor, when an accession must be canceled, I want to record a reason so that the audit trail explains why the test was not completed.
Acceptance criteria (Gherkin):
- Given an accession in
orderedorcollectedstate, when canceled with reason, then status becomescanceledandLabAccessionCanceledevent is published. - Given an already-released accession, when cancellation is attempted, then
LAB_ACCESSION_ALREADY_RELEASED409 is returned.
Technical notes:
POST /v1/laboratory/accessions/{id}/cancel; reason required
Definition of Done:
- State machine test; integration test; docs updated.
LAB-US-016 — Clinician result acknowledgment queue
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician views unreviewed and critical results in their inbox |
| Epic link | LAB-EPIC-06 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:laboratory, type:backend, slice:S1 |
| Components | result-acknowledgment |
| FR references | FR-LAB-011 |
| Legacy FR refs | FR-RES-020, FR-RES-021 |
| Dependencies | LAB-US-007 |
User story: As a clinician, when I start my rounds, I want to see a queue of unreviewed and critical results requiring my attention so that I don't miss important findings.
Acceptance criteria (Gherkin):
- Given released results with no ack, when I query the queue, then unreviewed results for my patients are returned sorted by criticality and time.
- Given a result I acknowledged, when I query, then it no longer appears in the unreviewed queue.
- Given a critical result, when listed, then it is visually distinguished in the API response with
criticalFlag: true.
Technical notes:
GET /v1/laboratory/results/queue?assigneeId=&status=unreviewed
Definition of Done:
- Integration tests; docs updated.
LAB-US-017 — Acknowledge lab result
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician records an acknowledgment action on a released result |
| Epic link | LAB-EPIC-06 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:laboratory, type:backend, slice:S1 |
| Components | result-acknowledgment |
| FR references | FR-LAB-011 |
| Legacy FR refs | FR-RES-022 |
| Dependencies | LAB-US-016 |
User story: As a clinician, when I review a result, I want to acknowledge it with a specific action type so that the system knows I have seen it and how I responded.
Acceptance criteria (Gherkin):
- Given a released result, when I POST ack with type
plan-documented, thenLabResultAcknowledgedevent is published and result removed from unreviewed queue. - Given a correction applied after my ack, when processed, then the corrected result re-enters my unreviewed queue.
Technical notes:
POST /v1/laboratory/results/{id}/acknowledge;ack_typevalidated against allowed enum
Definition of Done:
- Integration test; re-ack-after-correction test; docs updated.
LAB-US-018 — Trending view for analyte over time
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician views time-series trend for a LOINC-coded analyte |
| Epic link | LAB-EPIC-06 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:laboratory, type:backend, slice:S2 |
| Components | trending |
| FR references | FR-LAB-011 |
| Legacy FR refs | FR-RES-011 |
| Dependencies | LAB-US-007 |
User story: As a clinician, when I review a patient's lab history, I want to see a time-series trend for a specific analyte so that I can assess whether the patient's condition is improving or deteriorating.
Acceptance criteria (Gherkin):
- Given 12 months of released potassium results for a patient, when I query trend for LOINC
2823-3, then a sorted list of{effectiveAt, value, unit, abnormalFlag}is returned. - Given a trend query, when executed, then response time < 2 s p95.
Technical notes:
GET /v1/laboratory/patients/{id}/trend?code=2823-3&from=&to=- Indexes on
(tenant_id, test_code, released_at DESC)support efficient retrieval
Definition of Done:
- Performance test; integration test; docs updated.