Commit Graph
7808 Commits
Author SHA1 Message Date
Marc-André Bombeck e2ed62c74f docs(api): publish the environment backfill limits 2026-10-08 22:37:56 +02:00
Marc-André Bombeck 98183700ce fix(handover): keep a claimed handover when its Guardian deletes their account
created_by_id cascaded, so a Guardian who deleted their own account took
every handover they had issued with them, including a claimed one whose
decision the new owner had not made yet. The claim had already applied
the proposal, possibly MANAGE for a co-Guardian, and the owner lost the
one place that asks them to confirm it.

The column is nullable now and cleared on delete (migration 0390). An
unclaimed link without a creator is refused like any link whose creator
lost access. A decision that names a Guardian whose account is gone is
refused with 422 instead of failing on a grant for a missing account.
2026-10-08 22:37:56 +02:00
Marc-André Bombeck c7cee9c8a7 fix(coach): hold get_day to the Coach exclusions and keep free text out
get_day read through the day loader rather than the snapshot, so it
ignored the person's Coach exclusions: a person who kept mood or
medications from the Coach had both sent to the model as soon as it
asked for a day. Each exclusion token now maps to the day sections and
measurement types it covers; the sections are not loaded and the types
are dropped from the projection. A guard runs every token through the
executor.

A dated document reached the model under its title or file name, and a
visit under its reason or the practitioner's name. Both now carry their
kind. The remaining titles are fenced as the person's own text in the
Coach, as the MCP endpoint already did.
2026-10-08 22:37:56 +02:00
Marc-André Bombeck 509166afc4 fix(environment): keep one account from draining the Open-Meteo budget
The backfill route shared the analytics-read bucket and sent every
explicit range without a key, so one account could queue two-year
backfills until the instance-wide daily budget was gone for everyone.

The route now allows three backfills a rolling hour per account (429
environment.backfill_rate_limited) and one queued backfill per
twenty-minute slot, keyed on the account (409
environment.backfill_pending). Every fetch made for an account is also
charged to that account's own share of the daily budget, one twentieth
of it, so a refused request ends the run as budget-blocked and the
nightly run continues.
2026-10-08 22:37:56 +02:00
Marc-André Bombeck 78cde56542 fix(environment): fail closed on a travel period that does not open
A location period whose sealed location this host cannot open was
dropped from the resolver, so its days fell through to the home and the
next refresh overwrote the stored travel days with the weather at home.
Those days are now skipped and the run logs an error signal.

