The Patient entity and its US Core satellites (SP5)

Featurekamo-shared-library
Shipped
August 25, 2026 at 2:49 PM UTC
Author
Kamo
Commit
8ff3660

A new entity rather than a reuse, and each rejection is concrete rather than stylistic. Not Member: GET /api/security/members/names returns every member of an org with NO right gate and the by-id endpoint returns name parts plus date of birth on a same-org check alone, so any authenticated session — including another patient's — would enumerate the roster. Not Lead: it is @Inheritance(JOINED), and a fetch graph into one of those took /leads down on the Hibernate version much of the fleet still runs. Not Contact: those belong to a User's personal address book, which is the opposite of a patient belonging to the practice. The same human is a DIFFERENT Patient in each practice. That is the tenancy boundary, not a compromise — each covered entity holds its own record, MRN and consent posture, and cross-practice identity is a matching problem for the MPI rather than a shared row. Three sex/gender concepts are separate columns because they answer different questions and one column cannot serve all three. Administrative gender is what appears on paperwork; birth sex is certification-tested and searchable; sexForClinicalUse is the one that drives reference ranges and dosing. Conflating them is how a trans patient gets the wrong reference range on a lab result. birthDate is a LocalDate deliberately — a birth date has no time and no zone, and giving it one shifts it across the date line for anyone who travels. This is why the discipline harness bans LocalDateTime specifically rather than all local types. birthDatePrecision is there because a patient who knows only their birth year is common, and storing that as 1 January then matching on the exact date silently fails to find them. Race and ethnicity are a child table because a person can report several and a single column forces someone of mixed heritage into whichever box the code happened to keep. They feed quality measures and disparity reporting, so they are not demographic decoration — and selfReported defaults true because they are recorded from what a patient said, not inferred. Language carries interpreterRequired as an independent fact: a patient may prefer Spanish and decoder an interpreter, and inferring one from the other is wrong in both directions. Section 1557 turns on both. active means "this record is in use", NOT soft-deleted, and mergedInto plus mergedAt exist because the platform's only existing merge is one-way with no unmerge — and merging the wrong two patients is among the most damaging mistakes a practice can make. contactId keeps the CRM link so acquisition attribution follows a person from first enquiry into the chart. No standalone EHR can do that, because it has no person before it has a patient. 1651 tests green.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing