The root layout now reads the public monitoring switches while it
renders, so nothing calls GET /api/monitoring/settings any more (the
native client never did); the route and its unpublished entry in the
route coverage guard go. The Coach feedback card reads the AI quality
card's cell, which left queryKeys.adminCoachFeedback() without a reader.
The Coach memory editor, the managed profile's handover link, the cycle
length fields and the command palette's search set a font size under
16 px on touch screens, so iOS zoomed the page when one took focus. They
now read 16 px on a touch screen and keep their smaller size under a
fine pointer, like the field primitives. With no field left over, the
guard's list of known offenders goes.
A swipe past the close point paints the sheet where the finger let go and
asks the owner to close it. A sheet holding unsaved input asks about
discarding first and stays open, so it sat half pushed off the screen
behind the question. One frame after the close, a sheet whose content is
still open now glides back to its resting place. The function's size
baseline rises by the seven lines this takes.
The illness episode, cycle day, custom metric, target, mood tag, symptom
and life event sheets open filled from a record or a fetch, so the body
check cannot tell typed input from what was loaded, and a stray tap
outside or a swipe dropped the change without asking. Each now keeps
what it opened with (the cycle day once its fetch has hydrated the form)
and passes whether the fields differ to the sheet's dismissal guard. The
custom metric and life event forms report it to their hosts the way the
workout form does.
The size baselines of the six sheet functions and the cycle sheet file
are raised by the few lines the comparison takes.
Where the composer fills the column (a phone, the drawer, a narrow
column), the New conversation button floats above the composer and
covered the last message's time and actions at 390 px. The thread now
keeps the button's height free below the last message while the button
floats, the way the shell clears the Coach button, and drops back to its
plain gutter once the button sits beside a wide composer.
The tab strip on every Insights page read the whole overview (22 KB),
a workouts page, the ECG list and the analytics summaries to learn which
pills to show. GET /api/insights/presence answers just that, for the
record on screen: the reading count per type after the record's module
switches, and whether it has mood entries in the last 90 days, a tracked
scheduled medication, workouts and ECG recordings. A flag whose page a
switched-off module hides is false and its rows are not read. The strip
now reads this and the saved layout; each page keeps loading its own data.
The cases for the bounded read asserted the query's where clause on a
mocked client and cast their fixtures past the type checker. Against
Postgres they now show what the read promises: a rolling medication last
taken before the window shows the dose that fell due inside it, and a year
of earlier takes answers the same ledger as a short history.
The route test that pinned the cached answer replaced the database client
and the engines, so it saw the calls the route makes and not what a reader
gets back. The integration test drives the shipped route: a reading written
past the API leaves the second open on the stored answer, while a
measurement written through the API and a module switch both show it.
The sheet now owns its dismissal guard and the Back handling, and three pages gained one or two lines for the save and refetch order; each branch had recorded its numbers, the merge kept only one side.
Two Settings pages had German words in their address in every
language: /settings/gesundheitsakte and /settings/layout/vorsorge.
They are /settings/health-record and /settings/layout/checkups now,
and the old addresses (and the older /settings/sharing and
/settings/vorsorge) redirect permanently, so bookmarks and links in
old reports keep working; the report range a since-last-visit link
carries in its query passes through. The in-app links, the tour, the
command palette, the anchor forwards and the e2e specs use the new
addresses, and the two message groups follow the new slugs.
The medication detail tabs put German words into the address for
every language (`?tab=verlauf`, `?tab=bestand`). They are overview,
effect, schedule, supply, history, injection and advanced now. Every
old slug maps to its tab, so a bookmark, a link in an old report or a
low-stock notification sent before the change opens where it did. The
low-stock push, the list card and the efficacy summary link with the
new slugs. The slug vocabulary moves into its own module with a test.
Ketones joined in v1.47.2 without their two sibling lists. The metric
catalog treated them as connectable and sent a user without readings
to the integrations page, where nothing delivers ketones; they now
count as manual-only and the row offers "Log ketones". And a checkup
could not be tied to ketones, so a "measure ketones" reminder never
resolved on its own; ketones are now a reminder target next to blood
glucose, with a label in all ten locales. A catalog test also holds
every metric that cannot be connected to a way to log it.
"Log day" always opened on today and let the picker run to today, so
for an illness marked recovered on Monday a note added on Thursday was
filed three days after the recovery, and the day view showed a sick day
with no illness beside it. Both places that open the sheet now hand it
the episode's last day: today while it runs, the recovery day (read in
the profile zone, as the server reads it) once it ended. The picker
opens there and offers nothing later.
The card view marks a checkup the Coach created and one that is
switched off; the list view drew neither, so a disabled checkup looked
active there. Both views now take the badges from one helper, and the
list row shows them beside the title.
The illness screens showed "Weight", "Pulse", "Recovery" and "Day" in
English in German, French, Spanish, Italian and Polish: the four keys
had been copied from English into those catalogs, while their
neighbours were translated. The same copy left the medication wizard's
"Name" step in French, Spanish, Italian and Polish and the dashboard
layout's "Chart" column in German in English. A test now holds the
illness keys translated in every locale.
The guidelines still described the v1.4 app: a drawer nobody uses, six
languages instead of ten, a hover tooltip as the default inline help
although a tooltip never opens on a phone, Dracula utilities for status
colours, a page container and a Settings body gutter the standards
removed, and components that do not exist (a status callout, a
multi-select, honey-pot fields, a per-integration test log). Where the
UI standards own a topic, the page now points there; the rest (button
choice, form controls, settings persistence, filter bars, the dialog or
sheet choice, loading, empty and error states, accessibility, i18n,
motion) is kept and corrected, with the section numbers code comments
cite unchanged. Inline help is an InfoPopover that opens on a tap.
The demo migration was named after the host it targets, and its
README, the v1.5 demo seed and a contributing note named the
maintainer's servers, a container id, an app id and two account ids.
The script already takes every host, container and account from its
environment, so the prose now says source and target and lists the
variables instead. It is renamed to migrate-demo-account.sh; the
password guard follows the new name.
The feature bullets opened with slogans; they now open with what the
feature is. Dashes and middle dots become commas, full stops and bars.
Health scores and correlations move out of the AI bullet, since no
model computes them, and that bullet is marked optional. "Ships
releases roughly weekly" did not match the release list; the README
now says it ships often and points at the release notes rather than
the full CHANGELOG.
The encryption and PWA bullets stay as they are for now: the PWA line
is being corrected on its own.
Settings, About sent every user to CHANGELOG.md on GitHub, a 1.4 MB
file with every release since the first. The link now opens the
releases page, which carries the same notes per version and loads.
The `changelog` field of /api/version keeps its name and stays a URL.
CONTRIBUTING-AI.md pointed contributors at a CLAUDE.md "checked in and
kept current"; that file is not in the repository, so the pointer and
the twenty references to it in folder READMEs, docs and comments led
nowhere. The note goes; its one lasting rule (whoever commits a change
stands behind it, no real names, no live health figures, no secrets)
moves into CONTRIBUTING.md. The folder READMEs keep their own content
and drop the dead link, and a reference to the unpublished planning
folder goes with it.
The checked-in .mcp.json was a local editor config no workflow reads;
it is untracked and ignored now. The translation tiers are described
as machine-translated first drafts reviewed by the community, and the
list names Russian, which it missed.
Five tests only read source or message files to assert that something
long gone stays gone: the moodLog bridge (removed in July), the chart's
target-adjust link (v1.16.8), the mood notes timeline, the medication
detail page's old dose card and wizard host (v1.15.18), and the
obsolete v1.34.1 copy keys. They protect no behaviour, and the general
i18n guards already hold every key and placeholder across the ten
locales, which the v1.34.1 list did for nine. The source-read baseline
drops from 337 to 332.
After switching between mg/dL and mmol/L, the target panels on the
glucose page kept the old unit for up to ten minutes. Their unit comes
from the cached targets payload, and the save only refreshed the
account payload. The select now invalidates the targets query, and the
route evicts the profile-derived server caches when the unit changes,
the same way the thresholds and source-priority writes do.
The unit-system and glucose-unit selects invalidated
`userUnitPreference()` and `userGlucoseUnit()` after a save, but no
query was ever registered under either key; every surface reads the
units from the account payload, which both selects already refresh.
The localised dispatch helper accepted a `channel` and copied it into
the metadata as `preferredChannel`, which no sender reads. No caller
passed it either; every message still fans out across the user's
enabled channels. The option, its comment and the test that pinned the
copy go.
The card menu took an optional side-effect item for the GLP-1 card, but
no page ever passed the handler, so the item never rendered; side
effects are logged on the detail page. The slot, its message key in ten
locales and the tests that passed it by hand go.
One test pinned behaviour only the dead slot produced: a delegate who
may write but not manage kept a menu trigger because the test supplied
the handler. In the app that delegate never had one, and the test now
says so.
Four files under src had no production importer:
- the keyset measurement reader for backups: both backup writers moved
to the shared account copy and the streamed full backup, and its
header still claimed both used it;
- the HOMA-IR calculator and the mood stability label, never wired to a
surface;
- the backup verdict helper, which only the backup classification guard
called. It now lives in that guard.
The queue expiry table is what the job queue guards check the registry
against and nothing in the worker reads it, so it moves to the test
helpers. The version-stamp baseline drops the deleted files.
An unreadable provider key is now reported once: the empty-chain fallback no longer opens the same key a second time. Two files shrank, so the size baseline steps down.
Nothing in package.json, the workflows, the Dockerfile, the docs or the
other scripts named these: the bench result dumps of v1.44 and v1.45
that no document cites, the version-pinned release checks, the v1.42
correlation dry run, the read-only diagnostics and their cleanup and
conversion companions for defects fixed long ago, the one-off locale
builder from v1.4.25, and the four June seeders for the demo account.
Kept: the two bench results the benchmarks doc cites, the Google Health
sleep repair (the duplicate-night issue is still open), the demo
migration its password guard reads, the column-reader sweep its test
imports, and the message-bundle tools. The scripts README now lists the
kept tools that it did not name before and drops a reference to a file
that does not exist.
The comment said the series route caps sleep at 365 days; the route
accepts up to 3650, which is what the "All" tab asks for and what the
clamp below applies.
The cycle page's calendar card showed one line of loading text until
its read arrived, then grew to the month grid, so the predictions and
everything below moved down once the page had drawn. The card now
draws the month header and this month's weeks in the grid's own cell
size while it loads, with the loading text kept for screen readers.
`/admin/backups` opens with the recovery readiness card, which showed a
one-line spinner until its seven signals arrived as one block, so the
backups and off-host cards below jumped down by the whole list after
they had drawn. The loading state now draws one placeholder row per
signal in the list's own row shape, with the loading text kept for
screen readers.
A metric page without data offers to ask the Coach about the metric.
44 of the 49 pages passed that question as an English literal, so a
German, French or Korean reader got an English first message; only 5
read it from the catalog. 31 of the 44 were one sentence with the
metric's name swapped in.
`HealthKitMetricPage` now asks that sentence once, translated, around
the page's own title, and the 31 pages drop the prop. The 13 pages
with a question of their own (steps, HRV, sleep, mood, workouts, blood
pressure and others) read it from the catalog like the 5 did, in all
ten languages.
The dashboard snapshot carries the mood tile's series from the same
engine `/api/mood/analytics` answers from, and the snapshot already
seeds the widget layout, but nothing seeded the mood analytics cell the
mood chart reads, so every dashboard open with the mood chart asked for
the series again. Both seed points, the server render and the client
snapshot read, now fill that cell too, under the same rule as the
layout: only an empty cell, and only when the tile has readings, the
one case where its summary is the route's.
Every document load, the sign-in page included, mounted a client
effect that asked `/api/monitoring/settings` for three instance-wide
values (false, null, false on a default install) before it mounted the
analytics script or the error reporter. The root layout is a server
component and now reads them while it renders and passes them in, so
the request and the effect go. The key-mismatch page reads nothing, as
before. The route stays for other callers.
The layout shell around every Insights page read the advisor briefing
(`GET /api/insights/generate`, about half a second on the demo) for the
regenerate button on the tab strip, and once the answer arrived it
loaded the schema chunk that parses it. The strip shows no briefing;
only the overview does, and it reads the same cell itself. On a metric
page both were spent on a button.
The shell's hook now reads the briefing only while its own regenerate
settles. A regenerate from a page that never read the briefing reads
it first, so a timed-out attempt still settles against the briefing it
replaces instead of taking the old one for fresh. The button, its
toasts and the overview are unchanged. The strip's comments that said
only the overview carries the button are corrected.
The history tab asks for a window of at most a year, but the route read
every intake the medication ever had: a medication taken three times a
day for five years read about 5,500 rows on each open. The full read
also served to find the last intake for rolling cadences.
The route now reads the rows anchored or taken in the window, and of
the rows before it only the latest take, which is the anchor a rolling
grid re-starts from and the last intake when none falls in the window.
Earlier takes place no slot in the window, so the ledger is the same.
The read moves into its own function, which shortens the handler.
Insights > Medications sent two requests per medication on entry: the
compliance calendar inside each card and the efficacy read behind the
summary card, which sits below every card and shows one line per
medication. The summary now mounts through `DeferUntilNear`, like the
overview's sections, so the efficacy reads go out only when the reader
scrolls toward it. The cards and what the summary shows are unchanged.
The comment that called the summary "never a second computation" goes:
the efficacy route builds the view on every request.
`/admin/coach` mounts the AI quality card and the Coach feedback card,
and both read `GET /api/admin/ai-quality` with the same unwrap, under
two different query keys, so every open sent the request twice. The
feedback card now reads the quality card's cell.
The version poller asked `/api/version` every 60 s from a plain timer,
also in a background tab or a minimised window, for as long as the
browser let timers run. It was the largest standing poll and the only
one that kept going while hidden, although the poll notes describe it
as the same shape as the others.
A tick now asks only while the tab is in view, and a tab that comes
back after a full interval away checks once at once, so a release
still surfaces on return.
`/api/insights/health-status` recomputed the coincident deviation and
the 180-day changepoint scan over every vital on each open of Insights,
the only read on that page that stayed slow when warm, for a body of a
few hundred bytes. It now goes through the same analytics cell its
sibling reads use. Measurement and mood writes sweep the record's
cells; the reader's day and the switched-off modules are part of the
key, so a new day or a module switch never serves the other answer.
Weight thresholds accept values from 1 kg, the notification preference descriptions name only the events something sends, and the error-code catalogue lists the new codes.
The settings nav filtered its Coach entry by the raw module map, which
follows the operator's switch and Hide Coach but not a missing provider
or missing consent, so it kept an entry the sidebar and the bottom bar
had already dropped. It now reads useNavModules like them. Settings, AI
decides from the same map whether to list the stored Coach memory, so
the memory always has one place in Settings where it can be read and
deleted.
On a phone the tour falls back to a card at the bottom edge, 8 px above
it, so in the installed app its Next and Skip row sat inside the home
indicator strip. The card now adds the bottom safe area, as the bottom
bar and the sheets do.
The alias chips on a biomarker and the attached-document chips of a
share link had 24 and 20 px remove buttons that act on the first tap,
without a confirm. Aiming for one chip's x on a phone could remove the
one beside it. Both keep their look and take the 44 px touch area the
attachment pills already use.