v1.37.28 — the parts of a shared record nobody could see - #838
Merged
Conversation
Three surfaces built the label key by interpolation — the doctor-report PDF's visit table, the daily digest's upcoming-visit line and the clinician share view. `EncounterKind` is `ROUTINE` and the bundle leaf is `routine`, so all three rendered `encounters.kind.ROUTINE` verbatim: into a document a practice files, onto a lock screen, and onto the page. Route them through `encounterKindLabelKey`, a literal switch beside the existing `encounterKindLabel`, so the mapping is written down once. The enum-derived i18n guard could not have caught this: it only knows key spaces of the form `prefix + member`, and this one is not. It gains a second arm for spaces whose mapping is a function, with `encounters.kind` as its first entry. Break-proof: point one arm of the resolver back at the uppercase key and six locale cases go red.
A leaf on a share link can be absent from the payload for two reasons that look identical downstream: the owner shared it and recorded nothing, or they shared it and the domain is switched off on their account. The aggregator ANDs the selection and the module map and returns the same null either way, so the recipient could not tell an empty section from one that never had a chance to carry anything. `loadShareViewData` now resolves the whole module map — it needed the `doctorReport` key from it anyway — and returns `unavailableLeaves`: the leaves the link DOES carry whose owning module is off. Only selected leaves appear, because a withheld leaf's module state is not the recipient's business in any direction. The map is deliberately NOT handed to the aggregator. The third argument is the frozen selection and there is no fourth, which is what keeps this surface from growing an options object that widens what it asks for; the resolver memoises its reads per request, so the aggregator resolving its own map again costs no round-trip. The module-gate suite moves off `isModuleEnabled` with it, and gains three cases for the new verdict, including the one that proves an unshared leaf is never named.
Someone ticks "Lab values" in the selection, sends the link, and the
doctor opens a page that says nothing about lab values — while the PDF
download from the same link prints them. Eleven of the seventeen
structured leaves were like that: the control existed, the data reached
the page object, and no component read it. The person had every reason
to believe they had shared something they had not.
Rendered, in catalogue-group order, which is also the PDF's:
- emergency information, framed and first, as it is page one there;
- personal details (name, date of birth, gender, height);
- lab values, with the reference window each reading was judged
against, printed as the source report printed it;
- GLP-1 therapy and the logged-dose ledger;
- conditions and illnesses, visits, immunizations;
- family history, mood, the menstrual cycle.
What each shows follows the PDF, which has already settled what belongs
in a section and in what order. The layout does not: a six-column table
becomes a labelled block, a four-column one becomes a row with its
detail composed onto the value side.
Absence is stated, never implied. A leaf the link does not carry renders
nothing at all — the recipient was never promised it. A leaf on the link
with nothing behind it renders its heading and says so, and one whose
domain is switched off on the owner's account says that instead. This
extends what the health-profile section has always done per fact to the
section level, and the glucose, medication and allergy cards adopt it
too; the measurement groups deliberately stay silent, because a group
card stands for up to seventeen leaves and twelve empty ones is noise.
Every card now asks the frozen selection directly through `LeafScope`
rather than inferring consent from the presence of data. The aggregator
already applies the selection, but a section that reads only "is there
data" trusts a gate it cannot see.
Three rows on the emergency card are composed from other leaves — severe
allergies, the drug list, chronic conditions. Each appears only when its
own leaf is on the link: printing "Not recorded" for a leaf the owner
withheld states an absence in the record where the truth is an absence
in the share, and on that card it is the dangerous direction to get
wrong.
The glucose card's heading was "Lab values", which was the wrong name
for it even before the actual lab results arrived; it is the glucose
group's own label now.
`INSURANCE` gets no renderer and must not get one: the create route
refuses the leaf outright, so no link can carry it.
Eleven leaves stayed invisible for eleven releases because nothing connected the two lists. The picker-to-aggregator guard was satisfied and kept being satisfied; the page was not on either end of it. `DISPOSITIONS` is `Record<StructuredLeafId, …>`, so an eighteenth structured leaf fails `pnpm typecheck` until someone writes down whether it renders or why it cannot. Per leaf the suite then renders the real view over a real fixture and asserts the marker appears; re-renders with that leaf alone removed from the frozen selection, payload untouched, and asserts it is gone; and for a refusal, reads the list that enforces it rather than trusting the note. Once over the whole set: a carried leaf with nothing behind it says so, a leaf whose module is off says something else, and a leaf the link never carried says nothing at all. The `share-downloads` row is asserted here too. Its test id had zero references anywhere in the tree, so the two buttons a practice clicks could have lost a link, pointed at the wrong path, or vanished from a documents-only share unnoticed. Break-proofs, each confirmed red then restored: drop a section element (that leaf's render case); gate `LeafSection` on data instead of scope (thirteen withholding cases); collapse the switched-off arm (the distinction case); empty `SHARE_LINK_FORBIDDEN_LEAVES` (the refusal case); add an eighteenth leaf (typecheck).
Both routes were exempt from the contract as `shareLink` — "authenticated
by the URL credential, for a recipient with no account". That reason
holds for the page a person opens. It does not hold for two routes whose
whole purpose is to hand a practice a file it files into another system,
which is exactly the audience a contract has. `/c/{token}/d/{id}` was
published all along, one route away in the same tree.
Each entry states the gate it shares with the page, the frozen selection
it can never exceed, the flat 404 that covers every miss class including
a documents-only link, and the 20/h per-link bucket. The FHIR entry says
plainly that it is a download and not a REST face, and names the two
resource families the bundle deliberately omits.
The create-request description claimed "there is no FHIR or other
machine-readable face behind a share token", which stopped being true
when these routes landed. It now describes what a link actually serves
and records that the insurance leaf is refused outright.
The coverage guard gains the other half of its staleness check. It caught
an exemption for a route that had disappeared, and never the reverse — an
exemption left behind after the route was published, which is how these
two would have stayed on the list while the contract already carried
them. Break-proof: re-add either entry and it goes red naming it.
A reading judged against the window its own report printed, beside a saved band that states different limits, is two answers about one number. The PDF names both — the range in the column, the saved band in a footnote under the table — and says in as many words that showing one of them is a partial answer. The page carried only the first. Both fit on the value line here, so there is no footnote to place. Break-proof: force the divergence branch off and the case goes red.
Most labels on this page are resolved from a runtime value, not written as a literal: an illness type, a lifecycle, a visit kind, a vaccine slug, a blood-type constant, a cycle phase, a GLP-1 side-effect tag. The call-site guard cannot see any of them, and the enum-derived guard can only cover a space whose members it can enumerate from a source. A key that does not exist therefore renders as its own dot notation, in front of a doctor, with every other guard green — which is exactly how `encounters.kind.ROUTINE` reached three surfaces. One fixture with every section populated, six renders, one assertion: nothing in the reader-visible text may look like a dotted key path. Break-proof: put the interpolated visit-kind key back and all six go red naming it.
`GET /api/insights/derived` accepts `windowDays`, bounded to 1..90, and threads it to the dispatcher. Omitting it changes nothing: every engine keeps its own default, so a caller that asks for nothing reads exactly what it read before. Ninety is where the tier this route promises runs out. The rollup router opens its WEEK floor at 91 days, and the baseline engines cannot compose a spread from WEEK buckets, so a 91-day request falls through to a raw `measurement.findMany` with no row cap — for a densely sampled type that is a full history scan, and the analytics-read budget allows 120 of them a minute per account. The trailing `series` is capped at 30 points regardless, so past the ceiling a wider window would only broaden the mean behind the value. Out of range is a 422 rather than a clamp. An unhonoured window is visible rather than silent. `provenance.windowDays` is the window the engine actually used and `coverage.historyDays` the days that actually backed it, so a three-week record answering a thirty-day request reports both numbers instead of implying coverage it does not have. `HEALTH_SCORE` composes fixed per-pillar windows and reports the widest; a new suite freezes that it is the only metric that does, so an arm added later cannot quietly drop the parameter. Recovery keeps its canonical resolution under the wider window. A worn band stamps the wake morning and the computed proxy the night before, and only `resolveCanonicalRecovery` pairs them; a test seeds forty such nights, asks for sixty days, and holds the answer to forty nights with a flat trend — a bypass leaves eighty rows and a false climb. Contract: model `WellnessScoreValue` field for field and publish it. The envelope's `value` has to stay an open record because one route serves eighteen differently-shaped metrics, so nothing can `$ref` the shape and it went out undocumented — `series`, `daysInWindow` and `asOf` were on the wire with no way to find them short of inspecting a live response. It is registered in the forced-components slot instead, held against the runtime interface by a test, and the fields it does not cover are named in the schema comment so the remaining gap is known rather than invisible.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Eleven parts of a shared record were selectable and invisible. Someone ticked "Lab values", sent the link to their doctor, and the page showed nothing — while the same link's PDF download carried them. The data had been arriving on the page all along, from the same aggregator the PDF uses.
The eleven now render: identity, emergency data, lab results, GLP-1 therapy, logged doses, illness episodes, visits, immunizations, family history, mood and cycle.
An empty section now says which kind of empty it is. Not selected stays absent entirely, because naming a section is itself a disclosure. Selected with nothing recorded says so. Selected but switched off in the owner's account says that instead, so a recipient does not read an empty card as an empty life. The module verdict had to be carried beside the payload to make this possible: the aggregator ANDs the selection and the module map and returns the same
nulleither way.Two corrections to the original framing, both established rather than assumed. Insurance cannot be one of the eleven — it is refused at share-link creation with a 422 naming the leaf, so no link can carry it. Logged medication doses were the one that was missed: they reached the FHIR download and neither the page nor the report, so it was the only control whose effect a person could see only by opening the bundle in another program.
A live defect found while mirroring the PDF's content. Three surfaces built a visit's kind label by interpolating an enum member into a key space whose entries are lower case.
EncounterKindisROUTINE; the key isroutine. So a routine appointment printed asencounters.kind.ROUTINEin a document a practice files and on a phone's lock screen. The enum-derived i18n guard gained an arm for key spaces whose mapping is a function rather than a prefix, which is the shape that made this invisible.The guard.
DISPOSITIONSis keyed byStructuredLeafId, so an eighteenth leaf fails typecheck until someone writes down whether it renders or why it cannot. Per leaf it renders the real view over a real fixture, then re-renders with that leaf removed from the selection and the payload untouched, and asserts it is gone. Five break-proofs confirmed red then restored, plus a sixth that renders all six locales and fails on any dotted key path in reader-visible text.Also here
GET /api/insights/derivedtakes awindowDaysparameter. The ceiling is 90, which is the last day before the read leaves the bucketed rollup tier for an unbounded scan; beyond it the request is refused rather than clamped. A test proves a longer window still goes through the canonical recovery resolution — without it an account with a worn band double-counts every night, and it would look right in every fixture.The wellness-score value object is modelled in the published contract. It was an open record documented for one metric, which is why a client team read the contract, built what they could see, and reported two fields as missing that were in the payload.
Numbers
Bundle 3477 of 3500. Per-route unchanged to the byte:
/page 450,/insights/mood 450,/insights 437,/measurements 435. Every new component is server-rendered, so this family ships no new client JavaScript.Gate: typecheck, lint, format, knip, openapi:check, 22,121 tests, build, bundle-check.