diff --git a/src/__tests__/baselines/version-stamp-comments.json b/src/__tests__/baselines/version-stamp-comments.json index fc64b4a07..278f68243 100644 --- a/src/__tests__/baselines/version-stamp-comments.json +++ b/src/__tests__/baselines/version-stamp-comments.json @@ -1,7 +1,7 @@ { "$comment": "Comment lines that open with a release version, per file under src/. Maintained by src/__tests__/version-stamp-ratchet.test.ts.", "count": 2892, - "total": 9220, + "total": 9216, "entries": { "__tests__/account-payload-consumer-guard.test.ts": 2, "__tests__/acting-account-boundary-guard.test.ts": 7, @@ -1963,10 +1963,10 @@ "lib/doctor-report-helpers.ts": 1, "lib/doctor-report-pdf-core.ts": 4, "lib/doctor-report-pdf/clinical-records-notes-section.ts": 6, - "lib/doctor-report-pdf/header-profile-section.ts": 3, + "lib/doctor-report-pdf/header-profile-section.ts": 2, "lib/doctor-report-pdf/measurements-charts-section.ts": 3, "lib/doctor-report-pdf/medication-mood-wellness-section.ts": 7, - "lib/doctor-report-types.ts": 21, + "lib/doctor-report-types.ts": 20, "lib/doctor-report/__tests__/leaf-gating-sweep.test.ts": 1, "lib/doctor-report/clinical-records.ts": 2, "lib/doctor-report/collect.ts": 4, diff --git a/src/__tests__/share-view-leaf-render-guard.test.ts b/src/__tests__/share-view-leaf-render-guard.test.ts index bd50f93f0..6dba245a7 100644 --- a/src/__tests__/share-view-leaf-render-guard.test.ts +++ b/src/__tests__/share-view-leaf-render-guard.test.ts @@ -59,7 +59,7 @@ function emptyReport(): DoctorReportData { end: "2026-01-31T00:00:00.000Z", }, patient: { - username: null, + name: null, dateOfBirth: null, gender: null, heightCm: null, @@ -156,11 +156,10 @@ const DISPOSITIONS: Record = { kind: "rendered", data: { patient: { - username: "shared-account", + name: "A. Patient", dateOfBirth: "1979-04-02T00:00:00.000Z", gender: "FEMALE", heightCm: 171, - fullName: "A. Patient", }, }, marker: "A. Patient", diff --git a/src/app/c/[token]/__tests__/report-download.test.ts b/src/app/c/[token]/__tests__/report-download.test.ts index b52bbf05c..228857a33 100644 --- a/src/app/c/[token]/__tests__/report-download.test.ts +++ b/src/app/c/[token]/__tests__/report-download.test.ts @@ -97,7 +97,7 @@ function report(): DoctorReportData { end: "2026-01-31T00:00:00.000Z", }, patient: { - username: null, + name: null, dateOfBirth: null, gender: null, heightCm: null, diff --git a/src/components/clinician/__tests__/clinician-view-locales.test.tsx b/src/components/clinician/__tests__/clinician-view-locales.test.tsx index 4fa3094b9..75980ba2f 100644 --- a/src/components/clinician/__tests__/clinician-view-locales.test.tsx +++ b/src/components/clinician/__tests__/clinician-view-locales.test.tsx @@ -42,11 +42,10 @@ const FULL_RECORD = { end: "2026-01-31T00:00:00.000Z", }, patient: { - username: "shared-account", + name: "A. Patient", dateOfBirth: "1980-01-01T00:00:00.000Z", gender: "FEMALE", heightCm: 170, - fullName: "A. Patient", }, practiceName: null, measurements: {}, diff --git a/src/components/clinician/__tests__/clinician-view.test.tsx b/src/components/clinician/__tests__/clinician-view.test.tsx index da8a4a3f3..7b16bb750 100644 --- a/src/components/clinician/__tests__/clinician-view.test.tsx +++ b/src/components/clinician/__tests__/clinician-view.test.tsx @@ -27,7 +27,7 @@ function makeReport( end: "2026-01-31T00:00:00.000Z", }, patient: { - username: "tester", + name: "tester", dateOfBirth: null, gender: null, heightCm: null, diff --git a/src/components/clinician/identity-sections.tsx b/src/components/clinician/identity-sections.tsx index 9dfef4aef..3eacd8e81 100644 --- a/src/components/clinician/identity-sections.tsx +++ b/src/components/clinician/identity-sections.tsx @@ -56,7 +56,7 @@ export function PatientIdentitySection({ fmtDate: (iso: string) => string; }) { const patient = report.patient; - const name = patient.fullName ?? patient.username ?? null; + const name = patient.name; const genderKey = patient.gender ? GENDER_LABEL_KEYS[patient.gender] : undefined; diff --git a/src/lib/__tests__/account-name.test.ts b/src/lib/__tests__/account-name.test.ts index 3d080ebb1..99e0ff3cc 100644 --- a/src/lib/__tests__/account-name.test.ts +++ b/src/lib/__tests__/account-name.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { accountName } from "../account-name"; +import { accountName, nameInitials, patientName } from "../account-name"; describe("accountName", () => { it("names the account by its display name when one is set", () => { @@ -19,3 +19,41 @@ describe("accountName", () => { expect(accountName({ username: "jdoe" })).toBe("jdoe"); }); }); + +describe("patientName", () => { + const own = { + username: "jdoe", + displayName: null, + fullName: null, + managedProfileAt: null, + }; + const managed = { + username: "managed-3f9a0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f", + displayName: null, + fullName: null, + managedProfileAt: new Date("2026-01-01T00:00:00.000Z"), + }; + + it("prefers the full name, then the display name, then the login", () => { + expect( + patientName({ ...own, fullName: "Jane Doe", displayName: "Jane" }), + ).toBe("Jane Doe"); + expect(patientName({ ...own, fullName: " ", displayName: "Jane" })).toBe( + "Jane", + ); + expect(patientName(own)).toBe("jdoe"); + }); + + it("never prints a managed record's generated login", () => { + expect(patientName({ ...managed, displayName: "Robin" })).toBe("Robin"); + expect(patientName(managed)).toBeNull(); + }); +}); + +describe("nameInitials", () => { + it("takes the first letter of the first two words", () => { + expect(nameInitials("Jane Doe")).toBe("JD"); + expect(nameInitials("max.mustermann")).toBe("MM"); + expect(nameInitials("Robin")).toBe("R"); + }); +}); diff --git a/src/lib/__tests__/doctor-report-pdf-core.test.ts b/src/lib/__tests__/doctor-report-pdf-core.test.ts index 081dd0a57..4547d92e5 100644 --- a/src/lib/__tests__/doctor-report-pdf-core.test.ts +++ b/src/lib/__tests__/doctor-report-pdf-core.test.ts @@ -27,7 +27,7 @@ function makeData(overrides?: Partial): DoctorReportData { end: "2026-05-03T12:00:00.000Z", }, patient: { - username: "testuser", + name: "testuser", dateOfBirth: "1985-06-15T00:00:00.000Z", gender: "MALE", heightCm: 182, @@ -1003,12 +1003,11 @@ describe("doctor-report emergency first page", () => { }); describe("extracted doctor-report section boundaries", () => { - it("header/profile prefers the legal name and renders insurance identity", async () => { + it("header/profile prints the resolved name and renders insurance identity", async () => { const data = makeData({ patient: { ...makeData().patient, - fullName: "Ada Lovelace", - username: "ignored-handle", + name: "Ada Lovelace", insurerName: "Example Health", }, }); @@ -1022,7 +1021,6 @@ describe("extracted doctor-report section boundaries", () => { }); const text = await extractText(bytes); expect(text).toContain("Ada Lovelace"); - expect(text).not.toContain("ignored-handle"); expect(text).toContain("Example Health"); expect(text).toContain("A123456789"); }); diff --git a/src/lib/account-name.ts b/src/lib/account-name.ts index 2340acaa6..7e39c7319 100644 --- a/src/lib/account-name.ts +++ b/src/lib/account-name.ts @@ -11,3 +11,31 @@ export function accountName(user: { const displayName = user.displayName?.trim(); return displayName ? displayName : user.username; } + +/** + * The name a clinical document gives a person: the full name when one is + * set, the display name next, the login last. A managed record's login is a + * handle generated when the record was created (`managed-`), not a name + * anybody chose, so it never prints; with no other name the line is left out. + */ +export function patientName(user: { + username: string; + displayName: string | null; + fullName: string | null; + managedProfileAt: Date | null; +}): string | null { + const fullName = user.fullName?.trim(); + if (fullName) return fullName; + const displayName = user.displayName?.trim(); + if (displayName) return displayName; + return user.managedProfileAt ? null : user.username; +} + +/** Up to two letters for an avatar, from a name such as `accountName()`. */ +export function nameInitials(name: string): string { + return name + .split(/[\s._-]+/) + .slice(0, 2) + .map((s) => s[0]?.toUpperCase() ?? "") + .join(""); +} diff --git a/src/lib/doctor-report-pdf-core.ts b/src/lib/doctor-report-pdf-core.ts index ad1d366a7..3a511be97 100644 --- a/src/lib/doctor-report-pdf-core.ts +++ b/src/lib/doctor-report-pdf-core.ts @@ -164,7 +164,7 @@ export function buildDoctorReportPdfDocument( title: t("doctorReport.title"), subject: t("doctorReport.subtitle"), creator: "HealthLog", - author: data.patient.fullName ?? data.patient.username ?? "HealthLog", + author: data.patient.name ?? "HealthLog", }); const pageWidth = doc.internal.pageSize.getWidth(); const pageHeight = doc.internal.pageSize.getHeight(); diff --git a/src/lib/doctor-report-pdf/__tests__/activity-effect-sections.test.ts b/src/lib/doctor-report-pdf/__tests__/activity-effect-sections.test.ts index 85587fa4d..9ccd51995 100644 --- a/src/lib/doctor-report-pdf/__tests__/activity-effect-sections.test.ts +++ b/src/lib/doctor-report-pdf/__tests__/activity-effect-sections.test.ts @@ -97,7 +97,7 @@ function data(overrides: Partial = {}): DoctorReportData { end: "2026-05-03T12:00:00.000Z", }, patient: { - username: null, + name: null, dateOfBirth: null, gender: null, heightCm: null, diff --git a/src/lib/doctor-report-pdf/__tests__/header-profile-gender.test.ts b/src/lib/doctor-report-pdf/__tests__/header-profile-gender.test.ts index ce126d2b5..db5846db6 100644 --- a/src/lib/doctor-report-pdf/__tests__/header-profile-gender.test.ts +++ b/src/lib/doctor-report-pdf/__tests__/header-profile-gender.test.ts @@ -32,8 +32,7 @@ function renderPatientLines(gender: string | null): string[] { doc, data: { patient: { - username: "testuser", - fullName: null, + name: "testuser", dateOfBirth: null, gender, heightCm: null, diff --git a/src/lib/doctor-report-pdf/header-profile-section.ts b/src/lib/doctor-report-pdf/header-profile-section.ts index 27975a81b..f4a3ca0d4 100644 --- a/src/lib/doctor-report-pdf/header-profile-section.ts +++ b/src/lib/doctor-report-pdf/header-profile-section.ts @@ -62,12 +62,9 @@ export function buildHeaderProfileSection( doc.setFontSize(9); doc.setTextColor(80, 80, 80); const patientInfo: string[] = []; - // v1.7.0 — prefer the legal full name on the cover; fall back to the - // username when no full name is set (collapses exactly like the - // practice name when neither is present). - const patientName = data.patient.fullName ?? data.patient.username ?? null; - if (patientName) { - patientInfo.push(`${t("doctorReport.patient")}: ${patientName}`); + // Collapses like the practice name when the record has no name to print. + if (data.patient.name) { + patientInfo.push(`${t("doctorReport.patient")}: ${data.patient.name}`); } if (data.patient.dateOfBirth) { patientInfo.push( diff --git a/src/lib/doctor-report-types.ts b/src/lib/doctor-report-types.ts index b82c4fffe..2b7a73697 100644 --- a/src/lib/doctor-report-types.ts +++ b/src/lib/doctor-report-types.ts @@ -80,17 +80,19 @@ export interface DoctorReportData { */ period: { days: number; since: string; start: string; end: string }; patient: { - username: string | null; + /** + * The name the cover, the PDF metadata, the FHIR Patient and the share + * page print, resolved once by `patientName()`: full name, display name, + * login, never a managed record's generated handle. Null prints no line. + */ + name: string | null; dateOfBirth: string | null; gender: string | null; heightCm: number | null; - // v1.7.0 — optional patient-identity fields for the export cover + - // FHIR Patient. `fullName` + `insurerName` are plaintext columns; the - // KVNR is NOT carried here (it's encrypted at rest and decrypted by - // the route, then handed to the builders separately). Optional in the - // type so pre-v1.7.0 fixtures that omit them still typecheck — the - // renderer treats absent and null identically. - fullName?: string | null; + // `insurerName` is a plaintext column; the KVNR is NOT carried here (it's + // encrypted at rest and decrypted by the route, then handed to the + // builders separately). Optional so fixtures that omit it still + // typecheck; the renderer treats absent and null identically. insurerName?: string | null; // v1.8.6 — German insurer institution number (IKNR), plaintext. Carried // here for the FHIR `Coverage` payor Organization identifier. Optional diff --git a/src/lib/doctor-report/__tests__/leaf-gating-sweep.test.ts b/src/lib/doctor-report/__tests__/leaf-gating-sweep.test.ts index da880ef5d..6650e83a6 100644 --- a/src/lib/doctor-report/__tests__/leaf-gating-sweep.test.ts +++ b/src/lib/doctor-report/__tests__/leaf-gating-sweep.test.ts @@ -130,6 +130,8 @@ function seed() { rows.userUnique = [ { username: "owner", + displayName: null, + managedProfileAt: null, dateOfBirth: new Date("1980-05-05T00:00:00.000Z"), gender: "OTHER", heightCm: 180, @@ -328,7 +330,7 @@ describe("per-leaf gating sweep", () => { 0, ); expect(Object.keys(allPayload.compliance).length).toBeGreaterThan(0); - expect(allPayload.patient.username).toBe("owner"); + expect(allPayload.patient.name).toBe("Owner Name"); expect(allPayload.patient.insurerName).toBe("Sample Insurer"); expect(allPayload.bmi).not.toBeNull(); expect(Object.keys(allPayload.glucoseStats).length).toBeGreaterThan(0); @@ -457,6 +459,47 @@ describe("the derived figures ride their own leaf", () => { }); }); +describe("the name the report prints", () => { + const identity = selectionFromLeaves(["PATIENT_IDENTITY"]); + const asUser = (fields: Record) => { + rows.userUnique = [{ ...(rows.userUnique[0] as object), ...fields }]; + }; + + it("names a managed record by its display name, never its generated login", async () => { + asUser({ + username: "managed-3f9a0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f", + displayName: "Robin", + fullName: null, + managedProfileAt: new Date("2026-01-01T00:00:00.000Z"), + }); + const payload = await collectDoctorReportData("u1", RANGE, identity, { + moduleMap: moduleMap(), + }); + expect(payload.patient.name).toBe("Robin"); + }); + + it("prints no name for a managed record that has none", async () => { + asUser({ + username: "managed-3f9a0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f", + displayName: null, + fullName: null, + managedProfileAt: new Date("2026-01-01T00:00:00.000Z"), + }); + const payload = await collectDoctorReportData("u1", RANGE, identity, { + moduleMap: moduleMap(), + }); + expect(payload.patient.name).toBeNull(); + }); + + it("prefers the display name over the login on an own account", async () => { + asUser({ username: "owner", displayName: "Jane Doe", fullName: null }); + const payload = await collectDoctorReportData("u1", RANGE, identity, { + moduleMap: moduleMap(), + }); + expect(payload.patient.name).toBe("Jane Doe"); + }); +}); + describe("fail-closed exclusion", () => { it("excludes a measurement type the catalogue does not know", () => { const gate = resolveReportGate( @@ -571,7 +614,7 @@ describe("the empty selection serves nothing", () => { selectionFromLeaves([]), { moduleMap: moduleMap() }, ); - expect(payload.patient.username).toBeNull(); + expect(payload.patient.name).toBeNull(); expect(payload.patient.insurerName).toBeNull(); expect(payload.labResults).toBeNull(); expect(payload.allergies).toBeNull(); diff --git a/src/lib/doctor-report/collect.ts b/src/lib/doctor-report/collect.ts index e40039f56..5d76aafd3 100644 --- a/src/lib/doctor-report/collect.ts +++ b/src/lib/doctor-report/collect.ts @@ -84,6 +84,7 @@ import { loadLabResults, } from "./clinical-records"; import { dayKeyAsUtcMidnight } from "@/lib/tz/date-only"; +import { patientName } from "@/lib/account-name"; import { readingsMean } from "@/lib/measurements/day-mean"; import { loadScreenerUnvalidatedTranslations } from "./screener-translations"; @@ -179,6 +180,8 @@ export async function collectDoctorReportData( where: { id: userId }, select: { username: true, + displayName: true, + managedProfileAt: true, dateOfBirth: true, gender: true, heightCm: true, @@ -720,14 +723,13 @@ export async function collectDoctorReportData( end: end.toISOString(), }, patient: { - username: identityOn ? (userProfile?.username ?? null) : null, + name: identityOn && userProfile ? patientName(userProfile) : null, dateOfBirth: identityOn && userProfile?.dateOfBirth ? userProfile.dateOfBirth.toISOString() : null, gender: identityOn ? (userProfile?.gender ?? null) : null, heightCm: identityOn ? (userProfile?.heightCm ?? null) : null, - fullName: identityOn ? (userProfile?.fullName ?? null) : null, insurerName: insuranceOn ? (userProfile?.insurerName ?? null) : null, insurerIkNumber: insuranceOn ? (userProfile?.insurerIkNumber ?? null) diff --git a/src/lib/fhir/__tests__/build-bundle.test.ts b/src/lib/fhir/__tests__/build-bundle.test.ts index 473758eae..0bdc2e550 100644 --- a/src/lib/fhir/__tests__/build-bundle.test.ts +++ b/src/lib/fhir/__tests__/build-bundle.test.ts @@ -69,11 +69,10 @@ function makeData(overrides?: Partial): DoctorReportData { end: "2026-05-03T12:00:00.000Z", }, patient: { - username: "sample-user", + name: "Sample Patient", dateOfBirth: "1985-06-15T00:00:00.000Z", gender: "MALE", heightCm: 182, - fullName: "Sample Patient", insurerName: "Example Insurer", }, practiceName: null, @@ -181,11 +180,10 @@ describe("buildFhirDocumentBundle", () => { const bundle = buildFhirDocumentBundle( makeData({ patient: { - username: "sample-user", + name: "Sample Patient", dateOfBirth: "1985-06-15T00:00:00.000Z", gender: "MALE", heightCm: 182, - fullName: "Sample Patient", insurerName: "Example Insurer", insurerIkNumber: "101234567", }, @@ -215,11 +213,10 @@ describe("buildFhirDocumentBundle", () => { const bundle = buildFhirDocumentBundle( makeData({ patient: { - username: "sample-user", + name: "Sample Patient", dateOfBirth: null, gender: null, heightCm: null, - fullName: "Sample Patient", insurerName: "Example Insurer", insurerIkNumber: "101234567", }, @@ -238,11 +235,10 @@ describe("buildFhirDocumentBundle", () => { const bundle = buildFhirDocumentBundle( makeData({ patient: { - username: "sample-user", + name: "Sample Patient", dateOfBirth: null, gender: null, heightCm: null, - fullName: "Sample Patient", insurerName: "Example Insurer", // no insurerIkNumber }, @@ -262,11 +258,10 @@ describe("buildFhirDocumentBundle", () => { const bundle = buildFhirDocumentBundle( makeData({ patient: { - username: "sample-user", + name: "Sample Patient", dateOfBirth: null, gender: null, heightCm: null, - fullName: "Sample Patient", insurerName: "Example Insurer", insurerIkNumber: "101234567", }, @@ -284,11 +279,10 @@ describe("buildFhirDocumentBundle", () => { const bundle = buildFhirDocumentBundle( makeData({ patient: { - username: "sample-user", + name: "Sample Patient", dateOfBirth: null, gender: null, heightCm: null, - fullName: "Sample Patient", // neither insurerName nor insurerIkNumber }, }), @@ -303,11 +297,10 @@ describe("buildFhirDocumentBundle", () => { const bundle = buildFhirDocumentBundle( makeData({ patient: { - username: "sample-user", + name: "Sample Patient", dateOfBirth: null, gender: null, heightCm: null, - fullName: "Sample Patient", // neither insurerName nor insurerIkNumber — just a bare KVNR. }, }), diff --git a/src/lib/fhir/__tests__/conformance.test.ts b/src/lib/fhir/__tests__/conformance.test.ts index 693da6fdf..6e924c033 100644 --- a/src/lib/fhir/__tests__/conformance.test.ts +++ b/src/lib/fhir/__tests__/conformance.test.ts @@ -36,11 +36,10 @@ function makeData(overrides?: Partial): DoctorReportData { end: "2026-05-03T12:00:00.000Z", }, patient: { - username: "sample-user", + name: "Sample Patient", dateOfBirth: "1985-06-15T00:00:00.000Z", gender: "MALE", heightCm: 182, - fullName: "Sample Patient", insurerName: "Example Insurer", insurerIkNumber: "101234567", }, @@ -286,7 +285,7 @@ describe("document Bundle — identity and resolvability", () => { fullBundle(), fullBundle({ patient: { - username: "u", + name: "u", dateOfBirth: null, gender: null, heightCm: null, @@ -375,7 +374,7 @@ describe("Patient.gender", () => { for (const [stored, expected] of cases) { const bundle = fullBundle({ patient: { - username: "u", + name: "u", dateOfBirth: null, gender: stored, heightCm: null, diff --git a/src/lib/fhir/__tests__/cycle-observations.test.ts b/src/lib/fhir/__tests__/cycle-observations.test.ts index 330a83d63..0bed3d981 100644 --- a/src/lib/fhir/__tests__/cycle-observations.test.ts +++ b/src/lib/fhir/__tests__/cycle-observations.test.ts @@ -15,7 +15,7 @@ function baseData(overrides?: Partial): DoctorReportData { end: "2026-05-03T12:00:00.000Z", }, patient: { - username: "sample-user", + name: "sample-user", dateOfBirth: "1990-06-15T00:00:00.000Z", gender: "FEMALE", heightCm: 168, diff --git a/src/lib/fhir/__tests__/glucose-clinical-observations.test.ts b/src/lib/fhir/__tests__/glucose-clinical-observations.test.ts index 8562be554..c773239fc 100644 --- a/src/lib/fhir/__tests__/glucose-clinical-observations.test.ts +++ b/src/lib/fhir/__tests__/glucose-clinical-observations.test.ts @@ -22,7 +22,7 @@ function makeData(overrides?: Partial): DoctorReportData { end: "2026-05-03T12:00:00.000Z", }, patient: { - username: "sample-user", + name: "sample-user", dateOfBirth: "1985-06-15T00:00:00.000Z", gender: "MALE", heightCm: 182, diff --git a/src/lib/fhir/__tests__/illness-snomed.test.ts b/src/lib/fhir/__tests__/illness-snomed.test.ts index ce4b162c7..3377b24c8 100644 --- a/src/lib/fhir/__tests__/illness-snomed.test.ts +++ b/src/lib/fhir/__tests__/illness-snomed.test.ts @@ -18,7 +18,7 @@ function makeData(overrides?: Partial): DoctorReportData { end: "2026-05-03T12:00:00.000Z", }, patient: { - username: "sample-user", + name: "sample-user", dateOfBirth: "1985-06-15T00:00:00.000Z", gender: "MALE", heightCm: 182, diff --git a/src/lib/fhir/__tests__/lab-observations.test.ts b/src/lib/fhir/__tests__/lab-observations.test.ts index 3cb005613..02ff11d73 100644 --- a/src/lib/fhir/__tests__/lab-observations.test.ts +++ b/src/lib/fhir/__tests__/lab-observations.test.ts @@ -17,7 +17,7 @@ function makeData(overrides?: Partial): DoctorReportData { end: "2026-05-03T12:00:00.000Z", }, patient: { - username: "sample-user", + name: "sample-user", dateOfBirth: "1985-06-15T00:00:00.000Z", gender: "MALE", heightCm: 182, diff --git a/src/lib/fhir/__tests__/terminology-displays.test.ts b/src/lib/fhir/__tests__/terminology-displays.test.ts index 70ede4b70..a4c766e85 100644 --- a/src/lib/fhir/__tests__/terminology-displays.test.ts +++ b/src/lib/fhir/__tests__/terminology-displays.test.ts @@ -22,7 +22,7 @@ function codings(overrides: Partial): FhirCoding[] { start: "2026-02-02T00:00:00.000Z", end: "2026-05-03T12:00:00.000Z", }, - patient: { username: "sample-user", dateOfBirth: null, gender: null }, + patient: { name: "sample-user", dateOfBirth: null, gender: null }, practiceName: null, measurements: {}, stats: {}, @@ -118,7 +118,7 @@ describe("medication codings", () => { start: "2026-02-02T00:00:00.000Z", end: "2026-05-03T12:00:00.000Z", }, - patient: { username: "sample-user", dateOfBirth: null, gender: null }, + patient: { name: "sample-user", dateOfBirth: null, gender: null }, practiceName: null, measurements: {}, stats: {}, diff --git a/src/lib/fhir/build-bundle.ts b/src/lib/fhir/build-bundle.ts index db9225600..0f2ddf71b 100644 --- a/src/lib/fhir/build-bundle.ts +++ b/src/lib/fhir/build-bundle.ts @@ -315,7 +315,7 @@ export function buildFhirDocumentBundle( // v1.9.0 — when the aggregator capped the administration set, disclose // it in the narrative so the export is honest: it carries the // most-recent N of M acted intakes, the oldest having been omitted. - const displayName = data.patient.fullName ?? data.patient.username ?? null; + const displayName = data.patient.name; const truncation = data.medicationAdministrationsTruncation; const narrativeText = [ `Health record for ${escapeXml(displayName ?? "patient")}.`, diff --git a/src/lib/fhir/resources/patient.ts b/src/lib/fhir/resources/patient.ts index 47f14bc93..dfca26ac4 100644 --- a/src/lib/fhir/resources/patient.ts +++ b/src/lib/fhir/resources/patient.ts @@ -49,8 +49,7 @@ export function patientResource( resourceType: "Patient", id: PATIENT_RESOURCE_ID, }; - const displayName = data.patient.fullName ?? data.patient.username ?? null; - if (displayName) patient.name = [{ text: displayName }]; + if (data.patient.name) patient.name = [{ text: data.patient.name }]; const gender = administrativeGender(data.patient.gender); if (gender) patient.gender = gender; if (data.patient.dateOfBirth) { diff --git a/src/lib/mcp/__tests__/doctor-report-module-gate.test.ts b/src/lib/mcp/__tests__/doctor-report-module-gate.test.ts index c85a7f8a7..db607e77d 100644 --- a/src/lib/mcp/__tests__/doctor-report-module-gate.test.ts +++ b/src/lib/mcp/__tests__/doctor-report-module-gate.test.ts @@ -74,7 +74,7 @@ const REPORT_FIXTURE = { start: "2026-03-29T00:00:00.000Z", end: "2026-06-27T00:00:00.000Z", }, - patient: { username: "tester" }, + patient: { name: "tester" }, stats: { WEIGHT: { avg: 80, min: 79, max: 82, count: 30, latest: 80 } }, compliance: {}, medications: [], diff --git a/src/lib/mcp/__tests__/doctor-visit-summary.test.ts b/src/lib/mcp/__tests__/doctor-visit-summary.test.ts index 01e81b3b4..5b82a4b5c 100644 --- a/src/lib/mcp/__tests__/doctor-visit-summary.test.ts +++ b/src/lib/mcp/__tests__/doctor-visit-summary.test.ts @@ -29,7 +29,7 @@ function payload(overrides: Partial = {}): DoctorReportData { end: "2026-04-01T00:00:00.000Z", }, patient: { - username: null, + name: null, dateOfBirth: null, gender: null, heightCm: null, diff --git a/src/lib/mcp/__tests__/prompts.test.ts b/src/lib/mcp/__tests__/prompts.test.ts index d321aa8e9..ffd37d7ad 100644 --- a/src/lib/mcp/__tests__/prompts.test.ts +++ b/src/lib/mcp/__tests__/prompts.test.ts @@ -73,8 +73,7 @@ function reportFixture( end: "2026-06-27T00:00:00.000Z", }, patient: { - username: "tester", - fullName: "A Person", + name: "A Person", dateOfBirth: "1990-01-01", gender: "female", heightCm: 170, @@ -213,8 +212,7 @@ describe("doctor_visit_summary — grounding + structure", () => { .text, ); const patient = data.patient as Record; - expect(patient).not.toHaveProperty("fullName"); - expect(patient).not.toHaveProperty("username"); + expect(patient).not.toHaveProperty("name"); expect(patient.dateOfBirth).toBe("1990-01-01"); }); }); diff --git a/src/lib/mcp/__tests__/resources.test.ts b/src/lib/mcp/__tests__/resources.test.ts index c402eaf6f..945023533 100644 --- a/src/lib/mcp/__tests__/resources.test.ts +++ b/src/lib/mcp/__tests__/resources.test.ts @@ -224,8 +224,7 @@ describe("healthlog://report/doctor-visit", () => { end: "2026-06-27T00:00:00.000Z", }, patient: { - username: "tester", - fullName: "A Person", + name: "A Person", dateOfBirth: "1990-01-01", gender: "female", heightCm: 170, @@ -267,8 +266,7 @@ describe("healthlog://report/doctor-visit", () => { expect(result).toHaveProperty("illnessEpisodes"); // Patient name never leaks. const patient = result.patient as Record; - expect(patient).not.toHaveProperty("fullName"); - expect(patient).not.toHaveProperty("username"); + expect(patient).not.toHaveProperty("name"); }); it("omits absent sections honestly (no zero-fill)", async () => { diff --git a/tests/integration/doctor-report-sections.test.ts b/tests/integration/doctor-report-sections.test.ts index 8b857cf4d..759dd957a 100644 --- a/tests/integration/doctor-report-sections.test.ts +++ b/tests/integration/doctor-report-sections.test.ts @@ -314,7 +314,7 @@ describe("doctor-report — per-section toggles", () => { expect(data.labResults).toBeNull(); expect(data.allergies).toBeNull(); expect(data.familyHistory).toBeNull(); - expect(data.patient.username).toBeNull(); + expect(data.patient.name).toBeNull(); expect(data.patient.insurerName).toBeNull(); }); });