A restore now opens every sealed location once. One that does not open
is kept sealed, so the refresh keeps skipping its days, and is named in
the restore report under a new catalogue.
2026-10-08 22:37:56 +02:00
Marc-André Bombeck 342d73fb36 fix(google-health): read a full day of intraday samples once per local day
A watch worn without its phone reaches Google's cloud hours after the
samples were taken. The two-hour intraday overlap is measured from the
end of the previous clean cycle, so anything arriving later than that
was never fetched. The first cycle of each local day now reads intraday
samples with the full day of overlap; later cycles keep two hours.
2026-10-08 22:37:56 +02:00
Marc-André Bombeck a9bf72a7ce Merge the last UI round into release/v1.42.0 2026-10-08 21:12:05 +02:00
Marc-André Bombeck fc2753ef79 Let the delegated-write journey add through the empty list's control
An empty measurement list carries its add action in the empty state,
not the header, so the write-grant journey waited on a header button
that never appeared. It takes whichever add control the page shows, the
way the other add flows already do.
2026-10-08 21:10:06 +02:00
Marc-André Bombeck 01f3a8b875 Check the account page's language select where it now lives
The mobile consistency check pinned the language select to the date of
birth's grid row. It now asserts the select sits in the display card,
outside the profile form, while the date of birth stays in the form.
2026-10-08 21:10:06 +02:00
Marc-André Bombeck 313acdf304 Size the source-priority skeleton to every metric line
The loading state now reserves one collapsed line per metric, the upper
bound of the loaded list, so the action row does not move down when the
ladders arrive.
2026-10-08 21:04:09 +02:00
Marc-André Bombeck d99306275c Drop the settings section descriptions nothing reads
Sixteen settings.sections.<slug>.description strings per locale had no
call site: the shell paints each section's subtitle, and no literal or
key template builds a section description. The reverse-coverage guard
counted them as reached only because its suffix arm accepts any
`${prefix}.description` template next to a sibling key such as
.title.
2026-10-08 20:55:01 +02:00
Marc-André Bombeck 8ff35231f8 Write English navigation and insights chip labels in sentence case
Settings, admin and main navigation read "Data & privacy", "Export &
import", "Health score", "API & tokens", "MCP connector", "System
status", "Danger zone" and "Admin console", and the insights metric
chips drop their title case ("Blood pressure", "Body fat" and the rest),
matching the labels around them. The other locales already followed
their own conventions; the API page title is translated where it had
been left in English.
2026-10-08 20:53:18 +02:00
Marc-André Bombeck 0fd1168fd9 Pin the connector settings at one fewer success tint
The endpoint card's green Enabled badge went with the status badge
that repeated its switch.
2026-10-08 20:53:17 +02:00
Marc-André Bombeck a589e15f86 Name the arrangement settings hub "Layout"
The hub arranges and picks what the dashboard, insights and each module
show and holds no theme control, so "Appearance" promised something it
does not have. It reads "Layout" in every locale, and its subtitle says
what it does. In German "Darstellung" now names only the account page's
language-and-units card.
2026-10-08 20:47:55 +02:00
Marc-André Bombeck f648168266 Derive the privacy page's stored-data list from the module registry
"What's stored" was six hand-kept lines that never named documents,
vaccinations, the cycle or workouts. It now names every module that
keeps records, read off the registry so a new module joins without an
edit, beside fixed lines for measurements, the profile, integration
tokens and account security, in foreground text since it is the content
of the card.
2026-10-08 20:46:51 +02:00
Marc-André Bombeck c4272343e3 Collapse the admin per-column encryption table by default
A fresh install lists well over a hundred encrypted columns, nearly all
empty, and the open table pushed every other encryption card off the
page. The card now opens on a toggle that names how many columns hold
rows, lists only those when expanded, and offers the full list behind
a second toggle.
2026-10-08 20:45:31 +02:00
Marc-André Bombeck 834803ff21 Page the admin app logs fifty rows at a time
The buffer answers with up to ~500 events and the table painted all of
them. It now shows fifty per page with the login overview's pager,
resets to the first page when a filter changes, and clamps the page
when a refetch returns fewer events.
2026-10-08 20:42:35 +02:00
Marc-André Bombeck 2d79b80298 Give the account page's save-on-change preferences their own card
Language, units, glucose unit and the hour and date formats each save
the moment they change, yet sat inside the profile form as if its Save
covered them. They move to a Display card with no save button; the
profile card keeps its fields, the timezone (which saves with the form)
and its Save.
2026-10-08 20:41:50 +02:00
Marc-André Bombeck 51374274aa List only the sources an account uses on the source-priority page
The read and save of /api/auth/me/source-priority now carry inUse: the
ranked sources the account has connected or holds at least one
measurement from, one index probe per source. The settings page lists
only those, hides a metric nothing delivers, and opens one metric group
at a time, the first by default, with the winning source named on each
collapsed line. The ladders stay whole: moving a source swaps it with
its nearest listed neighbour, so a hidden source keeps its slot.
2026-10-08 20:40:39 +02:00
Marc-André Bombeck c816946140 Tidy the token, connector and share-link settings cards
The API tokens card names every kind of token it lists and reads in
sentence case. Each ingest card and the connector card fold the
shown-once note into the paragraph about what the token can do, and
their name fields suggest a fitting example instead of one phone
shortcut everywhere, short enough to stay whole on a phone. The
connector card drops the status badge that repeated its switch. Share
links get their own card next to the one that creates them.
2026-10-08 20:35:56 +02:00
Marc-André Bombeck c90d28e597 Name the admin account and invite cards apart from their pages
The users page card is now "Accounts" and the invites card "Invite
links", so neither repeats the page title above it, and the token
overview reads "API tokens" in sentence case. The role filter pills
stay neutral when selected, and the three icon tools on a phone row
wrap as one group instead of leaving the sign-out icon on its own line.
2026-10-08 20:35:56 +02:00
Marc-André Bombeck 161963c82b Put the passkey and security-key add actions in the card's action row
Both cards now end in the one right-aligned action row with the add
action as the card's primary, like the authenticator card's set-up
button, and both show the same plain empty state. The error line moves
above the row so nothing follows it.
2026-10-08 20:35:55 +02:00
Marc-André Bombeck f363405df0 Merge the settings and admin UI round into release/v1.42.0 2026-10-08 20:21:39 +02:00
Marc-André Bombeck ee16d3ca72 Update settings and admin copy
New card titles and one-sentence descriptions, sentence case for admin
titles, a current anamnesis subtitle, and the keys the pass retired.
2026-10-08 20:20:04 +02:00
Marc-André Bombeck f4d2ec8988 Tidy admin settings cards
Offer every shipped locale as the server default language, move the app
log refresh out of the header status slot, let filter selects fill their
grid cells, name the coverage table as a focusable region, hide the
legacy-key badge when only one key exists, keep the encryption cards
still while they load, and stop repeating the page title in card titles.
2026-10-08 20:20:03 +02:00
Marc-André Bombeck 37f922af97 Fix settings field rows, defaults and narrow layouts
Module rows drop the field-label colon and inset, the reminder number
fields align, target defaults print at their own precision, the
appointment offer wraps instead of squeezing to one word per line, the
export format legend matches its neighbour label, and the profile-photo
explainer reads as body text.
2026-10-08 20:20:03 +02:00
Marc-André Bombeck 2dda933d77 Align layout settings cards and the insights arrange editor
The insights arrange list becomes a Settings card with its own header and
a Save that waits for a change; the self-link back to the page it sits on
and the focus grab on load are gone. The medications view card takes the
header shape the labs and documents cards use, the order card shares
their title and loads with row placeholders, and the documents view card
uses their icon.
2026-10-08 20:20:03 +02:00
Marc-André Bombeck 991764b0c8 Give settings cards one action row at the foot
The TOTP enable, the source-priority reset, the biomarker and record add
actions, and the about-me, profile-facts, emergency and condition saves
move into SettingsCardActions, right-aligned and last in the card. The
emergency Save waits for a change like its neighbours, an empty list
leaves its add action to the empty state, and the source ladder keeps
its up/down buttons side by side on a phone.
2026-10-08 20:20:03 +02:00
Marc-André Bombeck 97e4dd4c05 Split the AI provider settings into one card per concern
The page was one card titled like the page, holding seven grey panels
with three primary Saves, each left-aligned mid-card. Provider, fallback
chain, response timeout, document reading, consents and the check row
are separate Settings cards now, each with one action row at its foot.
The chain rows keep the name on its own line on a phone, and the cards
below the provider card wait for its reads so they do not jump.
2026-10-08 20:20:02 +02:00
Marc-André Bombeck 8fc467f8c5 Merge the second UI round into release/v1.42.0 2026-10-08 20:05:44 +02:00
Marc-André Bombeck a1a0afe675 Let the vaccination journey wait for its list before adding
The header add disappears once an empty Impfpass has loaded; the
journey now waits for a record or the empty state and clicks whichever
add control is on screen.
2026-10-08 20:03:47 +02:00
Marc-André Bombeck 276cac1d5b Point the e2e add flows at whichever add control a surface shows
A list that is empty now carries its add action in the empty state
instead of the header, and on the phone shell the dashboard's add opens
the capture sheet. The overflow, mood timestamp, measurement flow,
onboarding tour and delegated-write journeys accept either control; the
measurement list's empty-state add gets a stable data-slot for that.
2026-10-08 19:51:16 +02:00
Marc-André Bombeck 5be7a9c242 Label the privacy page's sign-in link in German like its headings 2026-10-08 19:37:31 +02:00
Marc-André Bombeck a5e947ce97 Keep the unit and the time whole when the phone meta line wraps
'vor 10 Stunden' broke across two lines; the unit and the time now
wrap as units, the source badge after them.
2026-10-08 19:37:02 +02:00
Marc-André Bombeck 0bcf465e38 Close the off-switch token comment
A broken comment terminator swallowed the light theme's off-track
value, leaving the track unset.
2026-10-08 19:36:02 +02:00
Marc-André Bombeck 3b4d031e1e Lay the medication tabs out three-up on a phone
Wrapped, the last two tabs stretched across the second row.
2026-10-08 19:34:16 +02:00
Marc-André Bombeck 7d3ee68bf1 Describe a device-served strain score by the device line alone
The proxy's TRIMP method sat above the device note and described a
number the card was not showing.
2026-10-08 19:33:51 +02:00
Marc-André Bombeck dd49ec63a6 Sort the shell-variant classes the way the formatter orders them 2026-10-08 19:20:49 +02:00
Marc-André Bombeck e227f8e4f3 Give off switches and disabled primary buttons light-mode contrast
In the light theme an off switch's track was the input grey at ~1.4:1
on the card, and a disabled primary button was white on a half-opacity
violet at ~2:1. An off track now has its own token at ~3.5:1, and a
disabled primary button a pale violet fill with a dark violet label at
~5.5:1. The dark theme keeps its look through the same tokens.
2026-10-08 19:17:50 +02:00
Marc-André Bombeck 28458448c2 Bring the day surfaces into one language
The cycle and travel rows of a day read their server sentinels
('cycle', 'travel', a raw flow level) instead of words, so a day said
'cycle · Tag 8'. They are worded in the reader's language now. The
date in the phone measurement row and the workout row wrapped to
'vor 9 Stunden…' because the text column shrank to its value; it takes
the row's width and wraps instead of truncating. The intraday pulse
card drops its coloured identity edge, takes a title that says what it
shows, and says there is not enough for a course instead of drawing one
dot across 24 hours. The day body's top inset moves from the banned
pt-5 to pt-4.
2026-10-08 19:14:22 +02:00
Marc-André Bombeck e9a5614733 Colour insight series from the chart palette, not the status tokens
Metric pages coloured their series with success, info, warning,
destructive or a raw palette yellow, picked per page, so a steps line
read as 'good' and a resting-pulse line as an alarm. Series now take
the chart tokens by hue family (chart-2 green, chart-3 pink, chart-4
cyan, chart-5 orange), and the systolic/diastolic pair and the pulse
pair stay paired. Status colours remain where they carry meaning:
target bands, time in range, mood score scales. The mood tag scale
drops its raw palette reds and greens for the status tokens, which are
tuned for the light theme.
2026-10-08 19:08:51 +02:00
Marc-André Bombeck a1b46eb2f4 Write workout durations and pace the reader's way
Workout durations read '38m 00s' and '1h 02m' in every locale, the pace
'5:51 /km', and the detail header printed its raw source key. The
detail now shows a clock reading with the locale's unit ('38:00 Min.',
'1:02:05 Std.'), the list and the dashboard tile the shared localised
minutes label, the pace 'min/km', and the header the source's name.
2026-10-08 19:07:37 +02:00
Marc-André Bombeck 8302f5f62a Write mood insight figures with the reader's decimal separator
Averages, deltas and correlation figures on the mood insights went
through toFixed and read '3.4' in German. They use the locale formatter
now, and the whole-number crosstab cells pick up thousands grouping.
2026-10-08 19:04:30 +02:00
Marc-André Bombeck 7d738c0e49 Lead the meta line with the unit in the phone measurement list
On a phone a long unit beside the value ('12,5 Atemzüge/min') pushed
the row's controls. The value stands alone and the unit opens the muted
meta line beside the time and source, where meta belongs.
2026-10-08 19:03:32 +02:00
Marc-André Bombeck 5850036527 Number the Telegram steps once and spell Client-ID one way
The setup steps carried '1.' in their copy inside a numbered list, so
the card read '1. 1.'. The list numbers them now. Three integration
cards spelled 'Client ID' and 'Client Secret' where the others write
'Client-ID' and 'Client-Secret'.
2026-10-08 19:02:32 +02:00
Marc-André Bombeck b3fb9afa2c Lead the privacy and about pages with the reader's language
The privacy policy is German first in every body, but its section
headings, title and table of contents led in English and its date read
2026-08-11 on both lines. Headings now lead in German with the English
title beside them, the contents list follows, and each date line writes
the date as its language does. The about page's headings, date and
sign-in link follow the reader's locale. No legal wording changed.
2026-10-08 19:02:00 +02:00
Marc-André Bombeck 5bb6b6ad21 Name the share segment's not-found page in the URL-credential guard
It sits under the [token] segment, so the route-shape leg finds it; it
reads no token and resolves nothing.
2026-10-08 18:56:02 +02:00
Marc-André Bombeck f944c07cdf Explain a dead share link and a broken invite in the reader's terms
A practice opening an expired or revoked share link met the app's own
404, 'this page does not exist', with a button to a dashboard it has no
account for. The share segment now has its own not-found page: link
expired or invalid, ask the person who shared it for a new one, and no
link anywhere. It still answers 404 for every failure class alike.

