import type { Patient } from 'fhir/r4'; /** * DOES THIS PRACTICE HAVE A CARE RELATIONSHIP WITH THIS PATIENT? * * Not a permission check - the server owns that, and this must never be read as one. It answers a * narrower question the CHART can answer for itself, so a clinician can be told why every clinical list * on an otherwise complete-looking chart is refusing. * * WHAT THE SERVER ACTUALLY DOES, which this mirrors and deliberately under-claims against. A * practitioner is granted the clinical axis when they are the patient's direct `generalPractitioner`, OR * when `generalPractitioner` names their organization. Failing both, execution falls through to the * AFFILIATION branch, which grants the visibility axis only - the name, the age, the phone number. That * is the two-axis model working, and it is why the chart opens and then every list refuses. * * MEASURED, 2026-08-30: a patient with `generalPractitioner: -` returned 403 on Condition and * DiagnosticReport while Flag and Provenance returned 200, in the same session, seconds apart. The * difference is not the resource type; it is that those two routes require patient access and the others * do not. */ /** Every reference on the patient, as `Type/id` strings. */ const referencesOf = (patient: Patient | undefined): string[] => (patient?.generalPractitioner ?? []) .map((reference) => reference.reference) .filter((reference): reference is string => Boolean(reference)); /** * True ONLY when we can be certain no care relationship exists. * * FAILS SILENT, NOT LOUD, and that asymmetry is the whole design. A false negative costs a sentence * that could have been shown; a false positive tells a clinician their practice has no relationship with * a patient it may well be treating, on a chart that is refusing for some other reason entirely. The * second is a statement about the practice's own records and it must not be guessed. * * So it stays silent whenever it cannot decide: * * no patient loaded yet -> silent, the answer is not knowable * no organization in context -> silent, there is nothing to compare against * ANY Practitioner reference -> silent. The server grants on a direct practitioner link, and this * layer cannot tell whether that practitioner is a colleague here. * A named GP we cannot place is exactly the case to say nothing about * this organization is named -> silent, the relationship plainly exists * * It speaks only for a patient whose `generalPractitioner` is empty, or names other ORGANIZATIONS and * this one is not among them. In both of those the absence is a fact about the record rather than a gap * in what this screen can see. */ export const practiceHasNoCareRelationship = ( patient: Patient | undefined, organizationId: string | undefined, ): boolean => { if (!patient || !organizationId) return false; const references = referencesOf(patient); // A direct practitioner link may be a colleague at this practice. Undecidable here, so say nothing. if (references.some((reference) => reference.startsWith('Practitioner/'))) return false; return !references.includes(`Organization/${organizationId}`); };