Patient Chart Service — Sync Contract
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template · 16 Offline & Sync
1. Conflict policy per aggregate
| Aggregate | Policy | Rationale |
|---|---|---|
| Problem | server_authoritative with lww+diff on non-status attributes | Clinical record ownership; status transitions serialized server-side |
| Allergy | server_authoritative with lww+diff on reactions / categories | Safety-critical; duplicate guard must run at server |
| VitalsSet | append_only for new sets; corrections create a new version | Historical integrity; never mutate the wire |
| Observation | append_only | Measurement immutability |
| ClinicalNote (draft) | lww per section body with offline queue up to 48h | Offline authoring is supported for field clinics |
| ClinicalNote (signed/amended) | server_authoritative; no offline amend | Legal record; server mediates cosign and amendment |
| NoteAddendum | append_only | — |
| ChartAccess | append_only | Audit trail |
2. Offline semantics
- Vitals and draft notes are the primary offline-capture surfaces on tablet / POC devices.
- Offline writes are queued with a client-generated ULID and a
clientGeneratedAttimestamp. On sync, the server assigns the canonical ULID (or accepts the client ULID after cross-tenant and duplicate guards). - Idempotency:
Idempotency-Keyheader required on POST replays; server returns the prior response if the key is known within the dedupe window (24 h). - NKA / NKDA assertions are never queued offline; they must be created while online to guarantee the active-set invariant.
- Signing a note offline is not supported. Draft edits are queued; the clinician must be online to sign.
3. Conflict resolution examples
| Scenario | Resolution |
|---|---|
| Two clinicians edit different sections of the same draft | Both retained; merged per section (lww per section); server emits note.updated_draft.v1 once |
| Two clinicians edit same section offline | Later clientGeneratedAt wins; earlier edit retained as a non-authoritative backup with conflictPreservedId |
Offline vitals set + online vitals set at same recordedAt | Both persisted as separate VitalsSet rows; chart timeline shows both with performer attribution |
| Offline problem add when server already has same code active | Reject with CHART_DUPLICATE_PROBLEM; client surfaces merge action |
| Offline allergy add when NKA active server-side | Reject with CHART_NKA_CONFLICT; client shows NKA present |
4. Ordering guarantees
- Intra-aggregate events preserve ordering per
(aggregateId, version). - Cross-aggregate ordering is not guaranteed; consumers that need order (e.g., population-health rollups) key on
(patientId, occurredAt).
5. Vector clocks
Not used. The service uses integer version per aggregate combined with a server-authoritative last-writer-wins tie-breaker for the specific fields marked lww+diff.
6. Backfill
When a previously offline device reconnects with a queued batch older than 30 days, the server marks resulting events with source=offline-backfill in the envelope metadata; downstream consumers MAY apply different rate-limits.
7. Open Questions
- Should draft-note offline queue be 48 h or 7 days? Current policy: 48 h; extend if field-clinic trials demand.