- the single- and bulk-delete flows on the measurement list both say
Messwert now, instead of switching to Messungen for the bulk case
- the practitioner field on the visit and vaccination forms reads
Praxis oder Arzt on both, dropping the slash form and the lone feminine
variant so it matches the practitioner list heading
- the visit saved/updated toasts lose their trailing period to match the
vaccination toasts they ship beside
- measurements subtitle drops to a single sentence and loses the phrase
that named Insights twice
- the mood self-rating card takes a short title so it stops wrapping three
lines and stops sharing the word Einschaetzung with the AI assessment
beside it; its noPattern and expectation lines move to direct address
- the daily-briefing empty state drops the worked example, leaving the
setup instruction on its own
The sub-page explainers had grown to four sentences of guideline detail that
pushed the stat strip and chart below the fold; the contract is a one- to
two-sentence definition, with the long form living behind the knowledge
disclosure. Each body now states what the metric is and why it matters.
Also aligns the readiness explainer to the score title across locales
(Bereitschaft / Disposición / Disponibilité), retiring the stray Tagesform.
The visit form's document / lab / condition link blocks and the
vaccination form's document link were flat lists capped at 25 rows with
no search: 30 lab values arrived undifferentiated and anything past the
cap was unreachable. Replace them with one shared picker — an inline
summary of removable chips over an add button, and a sheet that searches,
groups and multi-selects. Lab results gather under their panel and sample
date with a select-all per group, documents gather by month, illnesses
stay flat. The picker raises only the fetch limit; the server link cap
stays authoritative.
Search now covers the specialty column alongside name and practice, so a
field name like "Zahnmedizin" finds the practice even when the name and
practice hold nothing of the sort. The same OR reaches the visit form's
practitioner picker, which now shows the specialty beside the practice so
a specialty hit is legible. The list groups its rows under their
specialty, sorted alphabetically with the unspecified rows gathered into
one group at the end; when nothing carries a specialty the headings stay
suppressed and the list reads flat as before.
Manual water entry is gone from the app: the water item on the capture
picker, the dashboard quick-add menu and the hydration card's add button
are removed, and the manual quick-add sheet is deleted. Water that syncs
in through Apple Health still arrives and still shows on the hydration
card with its daily total, history and reference line.
The POST /api/nutrients/water endpoint and the batch sync path stay
untouched. The nutrients page's module-enable error, which had borrowed
a water-quick-add string, gets its own key across all six locales.
Input-only water keys (nav.capture.water*, nutrients.hydration.quickAdd*)
are removed.
Three small copy corrections that share the locale bundles:
- Resolve the formal-address outliers on the owner sharing and report
scopes to the informal register the rest of those surfaces use.
- Add the direct-address comma to the personalized dashboard greeting.
- Split the managed-record form error: a client-side validation failure
no longer surfaces as a network error, and a failed save gets its own
line.
The note above the write toggle claimed tokens are read-only in every
case, which the toggle directly below it contradicts by minting a
read_write scope. Rewrite it to state the real model: read-only by
default, write opt-in per token, never admin. Resolve the pronoun that
read as a formal address, and promote both scope notes to foreground so
this security copy no longer renders as muted meta.
- freeze the booster-mint and upload-suggestion routes into the sharing
surface guard with their why sentences; delegable-write and mutation
inventories move by the two routes they add
- inventory the two new routes as module-exempt data routes (SURFACE-gated,
like the medications posture)
- give the booster-plan audit action its own owner-facing verb in six locales
- register the dose delete as a destructive control; route the vaccination
success toasts through the outcome module
- move the report leaf counts (structured 15→16, all 92→93) and the history
group count in the panel; regenerate the OpenAPI selection bound
- seed the immunization rows in the export-route mock so the aggregator no
longer throws for a fully-selected report
- the ~35 antigen and combination display names, medical orthography per
locale, umlauts intact end to end; disease and antigen names only, never a
trade name
- the three clinical proper nouns whose written form is identical across
locales (Hib, Hepatitis A/B in Spanish) are recorded as legitimate matches
- IMMUNIZATIONS report leaf in the history group, gated both ways: the
vaccinations module and the leaf selection each exclude independently
(WR-7 seen red on the selection gate, reverted by content hash)
- collector loads the whole live history (reference data, not windowed, like
allergies) with server-resolved series positions; the PDF renders a compact
per-dose table — date, vaccine, dose, lot — with no due-status or gap analysis
- added to the default template so a new report offers it; stored selections
and frozen share links never widen
- leaf label and section copy in six locales
- from the record: a document link picker on the capture form, gated on the
documents module (blanked when off, never post-filtered)
- from the upload: a VACCINATION scan offers to file against a dose recorded
within the shared ±7-day window; a single candidate pre-selects, two offer a
picker with nothing pre-selected (WR-8 seen red on the pre-select rule,
reverted by content hash), zero shows nothing, and it never blocks the upload
- the suggestion rule reuses the visit moment's window constant rather than
forking it; it registers with the shared filing module when that lands
- suggestion and link copy in six locales
The write and manage levels each carried a paragraph of capability text
under their label, stacking into walls of text on the invite form. Cut
each to a single consent sentence inline and move the full enumeration
behind the info affordance at the option-heading height — what exactly
write may add, and the fence manage does not reach. The consent line
stays inline where it has to be read; the detail is one tap away for
anyone who wants the whole list. Six locales.
The scope question defaulted to whole record, which reads as the form
nudging the choice. Preselect neither option: an untouched fieldset now
blocks the form with a quiet hint instead of falling through to a
whole-record grant. The wire contract is unchanged — scope-absent still
means the entire record on the server, so the unset state deliberately
does not omit scope, it blocks the client until the owner picks. The
empty-selection and manage floors stay fail-closed as before.
- rung 2: a prefilled, user-confirmed, editable, declinable prompt writes an
ordinary VORSORGE reminder keyed on the dose's primary antigen; the
published origin enum and reminder DTO are unchanged
- idempotent by antigen — a second confirmation re-anchors the existing
reminder rather than minting a second (WR-9 seen red on the reminder count,
reverted by content hash)
- rolling day cadence so logging the next dose re-anchors the due date through
the Phase 1 satisfy matcher; the whole loop proven through real routes
- booster prompt copy in six locales, plural-aware
The score-composition settings grouped its topics under domain headings
(Heart and metabolism, Activity, Sleep, Body shape, Wellbeing). Remove
them and list the individual topics one under another with hairline
dividers, the way the health profile lists its facts. Each row keeps its
domain data attribute, so the flattening preserves the registry order and
tests still address a pillar by its domain. The now-orphaned domain-label
map and its six-locale strings go with it.
- compose rung-1 sentences from the seed's series length, booster interval and
category over a handful of i18n templates, with the per-entry source as a
verbatim footnote; nothing personal, no age or history conditioning
- surface it in the picker's chosen-entry detail and behind the antigen group
header's info affordance
- schedule templates in six locales, plural-aware
- one sheet for create and edit, saving with a date and one identity arm
(catalogue pick or free text); nothing else can block the save
- catalogue picker searches localised names and generic synonyms over the
static antigen list, never overwriting the free-text arm
- optional dose/series, lot, site, practice combobox, visit suggestion and
note, all reusing the encounter-form primitives
- write hooks fan out through the dose dependent-keys and the reminder reads
- form/site/sheet copy in six locales
- read the immunization log through the query-key factory, unwrapping the
envelope; the series arrive resolved per component antigen from the DTO
- render one group per antigen, a combined dose appearing in each of its
component groups with that component's own position; free-text and dead-slug
rows fold under their verbatim name
- neutral cards, meta in muted, no due status on the page
- add the dose-list dependent-keys bundle, evicting the preventive-care root
alongside the dose list so a satisfied booster never lingers on checkups
- series/lot/error copy in six locales
- register /vaccinations in the clinical nav spine, module-gated on
vaccinations (default-on) and part of the shared record
- join /vaccinations to the profile shared-record route family so a scoped
profile grant can present the page it is allowed to read
- add the page shell: authenticated, module-gated, with the standard
module-off redirect the medications surface uses
- add the nav label and the surface title/subtitle/empty copy in six locales
The visit stage's second half landed while this was in flight and both
touched the same three places. Resolved as one set: the link facade
carries the reverse document-to-visit direction and the new dose pair
side by side, the sharing literals hold both sets of admissions with the
count re-added on top, and the language bundles take the shorter module
description the trunk settled on.
Three lines in a 240 px menu: the name, the access level, the kind of
record. The middle one truncated more often than it read, and it is what
the warning banner states in full once you are inside the record, where it
governs what the page lets you do. Two lines left, and the level stays on
the row as `data-access-level` for the browser journeys.
The switcher's own "Read only" / "Can add entries" strings had no other
reader and leave all six bundles.
The ECG surface opened with three lines of muted text saying what HealthLog
does not do, before a single strip. Two of them said the same thing — one
from the page's explainer slot, one hand-rolled beneath it — and the third
repeated it under the list. The device attribution on the recording itself
already carries that claim where it means something, so the three go and the
page opens on the recordings.
- the device's verdict rides the timestamp as a tag instead of taking a line
of its own beneath it. It qualifies the recording; it is not a second fact
- a recording is a route now, `/insights/ecg/[id]`, with the back link ABOVE
the heading exactly like a workout detail. The detail used to swap itself
into the list card with its back button under the page title, and the
recording had no address at all: nothing to deep link, nothing for the
browser's own Back to return to. It reads by id, so a cold link works
- the list paints the five most recent strips. A watch that records every
morning turns an unbounded list into a scroll, not a history
`ecg-classification.ts` holds the verdict label map the list, the detail and
the cross-link card each used to carry a copy of.
Research Mode was the opt-in behind the estimated GLP-1 drug-level curve:
turn it on, read a disclaimer, get the chart. The chart stopped consulting
the flag several releases ago and has painted for every account since, so
the switch on Settings → Advanced changed no screen in either position. An
inert control that promises to hide a chart is worse than no control.
The curve is simply part of the medication page now. Gone with the switch:
the acknowledgment dialog, `/api/auth/me/research-mode`, the shared status
DTO and its gate helper, the disclaimer-version stamp, the query key, and
the three user columns the acknowledgment wrote (migration 0321). The
Privacy page's second button pointed at the retired control and goes too.
Settings → Advanced is now what it reads as: reset the data, delete the
account.
Environment, external assistant access, Documents, mental wellbeing and
nutrient intake ran 120-165 characters in German where their thirteen
neighbours sat at 30-75. Every one of the five carried the same second
sentence, "off by default", which restates what the switch beside the row
already shows.
The mental-wellbeing row also named two screeners where the module ships
four, and its second sentence held the one statement in the app about what
happens to the answers. That statement moves to the mental-wellbeing page
itself, where somebody is about to answer them.
A guard holds every module row to one sentence in all six locales, and
under the 120-character guideline in English and German.
Two cards on Settings → Account were asking questions that already have a
home somewhere else.
- cycle tracking is a module, and the Modules hub carries the switch that
drives the same `cycleTrackingEnabled` column. The Account card was a
second door to one decision, so it goes, and the "manage in Account"
deep-link beside the module switch goes with it. That link pointed at a
`#cycle-tracking` anchor no element ever rendered, so it never landed
anywhere in the first place
- changing a password belongs with the second factors and the passkeys.
The card moves to Settings → Security and leads that page. It renders
before the factor status resolves: it is now the only way to change a
password and must not depend on a read it does not need
Account keeps the profile, the photo, the identity fields and the AI
context. The `settings.cycleTracking.*` strings leave all six bundles.
- a query-key module and a dependent bundle that also evicts the
preventive-care root, because logging a dose settles a booster and the
checkups list goes stale in the same tick
- the OpenAPI module publishes the resolved series with the contract
written into the description: render text from these numbers, never
recompute them
- the health-background consent sentence now names the immunization
history in all six languages, so the widening is stated where it is
agreed to rather than inferred from a code comment
- no server-side cache helper: nothing cached reads a dose, and a sweep
over caches nobody reads would look like coverage without being it
The VISITS leaf ships in the release that made a visit possible, because a
selectable checkbox that produces nothing is worse than no checkbox. Leaf,
collector and PDF renderer land together, and the leaf carries no LEAF_MODULE
entry — like ALLERGIES beside it — because a visit is core and the whole report
is already gated by doctorReport. The absence is commented at the map so a
later reader does not read it as an oversight.
The rows print the date, the practice with its specialty, the kind, the reason,
the outcome and the conditions the visit was filed against. The two encrypted
columns decrypt at render, fail-soft, and never enter the stored selection. A
condition the person has since deleted is not named: the link row survives a
soft delete, and printing a label they withdrew would put it in front of a
doctor.
No FHIR resource type is added. That deferral is already written at the
Encounter model, and the bundle builder now says so at the point a reader would
look for the missing section: FHIR splits the future from the past across
Appointment and Encounter, which is a serialisation question this release does
not answer.
Two properties are free and worth stating: a stored selection never grows
(unknown ids drop) and a clinician share link's sections are frozen write-once,
so no existing report and no already-shared link gains a section.
The panel also offers a booked visit as the report's reason — one query, one
prefilled window end, one dismissible offer that names its own source and never
applies itself. A window control that moves on its own is what makes an export
panel untrustworthy.
- four route files: list and create, read and edit and delete, restore,
and the link pair; every delegable one is classified as health
background, in each leg of the sharing guard, with its reason written
- link and unlink stay bare, consistent with the document and visit link
routes; the three are revisited as one decision
- vaccinations becomes the nineteenth module key, default-on and
surface-gated like medications: the data routes stay exempt so a
restore keeps working and re-enabling finds every dose intact
- a delegate's dose entries get their own sentence in the owner's
activity view rather than reading as a generic change
A lab panel files itself against the visit it came from, and never has to. The
OCR review and the manual form both offer the suggestion through the one shared
field, anchored on the draw date rather than on now, so backdating a reading
moves the offer with it. The commit writes one link PER RESULT ROW, which is
why the join is m:n: a marker re-run on a different day belongs to its own
visit. Best-effort by design — the readings are the write that matters and must
not be lost to a link that could not be made.
The condition side gets a Visits about this card with an action that pre-links
the episode. From the episode side only, and that is a decision: an episode
spans weeks and a visit is a point, so matching the other way would be wrong
often enough to teach a person to ignore every suggestion in the product.
A due checkup gains a second action beside erledigt. Filing the visit as done
satisfies the checkup through the same forward-only, idempotent primitive the
checkbox uses, so doing both is one satisfaction and no error. The two actions
answer different questions — one says it happened, the other says where.
An appointment now appears where a person looks, and NEITHER surface relaxes
the reminder-table exclusion that keeps it out of the preventive-care lists.
Both read the visit table: a quiet line under the dashboard checkup block for
anything inside two weeks, and one rail item for today or tomorrow. A diff in
either that touches origin is the wrong fix.
A visit write now also refreshes the reminder reads. Closing a checkup moves
its next-due date on three surfaces that would otherwise keep showing it due
until their own poll.
OpenAPI regenerated in the same commit.
- a new backup section reads the dose and its document link through
their own delegates and writes both ends in one file, the arrangement
the visit section already uses
- the practitioner and the visit remap to the rows the same restore put
back; the booster reminder never travels, so its reference resolves to
null and the drop is reported rather than swallowed
- the antigen slug restores verbatim: a slug the catalogue has since
dropped still says what a person was given
- the round trip seeds a dose with every optional column filled and
reads each one back, so a returning row count cannot stand in for a
returning record
Every arm here is the same shape as the one that already exists for
conditions, because the vault's filing model is settled and a second shape
would be the thing to explain rather than the thing to use: encounterIds on
upload and on PATCH with replace-set semantics, linkEncounter / unlinkEncounter
beside the episode pair in the bulk action, and an encounterId list filter.
Both directions ride ONE table. The visit's sheet writes encounter_document_links
from its end and the document reads it from the other, declared as a second
direction in the link service rather than a fourth table. A visit's kind
crosses the wire as the enum constant rather than a rendered name: unlike a
condition label, which is the person's own words, a kind is one of eight closed
values whose human name the reader's own bundle owns.
The suggestion is offered where the document is being looked at, resolved by
the shared rule rather than by this surface: one candidate pre-selects with an
undo, two offer a picker with nothing pre-selected, none offers nothing at all.
It resolves with no AI provider configured, and it renders nothing once the
document already names a visit.
The bulk verb has a caller — the vault's selection bar files a whole selection
against a visit, mirroring the condition verb and hiding itself on an account
with no visits. Nothing here is one-ended.
OpenAPI regenerated in the same commit.
/checkups gains a second section rather than a nav entry. The page already
owns the right sentence — when do I have to do what, where — and is
deliberately not module gated, which is the same reason a visit carries no
module key of its own: what a visit may point AT follows the modules that own
those things, so the gate sits on the three link pickers and blanks the block
rather than rendering an empty one.
A visit saves with a date and nothing else. No required practitioner, no
required kind, no required link, and the only thing that can disable Save is a
date that will not parse. The status is derived from the date on a new visit
and only becomes a field on an existing one, because somebody booking an
appointment should not also have to tell the app that it is in the future.
The address book is record content, not configuration, so it sits under
/checkups rather than in Settings — and it inherits that page's route-family
classification with it. Deleting an entry says in one sentence that the old
visits stay, because a person deleting a doctor has every reason to fear
otherwise.
Two defects closed on the way:
- /checkups carried sharedRecord: true and appeared in neither route family,
so a scoped grant could not present it at all. It was invisible while a
whole-record grant admitted every path; the visits section makes it load
bearing, and the page is now listed under both domains whose content it
shows. Deduplicated, or a grant holding both would offer the page twice.
- the address-book search read the name only, so looking a practice up by the
name on the door returned nothing and read as "not in the book".
The practitioner search description in the OpenAPI module moved with it.
Two readings, side by side, never merged. The person's own rating leads
— larger, first, and returned exactly as it was stored — and the
comparison sits beside it with its range and the number of days it was
built from. The deviation is measured against the range, not against its
midpoint: inside it is a day that went as the account's own past days
suggest, and only outside it is worth a sentence.
The sentence is a counterfactual and says so. Never "your mood is 5.4",
always "a value around 5.4 would have been expected". The explanation
names what the comparison weighted for that day, out of what the job
stored for that day, in the words the capture sheet already uses — a
person reads "Workload" here because that is what the slider they moved
was called.
The route declares the whole record rather than the mind section, and
the reason is written at the declaration: the comparison reads the day's
context AND the figures the sleep, activity and recovery modules hold,
and it names them, so a delegate holding only the mind section would
receive a derived view of sections the owner declined to share. A scoped
grant is refused outright rather than served a contribution list with
the linked features quietly removed, which would read as "only the mood
fields mattered" and be false.
The causal-verb ban is a test now rather than a review habit: the copy
guard reads all six bundles and fails on a causal construction in any of
them, and on a statement that quotes a value without the days behind it.
Both halves were watched failing.
Review of the visit work found the appointment arm leaking in both
directions: rows the visit routes could strand, and a reminder surface
that would act on one as though it were a checkup.
An edit could orphan a live reminder. Pointing a still-booked visit at
the checkup it closed walked the foreign key off the visit's own
reminder, and the next reschedule then minted a second one, because the
re-anchor path asked the key what the visit owned instead of asking the
engine. An orphan fires: the cron reads the reminder table, not the
visit list, so it pushed for an appointment nobody could see, edit or
cancel, and deleting the visit did not reach it. The edit arm now
refuses the contradiction the create arm already refused, ownership is
resolved through the relation, deletion retires the row it owns rather
than the row it points at, and a sweep at the end of every visit write
retires anything left armed that no visit claims -- so the state is
unreachable rather than merely unlikely.
The by-id reminder routes never excluded appointments, and that was the
larger hole. Marking one done committed the satisfy and THEN failed on
the mapper, so every appointment push a person acknowledged answered 500
after the write had landed. An edit could give a spent one-shot a
cadence and turn it into a permanent nudge that appears in no list, or
switch a cancelled one back on. The delete is a hard delete, so somebody
holding only the measurements domain could destroy a row belonging to a
visit in the health background. All five refuse in the lookup now -- an
appointment is not addressable there at all -- and the chat buttons that
mark a reminder done or postpone it refuse it the same way. Nor is an
appointment satisfied by a lab result: the auto-satisfy matcher used to
resolve any free-text reminder from any panel, which would have marked
next month's cardiology appointment attended.
A visit is a seam between four domains, and names were crossing it. The
values never did, but the title of a discharge letter is itself the
sensitive part, and a grant that opened only the health background was
never consent for it. Link labels and dates now follow the target's own
domain; the link is still listed, so a delegate learns the visit
produced a lab result without learning which analyte. Closing a checkup
follows the same rule: it re-anchors a preventive-care cadence, which is
a measurements write, so a health-background delegate files the visit
and the closure is reported as skipped rather than performed in silence.
Three smaller repairs. A checkup now closes at the visit's own instant
rather than at the moment it was typed in, which anchors a backdated
entry correctly and makes correcting a mis-filed visit the no-op it
should always have been. A window ending before today no longer widens
back out to today. And the fallback in a push notification was the raw
enum constant, so a lock screen could read a schema value; it reads the
record owner's own language now, in all six.
The page ended on a "Why?" button that opened the coach with a
prefilled question about the session. It leaves on the maintainer's
call; the coach remains reachable from its own surfaces. The two
strings it carried leave all six locales with it.
The capture section and the insights card called the five sliders
"Five scales", a count where a name belongs. They now carry the
state-of-mind wording in all six languages, with Spanish diverging
from the first slider's own label so the block and the mood slider
stay distinguishable.
Query keys land in the central factory before any surface reads them,
because the lint rule that bans a bare array means the first component
listing visits cannot compile without a slot. There is deliberately no
server-side cache invalidator beside them: nothing cached reads a visit
yet, and a sweep over caches nobody reads would look like coverage
without being any. The module says so at the place a reader would go
looking.
The OpenAPI table publishes seven paths with everything resolved — the
practitioner as an object rather than an id, each link with its label
and date, the appointment's next due instant computed. What it does not
publish is the reminder origin enum, which still reads with two members:
the third value exists in storage and reaches no response, because every
preventive-care read excludes it. Keeping that contract closed is why
the exclusion is worth a guard.
Classifying visits under the existing health-background domain widens
what that domain grants. Somebody who shared it so a relative could see
an allergy list now also shares every appointment and the address book.
That is stated where it is agreed to rather than left in a code comment:
the consent screen's sentence names visits and practices, in all six
locales, and the activity feed gains a verb line for each of the eight
new delegated actions so an owner reads what was done rather than
'made a change'.
Nine visit routes and five practitioner routes, all classified under the
existing profile domain and each reviewed into the frozen sharing
literal with the sentence that argues for it. An address book of a
person's own doctors is record content, not account configuration: it
names no credential, no integration and no notification channel, which
is the fence that classification turns on. The eight sharing domains are
unchanged. Link and unlink stay bare, matching document link and unlink,
and the pair is revisited as one decision later.
Nothing is a required field. A visit saves with a date and nothing else.
Every id a body may carry is re-narrowed to the caller before anything
is written, and an unknown link id never blocks a save.
A booked visit mints exactly one reminder on the existing engine with
the new origin, and the appointment half is the part worth reading
twice. Both cadence columns stay null, which is the schema's one-shot,
so the first due instant is stamped directly and the never-nag property
is structural rather than guarded: after it fires, the cadence helper
has no occurrence to return. Rescheduling re-anchors that row and never
mints a second, which the tests assert by counting rows rather than by
checking the date moved.
Filing a visit as done closes the checkup it was filed against, on the
transition rather than on every later edit. Writing the test first found
that: satisfying is forward-only, so re-saving a completed visit was
pushing the checkup's next due date out again as if the person had been
a second time.
An appointment must appear on no preventive-care surface, and that is
four separate reads, one of them in the assistant tooling. Each carries
the exclusion, a guard proves all four by source text with a non-zero
match count, and the reminder mapper refuses such a row outright — so a
read site that loses its filter fails loudly instead of showing an
appointment as a checkup. The published origin enum is untouched.
- one comparison per context value, reusing the Welch test, the day floors and
the Benjamini-Hochberg family the tag board already runs; a wide sweep
filtered on a raw p < 0.05 would surface one-in-twenty noise as a finding
- two entries on one day count as one day
- every statement names how many days it compared, on the same surface as the
claim, and says it shows a connection rather than a cause
- the assistant snapshot carries the same rows with that constraint written
out beside them
- work, contacts, leisure and one notable event, each a closed accordion with
a count badge, on both the capture sheet and the edit dialog
- sleep, activity, vitals and the illness journal render read-only beside
them, each row naming where its figure comes from and offering a way in to
change it there
- the body line links into the illness module and captures nothing
- six locales for every label and every anchor
A person's whole 'which doctor' history was one free-text column on a
reminder: they typed the practice name once and could reach nothing else
that happened there. This turns that string into a record.
Five tables in migration 0320, all additive: an encounter that happened
or is booked, the account's own address book of practitioners, and three
links from a visit to the documents, lab results and conditions it
produced. The encounter is slim on purpose — the diagnosis stays on the
linked condition, the address on the linked practitioner, and the reason
each field was kept or cut is written at the model. No module gate, and
the schema says why rather than leaving the absence to be read as an
oversight.
Every carrier lands with the tables rather than after them. All five are
classified as travelling both ways and are proven doing it: a seeded row
of each goes out through the real export builder, the account is deleted
so the schema's cascade decides what survives, and the rows are counted
back through the real restore route. Two references deliberately do not
survive and are named instead of dropped quietly — the reminder a visit
points at is not in the backup, and a link whose far side a portable
export does not carry cannot resolve. The wipe plan deletes the links
before the lab results and documents they hang off, or the audit row
would report zero for rows that existed. The three encrypted columns are
registered for key rotation, without which dropping a legacy key would
make those notes permanently unreadable.
Widening the reminder origin enum is a type error at the reminder DTO,
which is the right outcome: the mapper now refuses an appointment row
outright rather than relabelling it as a checkup.
- one module holds the four sections, their key lists and the rating fields;
the Zod enums, the capture UI and the analytics all read it, so a value the
vocabulary does not name cannot be stored
- labels state facts, never judgements, and the contact circles are groups of
people rather than a score
- a pair guard binds the arrays to all six bundles in both directions, because
the reverse-coverage matcher sees a `mood.context.` template and stops being
able to tell a live label from a stale one