Patient Chart Service — User Stories
Service: patient-chart-service Story prefix: CHART-US Last updated: 2026-04-18
Stories
CHART-US-001 — Add problem to patient chart
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician adds coded problem to patient problem list |
| Epic link | CHART-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service, terminology-service |
| FR references | FR-CHART-001 |
| Legacy FR refs | FR-PROB-001 |
| Dependencies | cross-service: TERM-US-001 |
User story: As a clinician, when I diagnose a patient, I want to add a coded problem (ICD-10/11 or SNOMED CT) to the problem list so that the condition is part of the permanent longitudinal record and visible to all providers.
Acceptance criteria (Gherkin):
- Given a valid patient ID and an ICD-10 code resolved via terminology-service, when I POST to
/v1/problems, then aProblemis created withclinicalStatus=activeandpatient_chart.problem.added.v1is emitted. - Given an ICD-10 code is not available, when I submit free-text, then the problem is created with
codingPending=trueand acodingTaskflag. - Given the same coded condition is already active, when I attempt to add it again, then
422 CHART_DUPLICATE_PROBLEMis returned.
Technical notes:
- Terminology lookup via
TerminologyClientport. prb_ULID prefix;ProblemHistoryEntrycreated on each status change.
Definition of Done:
- Unit: all state transitions tested. Integration: tenant-isolation, outbox, duplicate-problem.
- FHIR
Conditionread surface verified.
CHART-US-002 — Update problem status (resolve, inactivate)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician resolves or inactivates an existing problem |
| Epic link | CHART-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-002 |
| Legacy FR refs | FR-PROB-002 |
| Dependencies | CHART-US-001 |
User story: As a clinician, when a patient's condition has resolved or is no longer active, I want to update the problem status so that the problem list accurately reflects the current clinical state.
Acceptance criteria (Gherkin):
- Given an active problem, when I PATCH status to
resolvedwith anabatementDate, thenpatient_chart.problem.resolved.v1is emitted. - Given a resolved problem, when I attempt to set
abatementDateon an active problem without changing status, then422is returned. - Given an entered-in-error status, when I attempt to reverse it, then
422 CHART_PROBLEM_IMMUTABLEis returned.
Technical notes:
- Optimistic concurrency via
versionfield. ProblemHistoryEntryappended on each status change.
Definition of Done:
- All six state transitions unit tested; history entry created in integration test.
CHART-US-003 — Mark problem entered-in-error
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician corrects erroneously recorded problem |
| Epic link | CHART-EPIC-01 |
| Status | To Do |
| Priority | Should |
| Story points | 2 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-003 |
| Legacy FR refs | FR-PROB-003 |
| Dependencies | CHART-US-001 |
User story: As a clinician, when a problem was entered in error, I want to mark it as entered-in-error with a reason so that the record is corrected without destroying the history.
Acceptance criteria (Gherkin):
- Given an active or resolved problem, when I mark it entered-in-error with a reason, then
verificationStatus=entered-in-errorandpatient_chart.problem.entered_in_error.v1is emitted. - Given reason is absent, when entered-in-error is attempted, then
422with validation error. - Given the problem is already entered-in-error, when any further mutation is attempted, then
422 CHART_PROBLEM_IMMUTABLE.
Definition of Done:
- Unit test: reason required. Integration test:
entered_in_error.v1emitted.
CHART-US-004 — Record allergy/intolerance
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician records medication or food allergy with reactions |
| Epic link | CHART-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-004 |
| Legacy FR refs | FR-ALG-001 |
| Dependencies | CHART-US-001 |
User story: As a clinician, when a patient reports an allergy or adverse reaction, I want to record the substance, category, reactions (manifestation + severity), and verification status so that the allergy drives safety checks at order entry.
Acceptance criteria (Gherkin):
- Given no active NKA, when I POST a medication allergy with reactions, then
Allergycreated withactivestatus andpatient_chart.allergy.added.v1emitted. - Given an active NKA record, when I attempt to add a substance allergy, then
422 CHART_NKA_CONFLICT. - Given the same coded substance is already active, when I add a duplicate, then
422 CHART_DUPLICATE_ALLERGY.
Technical notes:
alg_ULID prefix.AllergyReactionstored per reaction within the aggregate.- Event consumed by medication-service to refresh allergy-safety-check cache.
Definition of Done:
- NKA conflict, NKDA conflict, duplicate allergy tested in integration.
- Contract test:
allergy.added.v1schema conforms.
CHART-US-005 — Assert No Known Allergies (NKA / NKDA)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician asserts NKA or NKDA status |
| Epic link | CHART-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-004 |
| Legacy FR refs | FR-ALG-002 |
| Dependencies | CHART-US-004 |
User story: As a clinician, when a patient has no known allergies, I want to explicitly assert NKA or NKDA so that the absence of allergies is a documented clinical finding rather than simply missing data.
Acceptance criteria (Gherkin):
- Given no active allergies, when I assert NKA, then an
Allergywithnka=trueis created. - Given an active NKA, when a new substance allergy is added, then NKA is automatically inactivated before the new allergy is created.
- Given an active NKDA, when NKA is asserted, then NKDA is inactivated.
Definition of Done:
- NKDA auto-inactivation on NKA assert tested. Advisory reflects NKA status.
CHART-US-006 — Allergy advisory API
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Sync allergy advisory for medication-service and orders-service |
| Epic link | CHART-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:patient-chart-service, type:api, slice:S0 |
| Components | patient-chart-service, medication-service, orders-service |
| FR references | FR-CHART-005 |
| Legacy FR refs | FR-ALG-003 |
| Dependencies | CHART-US-004, cross-service: MED-US-001, ORDERS-US-001 |
User story: As the medication or orders service, when a prescriber selects a drug, I want to synchronously check the patient's allergy record so that the prescriber is alerted to potential allergic reactions before the order is committed.
Acceptance criteria (Gherkin):
- Given a patient with a penicillin allergy, when orders-service calls
GET /v1/allergies/advisory?patientId=pat_X&rxnorm=7980, then the response includes the matching allergy withhighestSeverity=severe. - Given a patient with active NKA, when advisory is called, then
{ matches: [], isNKA: true }is returned. - Given the patient has no allergies and no NKA, when advisory is called, then
{ matches: [], isNKA: false }.
Technical notes:
- Advisory is synchronous; P95 target < 300 ms.
- Callers implement fail-open; chart service does not block on advisory failure.
Definition of Done:
- Pact consumer test from medication-service and orders-service.
- Advisory integration test with concurrent allergy write.
CHART-US-007 — Inactivate / entered-in-error allergy
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician inactivates or corrects an allergy record |
| Epic link | CHART-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 2 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-006 |
| Legacy FR refs | FR-ALG-004 |
| Dependencies | CHART-US-004 |
User story: As a clinician, when a patient's allergy is no longer clinically relevant or was recorded in error, I want to inactivate or mark it entered-in-error so that it no longer drives safety checks.
Acceptance criteria (Gherkin):
- Given an active allergy, when I PATCH to
inactive, thenpatient_chart.allergy.inactivated.v1emitted; medication-service cache updated. - Given an allergy is entered-in-error, when advisory is queried, then it is excluded from the response.
Definition of Done:
- Medication-service allergy cache refresh tested in integration.
CHART-US-008 — Record vital signs panel
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician records a VitalsSet panel with LOINC-coded observations |
| Epic link | CHART-EPIC-03 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-007 |
| Legacy FR refs | FR-VIT-001 |
| Dependencies | CHART-US-001 |
User story: As a nurse, when I take a patient's vital signs, I want to record the measurements as a panel so that they are stored with LOINC codes and UCUM units and any abnormal values are immediately flagged.
Acceptance criteria (Gherkin):
- Given valid measurement values, when I POST
/v1/vitals, then aVitalsSet+Observationrows are created andpatient_chart.vitals.recorded.v1is emitted. - Given a SpO₂ value of 85 %, when the panel is recorded, then
patient_chart.vitals.abnormal_flagged.v1is emitted withseverity=HIGH. - Given hard-stop range policy for heart rate, when value 300 bpm is submitted, then
422 CHART_VITALS_RANGE_REJECTED. - Given height and weight are present, when the set is recorded, then BMI observation is derived and stored with
derivedFromlinks.
Technical notes:
vit_ULID for VitalsSet;obs_ULID for each Observation.- LOINC code required;
system=localif unmapped.
Definition of Done:
- BMI derivation unit test. Abnormal flag integration test. Range validation tests for warn and reject modes.
CHART-US-009 — Correct vital signs (new version)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician corrects an erroneously recorded vitals set |
| Epic link | CHART-EPIC-03 |
| Status | To Do |
| Priority | Should |
| Story points | 3 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-008 |
| Legacy FR refs | FR-VIT-004 |
| Dependencies | CHART-US-008 |
User story: As a clinician, when vital signs were entered incorrectly, I want to record a correction as a new version of the measurement so that both the original and corrected values are preserved in the audit trail.
Acceptance criteria (Gherkin):
- Given an existing VitalsSet, when I POST a correction with a reason, then a new
VitalsSetversion is created; original is immutable;patient_chart.vitals.updated.v1emitted. - Given no reason is provided, when correction is attempted, then
422validation error.
Definition of Done:
- Original immutability verified in integration test.
CHART-US-010 — Create clinical note draft
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician creates structured note draft with template-defined sections |
| Epic link | CHART-EPIC-04 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-010 |
| Legacy FR refs | FR-NOTES-001 |
| Dependencies | CHART-US-001 |
User story: As a clinician, when I need to document a patient encounter, I want to create a structured draft note using a template (SOAP, H&P, consult, etc.) so that I can organize my documentation in a consistent format.
Acceptance criteria (Gherkin):
- Given a valid templateId and author, when I POST
/v1/clinical-notes, then aClinicalNotewithstatus=draftand template-definedNoteSectionrows is created. - Given the note is in draft, when I PATCH section content, then
patient_chart.note.updated_draft.v1is emitted. - Given an encounter is in
plannedstatus, when I attempt to sign the note, then422is returned.
Technical notes:
note_ULID prefix.- Template definitions fetched from config-service or static registry.
Definition of Done:
- Draft creation and section edit integration tests.
note.created.v1schema conformance.
CHART-US-011 — Sign clinical note
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician signs note creating legal record and PDF in document-service |
| Epic link | CHART-EPIC-04 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service, document-service |
| FR references | FR-CHART-011 |
| Legacy FR refs | FR-NOTES-003 |
| Dependencies | CHART-US-010, cross-service: DOC-US-001 |
User story: As a clinician, when my documentation is complete, I want to sign the note so that it becomes the immutable legal record and a PDF is archived in the document management system.
Acceptance criteria (Gherkin):
- Given a draft note without cosign requirement, when I POST sign, then
status=signed,signedAtandsignedByset atomically,note.signed.v1emitted, PDF uploaded to document-service. - Given cosign policy requires attending attestation, when a resident signs, then
status=pending_cosignandnote.cosign_requested.v1emitted. - Given a signed note, when any section content is edited, then
422 CHART_NOTE_SIGNED_IMMUTABLE.
Technical notes:
- PDF upload via
DocumentServiceClientport → HTTP adapter. - If doc-service unavailable, signing completes; PDF upload retried via outbox.
Definition of Done:
- Immutability integration test. PDF upload retry via outbox tested. Cosign block tested.
CHART-US-012 — AI-assist chunk accept in note
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician accepts AI-suggested content into note section with provenance |
| Epic link | CHART-EPIC-06 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S3 |
| Components | patient-chart-service, ai-gateway-service |
| FR references | FR-CHART-016 |
| Legacy FR refs | FR-NOTES-009 |
| Dependencies | CHART-US-010, cross-service: AIGW-US-001, AIGW-US-009 |
User story: As a clinician, when the AI assistant suggests content for a note section, I want to explicitly accept the suggestion so that the AI-generated text is incorporated with a traceable provenance record linking it to the model and my acceptance action.
Acceptance criteria (Gherkin):
- Given an accepted
AIDecisionfrom ai-gateway-service, when I POSTAcceptAIChunkwithprovenanceId, then section content is updated,NoteAIProvenancerow is written,patient_chart.note.ai_accepted.v1emitted. - Given no
provenanceIdis provided, when AcceptAIChunk is called, then422 CHART_AI_PROVENANCE_MISSING. - Given the note is signed, when AcceptAIChunk is called, then
422 CHART_NOTE_SIGNED_IMMUTABLE.
Technical notes:
NoteAIProvenancewritten atomically with section content update in same DB transaction.note.ai_accepted.v1carriesprovenanceId,featureKey,modelVersion.
Definition of Done:
- AI provenance missing error tested. Pact consumer test against ai-gateway.
CHART-US-013 — Chart summary composition (widgets)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | GET chart summary fans out to owned and remote aggregates |
| Epic link | CHART-EPIC-05 |
| Status | To Do |
| Priority | Must |
| Story points | 8 |
| Labels | service:patient-chart-service, type:backend, slice:S1 |
| Components | patient-chart-service, medication-service, laboratory-service |
| FR references | FR-CHART-013 |
| Legacy FR refs | FR-PCHART-001 |
| Dependencies | CHART-EPIC-01..04, cross-service: MED-EPIC-01, LAB-EPIC-01 |
User story: As a clinician at the bedside, when I open a patient's chart, I want to see a unified summary of problems, allergies, recent vitals, current medications, recent labs, imaging, immunizations, and active care plans in a single view so that I have the full clinical picture without navigating multiple screens.
Acceptance criteria (Gherkin):
- Given a valid patient, when I call
GET /v1/chart/summary?patientId=X&widgets=problems,allergies,vitals,medications, then each requested widget is populated with current data in < 800 ms P95. - Given medication-service is unavailable, when summary is requested, then the medications widget shows an error state and other widgets are still populated.
- Given a different tenant's patientId, when summary is requested, then
403 CHART_CROSS_TENANT_REFERENCE.
Technical notes:
- Fan-out via
Promise.allSettled; partial results served on downstream failure. - Circuit breaker per downstream dependency.
Definition of Done:
- Partial failure scenario integration test. P95 < 800 ms load test.
CHART-US-014 — Chart timeline (cursor-paginated)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Longitudinal timeline with cursor pagination across all chart event types |
| Epic link | CHART-EPIC-05 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S1 |
| Components | patient-chart-service |
| FR references | FR-CHART-014 |
| Legacy FR refs | FR-PCHART-002 |
| Dependencies | CHART-US-013 |
User story: As a clinician, when I review a patient's history, I want to scroll through a reverse-chronological timeline of all chart events (notes, problems, vitals, orders, results) so that I can understand the patient's journey over time.
Acceptance criteria (Gherkin):
- Given a patient with 100 chart events, when I call
GET /v1/chart/timeline?patientId=X&limit=20, then 20 events are returned with anextCursor. - Given a
nextCursor, when I call the timeline with it, then the next 20 events are returned without duplicates. - Given
types=problems,notesfilter, when timeline is called, then only problem and note events appear.
Definition of Done:
- Cursor pagination integration test. Type filter tested.
CHART-US-015 — Chart banner (demographics + alerts)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Chart header banner with demographics proxy and active alerts |
| Epic link | CHART-EPIC-05 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service, registration-service |
| FR references | FR-CHART-015 |
| Legacy FR refs | FR-PCHART-003 |
| Dependencies | CHART-US-001, cross-service: REG-US-001 |
User story: As a clinician, when I open a patient chart, I want to see the patient banner with name, DOB, MRN, active allergy alerts, and current encounter context so that I confirm patient identity and see critical safety information immediately.
Acceptance criteria (Gherkin):
- Given a valid patientId, when I call
GET /v1/chart/banner?patientId=X, then demographics (from registration-service), active allergy count, and encounter context are returned in < 500 ms. - Given registration-service is unavailable, when banner is called, then cached demographics are returned with a
dataStale=trueflag.
Definition of Done:
- Stale-cache fallback integration test. P95 < 500 ms.
CHART-US-016 — Chart snapshot export (handoff)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Export structured handoff snapshot for patient transition |
| Epic link | CHART-EPIC-07 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S2 |
| Components | patient-chart-service, document-service |
| FR references | FR-CHART-020 |
| Legacy FR refs | FR-PCHART-007 |
| Dependencies | CHART-US-013 |
User story: As a discharging physician, when a patient is transferred to another care setting, I want to export a structured handoff snapshot so that the receiving provider has a complete, current clinical picture.
Acceptance criteria (Gherkin):
- Given appropriate permissions, when I POST
/v1/chart/snapshot?patientId=X, then a structured snapshot (active problems, allergies, recent vitals, critical results, active goals, current medications) is returned andchart.snapshot_exported.v1is emitted. - Given the export request, when it is processed, then an
AuditEntryof typeBULK_EXPORTis recorded in audit-service.
Definition of Done:
- Audit event emitted on every snapshot. Permission gate integration test.
CHART-US-017 — Clinical note AI assist request
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician requests AI-assisted note section draft |
| Epic link | CHART-EPIC-06 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S3 |
| Components | patient-chart-service, ai-gateway-service |
| FR references | FR-CHART-017 |
| Legacy FR refs | FR-NOTES-010 |
| Dependencies | CHART-US-010, cross-service: AIGW-US-001 |
User story: As a clinician writing a note, when I want an AI suggestion for a section, I want to trigger AI assist from the note editor so that I receive a draft suggestion that I can review, edit, and accept or discard.
Acceptance criteria (Gherkin):
- Given a draft note and active AI assist feature, when I request assist for a section, then the service calls ai-gateway-service and returns
{ draftText, decisionId, provenanceId }to the client. - Given the AI gateway is unavailable, when assist is requested, then
{ aiAssistAvailable: false }is returned; note authoring continues. - Given HITL is required for the feature, when assist is requested, then
{ status: "under_review", decisionId }is returned; clinician is notified when review completes.
Technical notes:
AIGatewayHttpAdapterimplementsAIGatewayPort.- Assist result is not automatically inserted; clinician must call
AcceptAIChunk.
Definition of Done:
- Graceful degradation when ai-gateway unavailable tested. Pact consumer test against ai-gateway.
CHART-US-018 — Cosign routing and attestation
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Resident note routed for attending cosign before finalising |
| Epic link | CHART-EPIC-04 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service, communication-service |
| FR references | FR-CHART-012 |
| Legacy FR refs | FR-NOTES-006 |
| Dependencies | CHART-US-011, cross-service: COMMS-US-001 |
User story: As an attending physician, when a resident signs a note that requires my attestation, I want to receive a notification and be able to review and cosign the note so that the legal attestation requirements for supervised practice are met.
Acceptance criteria (Gherkin):
- Given cosign policy for the resident's role, when the resident signs, then
status=pending_cosignandnote.cosign_requested.v1emitted; communication-service sends notification. - Given the attending cosigns, when attestation is submitted, then
status=signed,note.cosigned.v1andnote.signed.v1emitted. - Given cosign is rejected, when the attending rejects, then
status=draftand the resident is notified.
Definition of Done:
- Cosign workflow E2E test.
pending_cosignblock on further resign tested.
CHART-US-019 — Break-glass emergency chart access
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician invokes break-glass with reason for restricted chart access |
| Epic link | CHART-EPIC-07 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S2 |
| Components | patient-chart-service, audit-service |
| FR references | FR-CHART-018 |
| Legacy FR refs | FR-PCHART-005 |
| Dependencies | cross-service: AUDIT-US-001 |
User story: As an emergency physician, when a patient requires urgent care but their chart is restricted, I want to invoke break-glass with a documented reason so that I can access the chart to provide emergency care, with my access logged in the audit trail.
Acceptance criteria (Gherkin):
- Given a valid reason and duration, when I POST
/v1/chart/break-glass, then chart access is granted andpatient_chart.breakglass.invoked.v1is emitted (immediately ingested by audit-service). - Given no reason is provided, when break-glass is attempted, then
422 CHART_BREAKGLASS_REASON_MISSING. - Given break-glass was invoked, when I query audit-service, then the entry is present with reason, actor, timestamp, and patientId.
Definition of Done:
- Reason-required integration test. Audit entry verified. Break-glass spike alert configured.
CHART-US-020 — Sensitive segment access control
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Mental health / sexual health records gated by sensitive segment policy |
| Epic link | CHART-EPIC-07 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:patient-chart-service, type:backend, slice:S2 |
| Components | patient-chart-service |
| FR references | FR-CHART-019 |
| Legacy FR refs | FR-PCHART-006 |
| Dependencies | CHART-US-019 |
User story: As a compliance officer, when a clinician accesses a note flagged as a sensitive segment (mental health, sexual health), I want access to be gated by sensitive-segment policy so that patient privacy is protected and all access is logged.
Acceptance criteria (Gherkin):
- Given a note with
sensitiveCategory=mental_healthand no sensitive-segment access, when a clinician without access reads the note, then403 CHART_SENSITIVE_NOT_AUTHORIZED. - Given a clinician with sensitive-segment access, when they read the note, then
patient_chart.sensitive_access.recorded.v1is emitted.
Definition of Done:
- Access denied and access granted both unit tested. Audit event schema conformance.
CHART-US-021 — Append addendum to signed note
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Clinician appends addendum to signed note without modifying original |
| Epic link | CHART-EPIC-04 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-011 |
| Legacy FR refs | FR-NOTES-005 |
| Dependencies | CHART-US-011 |
User story: As a clinician, when I need to add information to a note I have already signed, I want to append an addendum so that the original signed content is preserved immutably and the addition is clearly identified as an amendment.
Acceptance criteria (Gherkin):
- Given a signed note, when I POST an addendum with body + reason, then
NoteAddendumis created,status=amended,patient_chart.note.addendum_created.v1emitted. - Given a signed note, when I attempt to edit section body directly, then
422 CHART_NOTE_SIGNED_IMMUTABLE.
Definition of Done:
- Original content immutability unit tested. Multiple addendum chain tested.
CHART-US-022 — Five-module data migration script
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | ETL scripts migrate legacy module data to unified patient_chart schema |
| Epic link | CHART-EPIC-08 |
| Status | To Do |
| Priority | Must |
| Story points | 8 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service, platform-eng |
| FR references | FR-CHART-021 |
| Legacy FR refs | — |
| Dependencies | CHART-EPIC-01..04 |
User story:
As a platform engineer, when the legacy five-module deployment needs to be consolidated, I want idempotent ETL scripts that migrate all historical data to the unified patient_chart schema so that no clinical records are lost or corrupted.
Acceptance criteria (Gherkin):
- Given a legacy
problems.*schema, when the migration script runs, then all rows are present inpatient_chart.problemwith ULID-mapped ids; row counts match. - Given the script is run twice, when it completes, then no duplicate rows are created (idempotent).
- Given a migration failure mid-run, when the script exits, then no partial data is committed (transactional rollback).
Technical notes:
legacy_id_maptable tracks(module, legacy_id) → new_ulid.- Each module migration is a separate script; run order: problems → allergies → vitals → notes → chart.
- Dry-run mode (no DB writes) required.
Definition of Done:
- Row count reconciliation verified in staging. Idempotency test. Dry-run mode working.
CHART-US-023 — Dual-publish NATS subjects during migration
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Service dual-publishes legacy and canonical NATS subjects during M0→M1 transition |
| Epic link | CHART-EPIC-08 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:patient-chart-service, type:backend, slice:S0 |
| Components | patient-chart-service |
| FR references | FR-CHART-021 |
| Legacy FR refs | — |
| Dependencies | CHART-US-022 |
User story: As a platform engineer, during the migration transition period, I want the service to publish events on both legacy and canonical NATS subjects so that downstream consumers can cut over at their own pace without service interruption.
Acceptance criteria (Gherkin):
- Given dual-publish mode enabled via feature flag, when a problem is added, then both
PROBLEMS.PROBLEM.ADDEDandpatient_chart.problem.added.v1are published. - Given dual-publish mode disabled (post-cutover), when a problem is added, then only
patient_chart.problem.added.v1is published. - Given a downstream consumer has confirmed cutover, when dual-publish is disabled, then no duplicate processing occurs.
Technical notes:
- Dual-publish controlled via
DUAL_PUBLISH_SUBJECTS_ENABLEDconfig flag. - Legacy subjects deprecated after all consumers confirmed on canonical.
Definition of Done:
- Feature flag integration test. Consumer deduplication (idempotent on
sourceEventId) verified.