A malformed invite link fell through to the plain registration form.
It now says the invitation is invalid and offers sign-in; the shape
check is public, so this reveals nothing about which tokens exist, and
a well-formed token still goes on to registration.
2026-10-08 18:55:45 +02:00
Marc-André Bombeck 005cb32ea8 Give every empty list one empty state and one add action
EmptyState now has a single action shape: centred at its own width on
the 44 px tap floor, never stretched edge to edge on some pages and
compact on others; the per-call-site size switch is gone, and the
dashed card takes the card radius. While a list is empty its empty
state carries the add action, so the header drops its own add button
there (measurements, mood, medications, labs, vaccinations, documents,
illness, check-ups, visits, procedures). The dashboard medication tile
of an account without medications offers 'Add a medication' instead of
'no data in the chosen range'.
2026-10-08 18:53:24 +02:00
Marc-André Bombeck 793c19e179 Keep delete affordances neutral until the confirmation
Row delete buttons, overflow-menu delete items and per-row revoke
buttons painted red in lists, so a list of tokens or shares read as a
wall of alarms. They now sit in the muted tone (foreground on hover)
or as an outline button; the red is kept for the confirmation dialog
that every one of them opens, and for the danger zones, which are a
destructive card-wide action by design.
2026-10-08 18:45:02 +02:00