Registration Service — User Stories
Service: registration-service Story prefix: REG-US Last updated: 2026-04-17
Stories
REG-US-001 — Register patient with demographics and identifiers
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Register patient with mandatory demographics and multiple identifiers |
| Epic link | REG-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:registration, type:backend, type:api, slice:S0 |
| Components | patients, identifiers |
| FR references | FR-REG-001, FR-REG-002 |
| Legacy FR refs | FR-REG-001, FR-REG-002 (ADMIN-REG v2.1) |
| Dependencies | — |
User story: As a front-desk registrar, when a new patient presents, I want to create a patient record with required demographics and multiple identifiers so that a unique, searchable identity exists in the system.
Acceptance criteria (Gherkin):
- Given tenant required fields are configured, when a create request omits a required field, then the API returns 400
MISSING_MANDATORY_FIELDwith the field name. - Given a valid demographic payload with two identifiers (NID and MRN), when the patient is created, then all identifiers are persisted and returned with correct
systemvalues. - Given a duplicate
system+valueidentifier already exists on another patient, when a create is attempted, then 409IDENTIFIER_ALREADY_ASSIGNEDis returned.
Technical notes:
POST /api/v1/patientswithidentifiers[]- Identifier uniqueness enforced at DB level via unique index
ix_patient_identifiers_tenant_system_value - MRN generated server-side
PAT-{tenantCode}-{timestamp}
Definition of Done:
- Unit + integration tests added; coverage ≥ thresholds in
DEFINITION_OF_DONE.md. - OpenAPI contract updated; Pact consumer tests green.
- Event schema registered; schema conformance test green.
- Telemetry spans/metrics added.
- Documentation updated in relevant 17 docs.
REG-US-002 — Create and transition encounter registration
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Register encounter and transition status through visit lifecycle |
| Epic link | REG-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:registration, type:backend, type:api, slice:S0 |
| Components | encounters |
| FR references | FR-REG-008 |
| Legacy FR refs | FR-REG-008 (ADMIN-REG v2.1) |
| Dependencies | REG-US-001 |
User story: As a front-desk registrar, when a patient arrives, I want to create an encounter and transition its status so that clinical staff have accurate visit context.
Acceptance criteria (Gherkin):
- Given a valid patient and facility, when an encounter is created, then status is
plannedandencounter.registeredevent is emitted. - Given a
plannedencounter, whenPATCH /statusis called witharrived, then status updates andencounter.status-changedis emitted. - Given an encounter in
finishedstate, when a status transition is attempted, then 409INVALID_STATUS_TRANSITIONis returned.
Technical notes:
POST /api/v1/encounters,PATCH /api/v1/encounters/:id/status- Optimistic lock
versionrequired on PATCH
Definition of Done:
- Unit + integration tests added; coverage ≥ thresholds.
- OpenAPI contract updated.
- Event schema conformance test green.
- Telemetry spans added.
REG-US-010 — Duplicate detection feedback on patient create
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Surface MPI duplicate score and block likely duplicates on create |
| Epic link | REG-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:registration, type:backend, slice:S0 |
| Components | MPI |
| FR references | FR-REG-004 |
| Legacy FR refs | FR-REG-004 (ADMIN-REG v2.1) |
| Dependencies | REG-US-001 |
User story: As a registrar, when creating a new patient, I want the system to detect probable duplicates so that I avoid creating split-identity records.
Acceptance criteria (Gherkin):
- Given a patient with same name + DOB already exists, when a create is attempted, then MPI scores the candidate and if score ≥ 85, returns 409
DUPLICATE_DETECTEDwith match list. - Given the create is blocked, when the event is published, then
REGISTRATION.patient.duplicate-detectedincludesmatches[]withid,mrn,score. - Given
isUnidentified=true, when MPI score ≥ 85, then the patient is still created andpatient.unidentified-duplicate-reviewis published instead of a 409.
Technical notes:
- MPI scoring configurable thresholds; default probable = 85
pg_trgm+ birth-date narrowing for candidate set
Definition of Done:
- Unit tests for MPI scoring logic; integration test for 409 path.
- Contract test for
patient.duplicate-detectedschema. - Coverage ≥ 90% on MPI module.
REG-US-011 — Governed patient merge with survivorship rules
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Merge duplicate patients with survivorship rules and audit trail |
| Epic link | REG-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 8 |
| Labels | service:registration, type:backend, slice:S0 |
| Components | merge, audit |
| FR references | FR-REG-005, FR-REG-021 |
| Legacy FR refs | FR-REG-005, FR-REG-021 (ADMIN-REG v2.1) |
| Dependencies | REG-US-010 |
User story: As a supervisor, when duplicate patients are confirmed, I want to merge them with documented survivorship rules so that a single authoritative identity is maintained.
Acceptance criteria (Gherkin):
- Given a supervisor role, when merge is requested, then source is deactivated, identifiers transferred to survivor, NOK
relatedPatientIdrepointed, andpatient.mergedevent emitted. - Given merge action is completed, then audit record includes
actorId,survivorId,sourceId, andmergeReason. - Given
REGISTRATION_UNMERGE_ENABLED=false, when unmerge is requested, then 403UNMERGE_DISABLED_BY_POLICYis returned.
Technical notes:
POST /api/v1/patients/:survivorId/mergePOST /api/v1/patients/:survivorId/unmerge(if enabled)- BR-REG-005/006: provisional/unidentified confirmations required
Definition of Done:
- Integration test covers merge + source deactivation + identifier transfer.
- Mandatory:
test/integration/outbox.integration.spec.tsincludes merge event. - Audit event verified in audit-service contract test.
REG-US-020 — Minimum-necessary patient search with break-glass
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Enforce minimum-necessary criteria on patient search with audited break-glass |
| Epic link | REG-EPIC-03 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:registration, type:backend, type:api, slice:S0 |
| Components | search, audit |
| FR references | FR-REG-003, FR-REG-022 |
| Legacy FR refs | FR-REG-003, FR-REG-022 (ADMIN-REG v2.1) |
| Dependencies | REG-US-001 |
User story: As a registration clerk, when searching for patients, I want minimum-necessary criteria enforced so that broad identity disclosure is prevented.
Acceptance criteria (Gherkin):
- Given only a single-character name query, when search is called without other criteria, then 400
PATIENT_SEARCH_INSUFFICIENT_CRITERIAis returned. - Given a valid MRN, when search is called, then results are returned without additional criteria required.
- Given
X-Ghasi-Break-Glass-Reasonheader present, when search executes, then audit event includesdetail.breakGlass.reason.
Technical notes:
GET /api/v1/patients?q=&birthDate=&...- Strong criterion (patientId, mrn, identifier) bypasses weak-factor check
Definition of Done:
- Unit tests for all criteria combinations.
- Break-glass audit verified in integration test.
REG-US-030 — Multi-script names and preferred language capture
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Capture multi-script names and locale-aware preferred language |
| Epic link | REG-EPIC-04 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:registration, type:backend, slice:S0 |
| Components | names, i18n |
| FR references | FR-REG-009, FR-REG-013, FR-REG-014 |
| Legacy FR refs | FR-REG-009, FR-REG-013, FR-REG-014 (ADMIN-REG v2.1) |
| Dependencies | REG-US-001 |
User story: As a registrar, when capturing a patient's name and language preference, I want multi-script support and RTL text direction hints so that records match local usage.
Acceptance criteria (Gherkin):
- Given a patient with both
fa-afandlatnname rows, when retrieved, then both are returned with correcttextDirection(rtl/ltr). - Given
preferredLang: "fa-af", when set and retrieved, then value is stored and returned. - Given an invalid
preferredLangvalue, when submitted, then 400 is returned.
Technical notes:
names[].script→textDirectionderived server-sidepreferredLangvalidated against allowed language set
Definition of Done:
- Unit tests for direction derivation, language validation.
- OpenAPI schema updated with script + textDirection examples.
REG-US-040 — Record and correct deceased vital status
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Record patient deceased status with RBAC, correction, and audit |
| Epic link | REG-EPIC-05 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:registration, type:backend, slice:S1 |
| Components | vital-status |
| FR references | FR-REG-016 |
| Legacy FR refs | FR-REG-016 (ADMIN-REG v2.1) |
| Dependencies | REG-US-001 |
User story: As an authorized clinician, when recording a patient's death, I want controlled deceased status recording and correction so that the legal identity status is accurate and auditable.
Acceptance criteria (Gherkin):
- Given a physician role with
patient_record:record_vital_status, when deceased is set totruewithdeceasedDateTime, then both fields are persisted andvital-status-changedevent is emitted. - Given a front-desk role (no vital status permission), when the endpoint is called, then 403 is returned.
- Given an existing deceased record is corrected, when
PATCH /vital-statusis called again, thenisCorrection: truein the event and prior state is stored inpatient_vital_status_corrections.
Technical notes:
PATCH /api/v1/patients/:id/vital-status- Requires
patient_record:record_vital_statuscatalog key
Definition of Done:
- Unit tests for authorization check, state change, and correction path.
- Integration test verifies audit record and correction table entry.
REG-US-041 — Provisional and newborn registration workflows
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Register provisional (emergency) and newborn patients with reconciliation paths |
| Epic link | REG-EPIC-05 |
| Status | To Do |
| Priority | Must |
| Story points | 8 |
| Labels | service:registration, type:backend, slice:S1 |
| Components | provisional, newborn, reconciliation |
| FR references | FR-REG-017, FR-REG-018 |
| Legacy FR refs | FR-REG-017, FR-REG-018 (ADMIN-REG v2.1) |
| Dependencies | REG-US-001, REG-US-011 |
User story: As an emergency or maternity registrar, when identity is incomplete, I want provisional and newborn workflows so that care can begin safely before full identity is confirmed.
Acceptance criteria (Gherkin):
- Given
isProvisional: true, when patient is created, then provisional record is created andisProvisionalflag is returned. - Given a provisional patient, when merge is called with
confirmProvisionalMerge: true, then provisional flag is cleared on survivor. - Given
isNewborn: truewithnewbornLinkedPatientIdpointing to mother, then linkage is persisted and returned in patient response.
Technical notes:
POST /api/v1/patientswithisProvisional/isNewbornflags- Reconciliation via
POST /v1/patients/:survivorId/mergewith appropriate confirm flags
Definition of Done:
- Integration tests cover provisional create + reconciliation merge + newborn linkage.
- Unit tests for flag mutual-exclusivity invariant.
REG-US-050 — Unidentified patient intake and reconciliation queue
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Register unidentified patients with enterprise key and SLA reminders |
| Epic link | REG-EPIC-06 |
| Status | To Do |
| Priority | Must |
| Story points | 8 |
| Labels | service:registration, type:backend, slice:S3 |
| Components | unidentified, reconciliation-queue |
| FR references | FR-REG-023 |
| Legacy FR refs | REG-NAT-US-010, REG-NAT-US-011 |
| Dependencies | REG-US-041 |
User story: As an emergency registrar, when an unconscious or unidentified patient arrives, I want to create a minimal record immediately so that care can proceed while a reconciliation queue reminds staff to confirm identity.
Acceptance criteria (Gherkin):
- Given
isUnidentified: truewithintakeContext, when created, thentemporaryEnterpriseKeyis returned andpatient.createdincludes it. - Given
REGISTRATION_UNIDENTIFIED_SLA_DAYS=7, when record created, thenunidentifiedSlaDueAtis set 7 days in future. - Given
GET /v1/patients/unidentified-reconciliation?slaBreachedOnly=true, then only records pastunidentifiedSlaDueAtare returned.
Technical notes:
- Temporary enterprise key format:
UNK-{tenantCode}-{timestamp}-{random8} - MPI score ≥ 85 does NOT block unidentified create — emits
unidentified-duplicate-reviewinstead
Definition of Done:
- Unit tests for enterprise key generation, SLA calculation.
- Integration test for reconciliation queue endpoint.
- Contract test for
unidentified-duplicate-reviewschema.
REG-US-060 — Idempotent patient create for offline-first retries
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Support idempotent patient create for safe offline-first retries |
| Epic link | REG-EPIC-07 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:registration, type:backend, slice:S0 |
| Components | idempotency |
| FR references | NFR-REG-004 |
| Legacy FR refs | NFR-REG-004 (ADMIN-REG v2.1) |
| Dependencies | REG-US-001 |
User story: As a platform engineer, when a mobile client retries a patient create after a network failure, I want idempotent create semantics so that duplicate records are not created.
Acceptance criteria (Gherkin):
- Given a request with
Idempotency-Key: abc123, when the same key is submitted twice within 24h, then the second call returns the same 201 body without creating a duplicate patient. - Given a different
Idempotency-Key, when submitted, then a new patient is created normally.
Technical notes:
- Idempotency key stored in Redis with 24h TTL
- Key scoped by
(tenantId, key)
Definition of Done:
- Integration test for key replay path.
- Integration test for cache expiry (mock time).
- Coverage ≥ 80%.