The picker for linking documents to an illness and for choosing
documents for a share link reads the newest fifty and dropped the
route's nextCursor, so with a longer vault an older report seemed not
to exist unless one already knew to search for it. A cut list now ends
with a line that says how many are shown and that the search reaches
the rest. Paging was left out: the coach attachment picker shares the
picker's cache key with a single-page read.
Deleting a vaccination, a doctor visit or an address-book entry closed
the sheet with no way back, although all three are soft deletes with a
documented restore route. The vaccination hook had a restore mutation
nobody called; visits and the address book had none.
The two hooks gain a restore mutation, and each delete now confirms
with an Undo toast, the same pattern the illness episode uses. The Undo
runs after the sheet has closed, so it awaits the restore itself and
the hook's own onSuccess refreshes the lists, instead of relying on
callbacks a closed sheet never receives. Restoring an address-book
entry brings the entry back, not the links the delete cleared on the
visits that named it. The three new success toasts are pinned in the
success-affordance guard.
personal-record-badge.tsx, intake-history-editable.tsx and
intake-history-preview.tsx had no importer outside their own tests and
the preview, so they rendered nowhere while four guard tests kept them
listed as live surfaces. They go with their tests, their guard entries
and the strings only they used.
GET /api/personal-records stays: the native app reads it. The
resolved-field guard now records its benchmark band as a native-only
field instead of naming the deleted badge as its reader.
A mood, symptom, lab value, custom metric value, unscheduled dose,
allergy, vaccination, visit or measurement saved on a weak signal could
be stored twice: the POST committed, the answer missed the 15 s
timeout, the form said it could not save, and the second tap was a new
call with a new key, so the server ran it again.
useIdempotentPost keeps one key until a save lands and renews it for the
next entry, so the retry replays the first answer. The create forms and
the three create hooks post through it; the comment on the per-call key
in api-fetch now says what that key does and does not cover.
LabForm and MoodForm each gain the one line that mounts the key, so
their function-size baselines go up by one. The intake toast guard now
recognises a dose posted through the hook.
Recovery, strain and other computed rows in the measurement list carried
a pencil and a bin. The pencil opened an edit sheet labelled "Value ():"
whose save the server then refused, and the bin deleted a score the
engine owns. Both are gone for COMPUTED rows.
Readings from a device or a provider keep both controls: the server takes
a note or time edit on them, and a wrong scale reading has to stay
removable. The server is unchanged.
The dose reminder printed the slot as the raw stored HH:mm, so it said
"20:00" while the medication card said "08:00 PM". That hit everyone who
set a 12-hour clock, and every en or ko user on the automatic setting,
which resolves to 12 hours for those languages.
The reminder now formats the slot's instant in the owner's zone with the
same formatter the card uses, honouring the owner's hour-cycle choice. A
recipient reading the message in another language gets that language's
clock when the owner left the setting on automatic.
The custom metric, allergy, vaccination, booster, visit, address-book,
workout and quick-add dose forms printed the server's error string as
it came: English in every locale, and for a network failure the
browser's own TypeError text. They now resolve the error code through
localizedApiError like the other entry forms, so a known refusal reads
in the person's language and anything else falls back to the form's
"could not save" sentence. A custom metric whose name is taken keeps
its specific message, now translated.
The workout form's browser test expected the server prose and now
expects the form's sentence.
Typing 720 instead of 72.0 in the measurement edit sheet showed the
English banner "Validation failed" in every locale, with no field
marked. The route refuses a changed value outside the type's band with a
422 whose issue list names the field, and the client dropped that list.
The sheet now checks the same band before sending and says, under the
value input and in the unit it shows, which range is accepted. ApiError
carries the field paths of a refusal, so a value the server still
refuses lands on the field too; any other refusal goes through
localizedApiError instead of printing the server's prose.
With the Medications module switched off, dose reminders, missed-dose
rows and low-stock alerts kept coming, a tap on one landed on
"Medications is switched off", and the Telegram buttons still booked
intakes. The reminder tick now drops the medications of an owner with the
module off, one module read per owner and tick, so neither a reminder nor
a missed-dose placeholder is written while it is off. The low-stock pass
skips such a user, as the mood reminder does for its module, and the bot
offers and books nothing.
A medication whose course had ended still counted as live: the low-stock
pass kept warning about its stock, and the bot's /add list offered it and
booked a dose after the end, taking a unit off the stock. The low-stock
pass and every bot path now skip a course that has ended on the person's
clock. A course that has not started yet keeps its reorder warning and
stays in the bot. A paused medication is already active: false, so the
existing filter keeps it out.
The bot's six copies of the medication lookup go through one helper.
The notification matrix listed "Unusual measurement", "Low adherence",
"Withings sync failed" and "Personal record" with a switch per channel,
three of them on by default, yet nothing dispatches any of them: a reading
far outside its range or a week of missed doses produced no message.
eventTypesForRole now leaves them out for every role, next to the
admin-only filter, and the preferences read no longer serves rows stored
for them. Their labels in all locales and their tap targets are deleted.
The types stay in EVENT_TYPES, so the preference write and the stored rows
keep parsing; no migration.
The label and tap-target tests walked EVENT_TYPES and so pinned the four
in place; they now walk the event types that are sent.
A save after the session ran out, or into a rate limit, showed the form's
generic "could not save". The server already names both refusals in
meta.errorCode (auth.missing for an ended web session, the status codes
http.rate_limited, http.forbidden and http.invalid otherwise), but the
catalogs had no sentence for them, so the lookup fell through to the
fallback. They have one now in all ten locales, and a refusal that
reaches the client without a code (a reverse proxy's 429) is read by its
status the way the server labels it.
Saving a weight, a mood, a workout, a measurement edit or a quick-add
dose emptied the form and kept the button spinning until every read the
write touches had refetched (snapshot, digest, charts, insights). On a
slow connection that looked like nothing had been saved. The vaccination,
visit, address-book, symptom and check-up hooks did the same through an
onSuccess that returned the refetch, which a mutation waits for.
The forms now report and close first and start the refresh without
waiting for it; the hooks no longer return it. The quick-add loses a
second sweep of the inline compliance charts that the medication bundle
already covers, and its source pin follows.
Confirming a document's facts creates medications, conditions and lab
values, and the profile take-over creates a condition or an allergy.
Both evicted only the roots of those lists, so the dashboard and the
Today digest showed the old medication set until the 120 s poll, the day
and timeline kept the old state for five minutes, and adherence and
nutrient reads were not evicted at all.
Both now spread the bundles of what they create, and the condition paths
pair them with the inactive refetch of the daily reads.
Marking a check-up done on the dashboard updated its tile, while the
Today rail above it kept listing it as due until the 120 s poll. The
reminder writes refreshed the two daily reads only through the inactive
pass, which fetches nothing for a query that is on screen. They are now
invalidated the ordinary way too, so a mounted rail refetches, and the
day root joins them because a check-up marked done is listed on its day.
The test fake pinned only the inactive call; a real query client now
shows the mounted rail refetching.
Symptoms, workouts, lab results, allergies, documents and questionnaires
evicted only their own lists. An open day, its calendar dots and the
timeline kept the pre-write answer for up to five minutes, so "Capture
for this day" saved a symptom or a workout and the day showed the old
list, and a deleted row stayed on it.
Workouts, labs, allergies and documents get a bundle beside the seven
that already carry the day and timeline roots; the six lab lists become
one (the OCR dialog's own copy of the commit hook's list goes with it).
Symptoms and questionnaires add the two roots to their list. Symptoms
stay off the illness bundle on purpose: nothing on the dashboard or the
Today digest reads them. Custom metrics need nothing: neither the day
nor the timeline reads them. invalidateKeys now drops a key listed twice,
since spread bundles share the two roots.
Every browser test iframe is named "vitest-iframe" and the Playwright
provider looks the frame up by that name. Once a page runs a second
file, the first file's iframe can still be found until its detach event
arrives, so the second file's first click lands in a frame that is going
away and fails with "Frame was detached". With fifteen browser files and
at most twelve pages, three pages now run two files each, and a full
local run failed this way in four of five tries. The setup now waits
until the frame found by name is its own.
In the full run the browser version made a neighbouring browser file
fail with a detached frame, while each passed alone. Render the chart in
Node with Recharts stubbed, as the coach result chart tests do.
The revoke audit is now asserted in the integration race test where a
rotation commits while the turn-off waits, instead of a route test that
mocked the handler and the client. The other new tests type their
fixtures rather than casting them away.
The custom metric chart drew its line in --primary, which put the index
in the action colour right under the ketone series drawn in a chart
token. Give the chart an optional series colour, default primary so a
custom metric looks as before, and draw the index in --chart-4.
The select is checked against SatisfiableReminder, so a column the
reschedule needs cannot be left out of a narrowed read again, and the
handler is back under its size baseline.
Only the selected style's hint is shown, so "it may start" and "they
may fall" had no noun on screen to point back to. Name the period in
every locale that left it implied, and put the Korean hints in the same
register as the rest of the card.
The shortcut called preventDefault before checking for the field, so on
a labs or documents page still loading, in error, or with nothing
recorded, "/" did nothing and the browser lost its own use of the key.
The count beside every filter bar was a plain span, so a screen reader
heard nothing while a list narrowed, only the empty state once nothing
was left. Make it a polite status region.
The single-reading line spelled out both readings with their names and
units and was cut off after the glucose in every layout. Show the two
numbers with one unit, which fits the tile, and keep the worded pair for
assistive tech.
DELETE audited the feed it read before its two revoke passes. When a
rotation committed in between, that feed was already revoked by the
rotation and the second pass revoked its successor, so the trail showed
the old feed revoked twice and the new one never. Read back the rows
carrying this request's revokedAt stamp and audit each.
A reading entered in mmol/L is stored as whole mg/dL, and the index
divided the unrounded conversion, so a 5.2 / 0.3 pair printed beside a
17.4 rather than 17.3. Round the glucose to the decimal it is shown with
first and divide that. The tiles also printed a two-decimal ketone
reading to one decimal; show it as recorded.
A "1 (calc)" unit is stored as "1", the explicit no-unit value, but the
dimensionless check ran on the raw string and missed it, so the read
went straight through instead of to review.
The first due slot of a Coach course honoured endsOn, but the tick's
roll-on, satisfy and skip all built their schedule input without it, so
a seven-day twice-daily course kept rolling on indefinitely and never
reached the null nextDueAt the expired-course cleanup waits for.
Carry endsOn on RollableReminder and SatisfiableReminder and select it
wherever a caller narrows the row.
A link with the date bounds reversed showed an empty list; it now reads
as the range it means. The search ignores accents as well as case, so
"hamoglobin" finds "Hämoglobin". The category pill only appears when
there are categories to choose between, or when a link already filters
by the only one, so the filter can still be cleared where it shows.
Clearing the search with its button removed the button under the
focus and dropped focus onto the page; it now stays in the field. The
/ shortcut no longer pulls focus out of an open dialog, menu or list,
and ignores a key another handler already took or one typed while an
input method is composing.
The labs search writes its trimmed query to the URL after a short pause,
and every change of the URL query replaced the field's text. The
field's own write came back as well: a trailing space vanished, so
"vitamin " followed by "d" searched for "vitamind", and a letter
typed while the write landed was lost. A query the field wrote itself
now leaves the field alone; one from back/forward, a link or Reset
still replaces it.
The override in pnpm-workspace.yaml now floors node-forge at 1.4.3, which
clears the five advisories against 1.4.2 and leaves pnpm audit --prod
clean. It arrives only under @parse/node-apn, which pins 1.4.2 exactly;
the APNs sender uses token auth, so the certificate path never runs.
Inside the rail, the full-width search field wrapped on phones and left
the filter icon alone on a line above it, and the shared FilterBar grew
a class override and a second layout rule to make room. The search
field now sits next to the unchanged rail: above it on phones, where
the pills keep the usual scroll row, and in one line with it from sm
up. FilterBar and docs/ui-guidelines.md are back as they were, so the
filter-bar grammar stays the one ui-standards.md defers to.
Drop a local-only ignore line, end the search placeholder with the
ellipsis character the other placeholders use, and put the
UrlFilterSyncOptions interface above the hook's doc comment so that
comment documents the hook.
The description of a labs category lost the status rule and the
sentence that a client never rebuilds the category, the order or the
status. Both are back as they were, with one line added: a client
that shows only the members matching a filter may summarise that
subset by the same rule.
The filter compares only the day of a marker's newest reading, so a
marker measured inside the range and again later drops out. "Date
range" promised more than that. The pill now reads "Last measured" in
every locale, and the from/to semantics are written down where the
filter is defined.
With groups opening only when flagged, the open state was worked out
from the filtered rows. Filtering to in-range markers, or searching for
an unremarkable one, closed the header while the counter reported
matches. A group's default now follows the server's status for the
whole category again, and while any filter is active every group with
a match is open. A group closed by hand during filtering stays closed
while the filter changes, is not stored, and the saved state returns
once the filter is cleared.
The saved-entry check and the reordered legacy precedence in
useModuleListPrefs reach the illness and checkup lists as well, and
the filter rail does not need them now that the sort pill is gone.
The hook is back to what the release line ships; the precedence
question can be raised on its own.
Settings > Labs already holds the list's sort order. The pill added a
second, URL-only one that was lost on the next visit, and Reset cleared
it while the rail counted it as no filter at all. The list now sorts
the way Settings says, and a ?sort param is ignored.