Files
HealthLog/CHANGELOG.md
T
Marc-André Bombeck fd9603a578 chore(release): v1.30.33 — the session cookie becomes a secret
Also closes the device list's exposure of the same value, refuses a
cross-device refresh rotation, replaces a vacuous expiry test with one
that fails when the check is removed, and asserts the Nightscout host
guard at save time rather than relying on the probe's placement.
2026-07-19 12:09:17 +02:00

852 KiB
Raw Blame History

Changelog

[Unreleased]

[1.30.33] — 2026-07-19

Security release. It ships on its own because it changes how a browser session is recognised.

  • The session cookie is a purpose-generated secret. It used to be the session record's own database key — an identifier built to avoid collisions, not to be unguessable, and one that appears wherever that record is referenced. The cookie now carries 32 bytes from the system's random source, and only its hash is stored, so the value in your browser exists nowhere on the server.
  • You are not signed out by this. Sessions from before the change keep working and are simply not renewed, so they run out their normal 30 days and disappear. Every sign-in from this release onward is on the new scheme.
  • The device list no longer hands out working credentials. It identified each device by that same record key — which, for a session predating this release, is that device's cookie. It now shows an opaque handle that is useless as a login, and the same handle is what signs a device out.
  • A refresh token cannot be rotated from a different device, and an expired one is refused. The test that was supposed to prove the expiry passed a token that had never been issued, so it proved nothing: with the expiry check deleted, the suite still went green. It is a real test now.
  • A Nightscout address is checked when you save it, not only when it happens to be probed first. The check itself already refused private, loopback and cloud-metadata addresses.

Operators: rotating API_TOKEN_HMAC_KEY now also signs out every browser session, not just API tokens. One migration (0255), additive.

[1.30.32] — 2026-07-19

  • A withdrawn AI consent stays withdrawn. Opening the AI settings granted consent again if you had taken it back, and recorded it as though you had done it yourself. A withdrawal is now a standing decision that only an explicit act can lift.
  • The web can withdraw AI consent at all. Until now only the app could; on the web, consent could be given and never taken back. The AI settings show what currently stands and offer the control that changes it. Withdrawing takes effect immediately and deletes nothing you have recorded.
  • Documents get read after you switch automatic reading on. Reading was only ever started at upload, so documents already in your vault stayed unread no matter what the setting said. Turning it on now works through what is already there, in batches, asking the same permission as any other read.
  • A quoted passage in a document review is the document's own text. The quote shown for a finding was the wording that came back from the read, not the line it was taken from — so it could be a paraphrase of your own record presented as a verbatim quote. The passage is now looked up in the document itself, and a finding whose source cannot be located says so instead of showing a quote you cannot check.
  • Pages that arrive with your data pre-loaded now say so on the wire. They were never cacheable in practice, but nothing stated it. Anything behind the sign-in wall is now marked private and uncacheable at the edge, so no proxy or future change can hold one person's record and hand it to someone else.
  • Deleting your last reading of a kind no longer leaves stale weekly, monthly and yearly figures. The daily figure went; the longer spans kept the old aggregate indefinitely.
  • The documented API matches what the endpoints return. Twenty corrections, including a total that the measurements list reports in a different place than the specification claimed, four medication figures named wrongly, a missing intake source, and eleven error codes that were never written down.
  • A retry after an unusable AI answer asks for the right shape. The correction prompt demanded fields that do not exist and named none of the ones actually required, so the one corrective attempt steered away from the contract instead of towards it.

No migrations. No breaking changes.

[1.30.31] — 2026-07-19

  • Today shows a dose before it is late. The card only surfaced a medication once it was already overdue, so it could report nothing needing attention while a dose was due. A due dose now reads as a calm note, an overdue one as a warning, and a day with everything taken still shows the all-clear. Reminders are unchanged — they still only fire once a dose is late.
  • The daily briefing stops disappearing. The check that verifies every figure against your own data was looking in fewer places than the briefing draws from, so a briefing quoting your medication adherence or a change since last month was thrown away as unverifiable. It now checks against everything the briefing is built from. And when a figure genuinely cannot be verified, yesterday's briefing stays up instead of the section going blank.
  • The "read the full briefing" link is gone from Today. Tapping the health-score ring already opens the same page.
  • The close button on a document sits flush with the edge again.
  • A document no longer says its summary is being generated forever. Summaries are only started when a document is uploaded while automatic reading is on — there is no catch-up for documents you already had, so the view promised a summary that was never coming, and hid the button that would have made one. It now says what actually happened — not attempted, unavailable, or held back — and offers to generate one in every case. A summary you generate that way is kept.
  • Removed the "read by AI" note at the bottom of a document.

One migration (0254). No breaking changes.

[1.30.30] — 2026-07-19

Four languages get their own words; a failed read stops claiming you have no data.

  • Reminder notifications, the assistant's reply language, chart and calendar labels, and durations were English or German for French, Spanish, Italian and Polish. The translations existed; the code that picked between them only knew two languages. It now uses the account's own.
  • Polish counts correctly. Polish has a third plural form that a two-form scheme cannot express, so counted phrases read wrong. The other five languages resolve exactly as before.
  • A screening history that failed to load said "not taken yet". On a mental-health surface that is a false statement about your own record — you could reasonably conclude you never completed something you did complete. That surface, and a nutrient card with the same flaw, now say the reading failed and offer to try again. Nothing retries silently: a failure you cannot see is worse than one you can.
  • Three sleep-rhythm cards that share one reading each printed the same failure notice; the page now shows it once.

[1.30.29] — 2026-07-19

  • A connector token that may only read is now refused at the write itself, not only by never being offered the write. The protection was real but rested entirely on how the connection is assembled, so a future change to that assembly could have opened writing with no test noticing. Any write added later inherits the check.
  • Names and text read out of your records now carry an explicit boundary when handed to a connected assistant, matching what the app already does for its own assistant.
  • Reading a medication list from a photo measured its cost against the server-wide limit even when you use your own key. It now uses your own limit. The older way of counting that caused this — the third surface to have it — is removed entirely rather than fixed in place, so nothing can reach for it again.
  • Removed a module that read as a check on generated recommendations but had not run for some time. It could not simply be switched on: it required fields the current format does not have, so enabling it would have rejected nearly every generation. The measurement it fed is unaffected and still runs.

No breaking changes.

[1.30.28] — 2026-07-19

  • Daily briefings count against the AI budget. They were the one remaining kind of generated text that did not, so a morning's briefings could run past the limit you set. On your own key they count against your limit, not the server's.

[1.30.27] — 2026-07-19

  • The resting heart-rate chart no longer stops partway. Older readings get condensed after a while, and the chart treated the condensed days as the whole story — so recent days went missing. It now shows both, and says when a day is an estimate.
  • Summaries recompute for the whole day or month. Recomputing from a point in the middle replaced the full period with a partial figure. Affected summaries correct themselves the next time that day is written to, or on the next nightly pass.
  • A repeated daily total in one upload keeps the newer value. It kept the older one.

No breaking changes.

[1.30.26] — 2026-07-19

  • Text read out of your documents is treated as data. Lab field names were passed to the assistant unchecked, so a document whose labels were written to look like instructions could influence later answers. Those names are cleaned now, and document text is marked as yours rather than as something addressed to the assistant.
  • A reminder the assistant picks up from a chat is proposed, not active. It waits for you to confirm it, like its other suggestions. Reminders from before this release keep working unchanged.

No breaking changes.

[1.30.25] — 2026-07-19

  • Generated cards count against the AI budget. Metric cards, biomarker cards, scores and the period narrative were not counted at all. On your own key they count against your limit, not the server's.
  • Switching language no longer regenerates everything twice. The same card in six languages counted as six separate jobs.
  • Long assistant conversations get cheaper again. Past a certain length the full context was re-sent on every turn instead of once.

No breaking changes.

[1.30.24] — 2026-07-19

  • The safety check on generated text covers every card, in every language. It ran on the assistant's replies but not on the metric cards, biomarker cards or document summaries — so a sentence the assistant would refuse to send could still appear on a card. It also only knew English and German wording, leaving four languages unprotected. Both are fixed.
  • A document transcription is deliberately never filtered: it reproduces your own document, and a letter from your prescriber is a record, not advice from us.

No breaking changes.

[1.30.23] — 2026-07-19

  • Switching a module off now also applies to connected assistants. With a module off, a connected assistant could still read that data — including the full doctor summary — and so could a shared clinician link. Both now honour the switch.

No breaking changes.

[1.30.22] — 2026-07-19

  • Background jobs stop piling up on themselves. Duplicate jobs were meant to be collapsed but were not, so a large sync could queue thousands of identical recomputations and a restart could re-queue a running history import. Existing installations are updated on the next start.

No breaking changes.

[1.30.21] — 2026-07-19

  • Dose reminders land on the right hour across a clock change. On the day the clocks change, a reminder could resolve an hour off, and the same dose could end up recorded twice. Nothing stored changed.
  • The next-dose day label was wrong when your phone's timezone differed from the one in your profile.

No breaking changes.

[1.30.20] — 2026-07-19

  • A restore refuses when the backup file names a different account than the backup you selected.

No breaking changes.

[1.30.19] — 2026-07-19

  • Switching a module off now holds everywhere. The sync feed still sent cycle data, the FHIR export still returned the full record, and the dashboard summary still built glucose and sleep cards regardless of the switch. Nothing is deleted — turning a module back on finds the history complete.
  • Two assistant features sent data out without checking your consent first. Both now check, and fall back to their plain text when consent is absent.

No breaking changes. The native app may see a 403 from the FHIR endpoints where a module is off.

[1.30.18] — 2026-07-19

  • Health data no longer reaches the logs or the backups. Cached responses are encrypted at rest — they echo what was just saved, so cycle notes and mood text sat unencrypted in every backup for a day. Diagnostic log entries are now scrubbed where they are written, and a failed AI request no longer records what came back with it.

Log lines and backups written before this release may still contain those values. No breaking changes.

[1.30.17] — 2026-07-19

  • API tokens enforce their scope. A token issued for medication intake reached every endpoint, including the full backup export. Routes that accept a narrow token now have to say so.
  • Revoke and re-issue any token you gave to a third party. Treat it as having had that reach for its whole lifetime.
  • The generic "create API token" button is gone. The token it made never worked for the medication intake it advertised, while reaching everything else. Tokens are issued per medication from that medication's own toggle. Existing tokens stay listed and revocable.

No breaking changes to the web app, the native client or the MCP connector.

[1.30.16] — 2026-07-19

  • Resting heart rate stops being lost. Older heart-rate readings are condensed on a schedule, and a resting value is derived before the raw readings go. A faulty check stopped that derivation after the first run while the raw readings were still cleared, so each run lost another day's resting figure for good. Days already affected cannot be recovered; from here nothing further is lost.

No breaking changes.

[1.30.15] — 2026-07-18

  • Assessments come in the language you set. French, Spanish, Italian and Polish accounts got German or English text instead of their own. Insight cards, the per-metric assessments, scores, the biomarker card and the period narrative now follow your language. German and English are unchanged. Stored texts in the four languages refresh on their next nightly run.

No breaking changes.

[1.30.14] — 2026-07-18

The workouts page joins the pages that paint on first load.

  • The workouts list opens with the sessions already there. It was the last high-traffic surface still fetching its rows after the page mounted, so it showed a loading skeleton first and filled in a moment later. The list is now read on the server and handed to the page populated, the same way the dashboard, insights, medications, and coach pages already work. The list logic moved into a shared read so the page and the API endpoint go through one cached, deduplicated pass instead of two.
  • API docs: the heart-rate bucket example matches what the server accepts. The aggregated heart-rate externalId was documented with an hourly example from its first version; the contract has been 10-minute buckets since v1.30.7. The description, the example timestamp, and the min/max field notes now describe the shipped contract.

No breaking changes.

[1.30.13] — 2026-07-18

More pages that paint on first load.

  • The insights, medications, and coach pages open with their content already there, the same way the dashboard now does — their above-the-fold data is read on the server and handed to the page populated, instead of a shell that fills in after a second round of requests. Nothing that would trigger AI work is pre-run: the daily briefing and the coach's own reads stay on-demand as before.

No breaking changes.

[1.30.12] — 2026-07-18

Faster medication reads.

  • The dashboard's medication section stays fast as your log grows. Its reads now use dedicated indexes instead of scanning the full intake history on every load, so a long history no longer slows down the daily view.

No breaking changes.

[1.30.11] — 2026-07-18

Native single sign-on.

  • The iOS app can sign in through your identity provider (OIDC SSO). Until now, single sign-on only worked in the browser — the native app couldn't complete it, which locked native users out entirely on an SSO-only instance. The server now supports a native sign-in handoff (an in-app authorization flow that returns the app's normal token, never a cookie), so the app can offer SSO once it adopts the flow. Existing password and passkey sign-in are unchanged. Multi-factor sign-in still applies, and an SSO session never satisfies a step-up prompt — those still require your own second factor.

No breaking changes.

[1.30.10] — 2026-07-18

Localized insight text, and a richer workout view.

  • Correlation insights now read in your language. The "this tends to go with that — a pattern worth watching, not a cause" lines under a metric were always written in English; they now render in your interface language (German, Spanish, French, Italian, Polish), with the association-not-causation framing preserved in each.
  • A workout you open shows more of what was recorded. The detail view now draws the session's heart-rate curve over time, its route as a clean line, the time spent in each effort zone, per-kilometre splits, and how it compares to your own average for that sport — all from data already synced, with no new connection. Anything a given workout didn't record simply isn't shown.

No migration. No breaking changes.

[1.30.9] — 2026-07-18

Faster dashboard first load.

  • The top card and the charts paint with their data on first load instead of flashing empty and then filling in. The daily summary and the charts' 31-day series are now read on the server and handed to the page already populated, so there's no wait for a second round of requests after the page appears. The welcome/summary card, likely the slowest thing to show before, now lands with the first paint.

No migration. No breaking changes.

[1.30.8] — 2026-07-18

Hardening for the new heart-rate upload validation.

  • A malformed bucket timestamp is rejected, not silently misfiled. A bucket whose time rolled over (a 24:00, an impossible day) is now refused rather than stored under a non-standard key, where it would have failed to merge with a re-post and could land on the wrong day.
  • The per-window low/high is sanity-checked before it counts. A window's low and high are kept only when they are in a plausible range and sit either side of the average; a glitched reading no longer skews the day's high/low band. The average is always kept.

No migration. No breaking changes.

[1.30.7] — 2026-07-18

Groundwork for a tidier heart-rate history.

  • The server accepts a 10-minute aggregated heart-rate upload. A watch records heart rate every few seconds, and uploading one row per raw sample fills the measurements list with the same value repeated many times a minute. The server now takes one row per 10-minute window — its average, with the low and high of the window — and the within-day pulse curve reads it directly, so the detailed 10-minute chart and the elevated-at-rest note are preserved. The de-duplication reaches the list once the mobile app sends the aggregated shape; existing per-sample history is unaffected and reconciles itself over time.
  • The daily high/low reflects the true spread. A day built from 10-minute windows now reports the real intra-window extremes, not the range of the averages.

No migration. No breaking changes.

[1.30.6] — 2026-07-18

Accessibility pass.

  • Heatmaps read to a screen reader. The mood and medication-compliance grids now expose each recorded day — its date and value — through a hidden day list, so the per-day figures are no longer reachable by pointer only. The chart looks exactly the same.
  • Small labels meet the minimum size. Muted meta text across settings, admin, the coach panel, and the insights cards is lifted off the sub-text-xs sizes; tabular figures and chart chrome are unchanged.
  • A couple of section headings. Two metric-page sections became proper headings, so heading navigation lands where it should.

No migration. No breaking changes.

[1.30.5] — 2026-07-18

Making a shipped feature findable, and a sync-status fix.

  • ECG has a home you can reach. Recordings now have their own page in Insights, a pill in the Heart group that appears once you have a recording, and links to it from the resting-pulse and heart-rate-variability pages. Before this they were reachable from a single spot, and a deep link into the overview could land before the section had drawn. The page is unchanged in substance — the same non-diagnostic, device-attributed view.
  • The "last synced" time stops lagging. A sync driven by an incoming update now records the connection's last-success time straight away, instead of leaving it frozen until the next full poll — so the Settings status and the assistant's integration read reflect a sync that already happened.

No migration. No breaking changes.

[1.30.4] — 2026-07-18

The read-only assistant connector now reaches the full record.

  • More of your data is readable. The connector gained direct reads for nutrition and vitamins, the intraday pulse curve, and stored ECG recordings, plus discovery for a set of device metrics that were collected but not advertised — wrist temperature, cardio recovery and load, day and workout strain, sleep score, breathing disturbances, fall count, the six-minute-walk distance, stair speeds, and energy expenditure. They can now be compared and baselined by name.
  • Correctness. Heart-rate variability resolves whether the source records the SDNN or the RMSSD flavour, so a ring- or strap-only account is no longer reported as having none. A workout logged by two devices is counted once, matching the workouts list — this also corrects the figure the coach sees.
  • Exposure stays deliberate. The mental-health screeners and the environmental signals remain off the connector by construction; a metric is only readable once it has been named for it.

No migration. No breaking changes.

[1.30.3] — 2026-07-18

Follow-up fixes from a full quality re-check of the app.

  • Correctness reaches every surface. The timezone, window, and grain fixes now also cover the coach's correlations, the per-metric cards, and the period narrative (they had kept an older copy). Daily insight cards roll over at your own midnight, not a fixed one. Personal records that a second device had inflated are re-derived on the next start, so a genuine record can be set again.
  • The metrics catalog shows a loading state and a retry on error, instead of briefly reading as "not tracked".
  • Small. One shared info popover, larger tap targets on the view toggles, a notch-safe height fix on the coach history page, and a few wording and translation gaps closed (a body-composition caveat, study citations, the backup scope note).

No breaking changes.

[1.30.2] — 2026-07-18

More audit-backlog fixes.

  • Coach history is fully reachable. The conversation list pages through your whole history and search runs on the server (by title), instead of only the first ~20 loaded chats.
  • Navigation and flows. A document links to the lab values it produced; the two "Recovery" pages are named distinctly and cross-linked; mood, mental wellbeing, and mood insights link to each other; the nutrients card moved out of "Source priority"; the onboarding checklist and tour no longer point at dead ends; asking the coach from a metric or a plan carries that context in.
  • Correctness. Correlations, the intraday chart, achievements, and the nutrients overview now bucket days by your own timezone and lead with the most recent data; a multi-source day is no longer counted twice.
  • Performance. The workouts list caches its de-duplication per filter, so paging through a long history is quick.

No migration. No breaking changes.

[1.30.1] — 2026-07-18

Polish and small fixes.

  • Mobile. Coach and document text wraps instead of overflowing; the intake history is a card list on small screens instead of a sideways-scrolling table; tap targets meet the minimum size; two dialogs became bottom sheets; the workout detail header stacks cleanly.
  • Feedback. Loading skeletons match the content that replaces them (no more layout jump), and the Today priority-card actions show a pending state and can’t fire twice on a double-tap.
  • Remembered choices. A chart’s time range and the measurement type you last logged are remembered; a coach chat deletes with an Undo; a backdated dose can be undone; lab panels get “Save & add another”; pull-to-refresh works on more pages (and a scroll-position bug that made it fire anywhere is fixed).
  • Consistency, accessibility, wording. Spacing and contrast tidied, more empty states use the shared component, headings and focus rings improved, a few German phrasings corrected, the daily line reads more calmly, and several explanations gained their guideline citation.

No migration. No breaking changes.

[1.30.0] — 2026-07-18 — Quality pass

A broad correctness, performance, and polish release, plus two new features. Nothing new is required of you; existing data is untouched.

Added

  • Document summaries. An uploaded document can get a short plain-language summary, generated once at upload and stored encrypted. It runs only when the external-AI document reading you already opted into is on, and never touches older documents.
  • Efficacy chart, readable. The medication efficacy chart scales its axis to the data (a real trend no longer looks flat), gains a “% since start” view, and the history tab reaches your full intake history, not just the last 90 days.
  • A metric catalog. A quiet “All metrics” view lists everything HealthLog can track and which device supplies each — so a metric you don’t have data for stays out of your daily views but is still discoverable in one place.

Fixed — correctness

  • The number shown matches reality. The workouts and measurements lists now lead with the most recent entries (a long history no longer hid recent days), and a metric recorded by two devices on the same day is counted once, matching the dashboard — instead of being summed twice.
  • Strava workouts carry their real sport (cycling, running, …) — like WHOOP already did — with a history backfill, and back-to-back rides no longer collapse into one.
  • Personal records and correlations no longer double-count multi-source days or mix per-reading and daily-total values.

Fixed — medical safety

  • The hypoglycemia alert now uses your own glucose unit; the doctor-report PDF states your own timezone; the crisis card offers Austrian and Swiss lines, not only German ones; and resting-pulse labels drop clinical-verdict wording for calmer, neutral framing.

Fixed — the daily view

  • The Today view is faster (its heaviest reads are now cached instead of recomputed on every refresh) and updates immediately after you log a measurement, mood, or water — not only medication.
  • The intraday heart-rate chart no longer draws a continuous shape across gaps in sparse data, and shows how many readings it’s based on. A failed load shows a retry instead of an empty state.

Fixed — onboarding, dates, consistency, accessibility

  • Connecting a device no longer dead-ends on a fresh account, and the setup steps (including the callback URL) are shown up front.
  • A date from a previous year now shows the year, everywhere it’s displayed.
  • Contrast, chart colours in the light theme, and a handful of duplicated components were tidied; keyboard access and headings improved on several surfaces; the insights and settings menus are grouped more sensibly.

Changed

  • A fuller backup. The full backup now also includes lab results, illness episodes, allergies, family history, workouts, and a documents list. What isn’t included (raw document files, workout GPS/heart-rate detail) is stated plainly.

Performance

  • The Today digest and the insights cards read through the pre-aggregated tier instead of scanning raw rows; batch imports write in one pass.

Two migrations (0250, 0251). No breaking changes.

[1.29.5] — 2026-07-17

Security

  • Hardened the same-origin redirect guard. A crafted relative post-login next path (e.g. /..//evil.com) passed the same-origin check but left a protocol-relative pathname that could re-resolve to a foreign origin when re-used in a redirect or client navigation. The guard now re-verifies the reconstructed path resolves on-origin and rejects it otherwise. Covers the OIDC callback and the password/passkey login redirect.

Fixed

  • OIDC login redirects use the operator app URL, not the request URL. Behind a reverse proxy that does not forward the original host, the login and callback routes built their browser redirects from the address the app was reached at inside the container, sending users to an unreachable internal URL after login. Every OIDC redirect is now built from the operator-set NEXT_PUBLIC_APP_URL, matching the convention the other integrations already use.

[1.29.4] — 2026-07-17

  • The workouts list shows the most recent first. The list was ordered oldest-first, so on a longer history the newest sessions never made the first page. It now leads with the latest workout.
  • Past-year dates show the year. A workout from a previous year now carries its year in the list, so a December date is never mistaken for this year's.

[1.29.3] — 2026-07-17

  • Hydration and micronutrients now have a home. Your fluid intake and the vitamins and minerals your device records get a surface. A water tile on the dashboard shows the day's total with quick-add amounts; a new Nutrients page under Insights shows hydration over the last month, caffeine when you record it, and the vitamins and minerals you actually have data for — each against its reference daily intake where that applies, shown as context, never as a verdict. Manual water entries and device-synced totals now coexist instead of overwriting each other. Sparse logging is never read as a deficiency.

    The nutrients tracking stays opt-in — enable it in Settings, or with the one-tap prompt on the page. Device-synced micronutrients flow once your client sends them.

One migration (0249). No breaking changes.

[1.29.2] — 2026-07-17

  • WHOOP workouts carry their real sport. WHOOP was the only source that didn't translate its activity into the app's own sport names, so WHOOP-tracked rides and runs landed unlabelled instead of as cycling or running. New and existing WHOOP workouts are now mapped, so your bike rides show up as cycling — history included. When the same session comes in from two sources, the canonical row now keeps the richest reading (heart rate, energy, distance) instead of dropping it.
  • Scroll back through the day's heart rate. The intraday heart-rate view gains a day navigator to step through previous days. The fine-grained history now stays for 90 days (was 14); older days show the coarser hourly shape.
  • Clear a card from Today. A milestone, a new ECG, or a tension note can be dismissed from the Today rail once you've seen it; the section tightens when the last one is gone. The cards that need an action stay until you act.
  • Vitals context moves into an info tap. The vitals tiles carry an (i) with the context — your reading against your personal range, and against your age where that applies — instead of a permanent caption. The VO₂ max heading no longer gets cut off.
  • Oura cycle phases feed the cycle tracker. Where your Oura connection provides cycle-phase data, it now fills the cycle tracker as a background source; your own entries always win.

Two migrations (0247, 0248). No breaking changes.

[1.29.1] — 2026-07-17

A round of fixes and small refinements from living with the daily view.

  • Today refreshes right after a dose. Logging or undoing a medication now updates the day's read immediately — the medication line no longer lingers on a stale count until the next reload.
  • A calmer Today hero. The extra score rings are gone; the hero keeps the one health-score ring and a tighter layout.
  • The Coach names the document you attached. Asking about a document now shows its title in the chat instead of a plain "1 document" count, and the attach menu stays within its popover on long labels.
  • A fresh install starts with tracking on. New installs now enable the tracking modules by default — you can still switch any of them off. The external-AI options and the daily push stay off until you turn them on.
  • Insights vitals lose the explainer line. The "your personal normal range is the median…" caption is removed from the baseline tiles.
  • Illness journal: set and edit the end. You can mark an episode's end date, clear it back to ongoing, and backdate the start. When nothing strayed from your personal range, the course section now reads a short settled line instead of appearing empty.
  • The morning refresh covers every sleep source. The refresh that folds last night's sleep into the day now triggers from Google Health, Fitbit, Oura, and Polar as well, not only Withings, WHOOP, and Apple Health. Each skipped refresh is now recorded so a stale morning read can be traced to its cause.

No new data is collected and there is no migration.

[1.29.0] — 2026-07-17 — A daily health companion

This release draws a line under a run of work that turns HealthLog from a place you record your health into one that meets you each day — and it completes that picture with the pieces that make it feel whole.

Over the last releases the dashboard gained a Today view that leads with the day's read, a health score that refreshes the moment last night's sleep syncs, a coach check-in when a plan is due for a look-back, and an optional morning note. Documents came closer to hand — the Coach opens beside them, you can bring several into a conversation, and lab values move between a document and your labs. This release adds the layer that ties it together:

  • Your heart's rhythm, in context. ECG recordings are no longer a page on their own — they're referenced where your resting heart rate and rhythm are discussed, always as the reading your device produced, never a reading of the app's own. A new recording surfaces gently in Today.
  • The shape of your day. Heart rate is now drawn across the day, not just as a daily number — so you can see it settle and climb. When it stays elevated while you're at rest and not moving, Today notes it plainly as possible tension, carefully and never as a verdict.
  • Quiet milestones. Reaching a steady stretch in your target range, or a new personal best, is marked with a calm moment — states you've reached, not streaks to keep, so there's nothing to break and nothing to nag.
  • One piece throughout. The Today view brings back the health rings you choose, each surface carries its metric's own calm colour, and the whole thing reads as one design rather than a stack of features.

Nothing new generates when you open the app — every daily surface reads the insight prepared overnight. No new data is collected; this makes more of what's already yours.

Four changes bring documents and your record closer together:

  • Ask the Coach opens beside your document. Asking about a document now opens the Coach in a side panel rather than jumping to a full page — expand it to full screen whenever you like, and it keeps its place.
  • Bring documents into a Coach chat. From the composer's "+", attach one or more documents — pick from your vault or upload a new one, at the start or mid-conversation — and the Coach answers grounded in them. A conversation with a document runs in its fenced mode: grounded in those documents only, with no access to your health data or tools, so nothing in an uploaded file can reach anything it shouldn't.
  • Lab values from a document, proposed for your labs. When a document you've had read looks like lab work, its values are detected and proposed — one tap to add them to your labs; they're never added without your say-so.
  • A lab scan is also kept. Scanning a lab report into your labs now also files the document in your vault (encrypted, like every upload) and links the two, so the reading and its source stay together.

Migration 0245 (attaching multiple documents to a conversation) accompanies this release; existing document chats carry over unchanged.

Three pieces turn the Today view into a daily loop:

  • A coach check-in. When a coach plan is due for a look-back, it surfaces as a calm card in Today with keep, adjust, or let-go — one tap. Letting go retires the plan quietly; silence never changes it.
  • A morning that reflects last night. The score and read are prepared overnight, but last night's sleep usually hasn't synced by then. Now, the moment it arrives, the sleep-dependent parts refresh on their own — so by the time you look, the day is up to date. Until sleep is in, Today says so plainly instead of showing a stale score.
  • An optional daily nudge. A once-a-day morning notification with the day's read, off by default and opt-in per channel under Notifications. It carries the refreshed digest when sleep is in, never repeats within a day, and is a calm note — never an alert.

All three read the digest prepared overnight; nothing is generated when you open the app.

With the Today view now leading the dashboard, the older opt-in hero card it replaced is retired, so the two never stack. The greeting stays, the tiles and charts are unchanged, and a layout stored with the old hero switched on simply ignores that setting — nothing to redo.

[1.28.53] — 2026-07-16 — A "Today" overview at the top of your dashboard

The dashboard opens with a Today view: your health score, the day's lead read, and a short "worth a look" list — a dose window that's open, an integration to reconnect, a check-up coming due — each a single tap to the right place. The familiar tiles and charts sit just below, unchanged.

It reads the insight that's already prepared overnight, so it loads instantly and never generates anything on open. When last night's sleep hasn't synced yet, it says so plainly and fills in the sleep-dependent parts once the data arrives, rather than showing a stale or empty score. This is the first piece of a single daily view that later surfaces — a morning refresh, an optional daily note — will all build on.

The dashboard gains tiles for readings that were synced and charted but never had a place on the home strip: heart-rate variability, blood oxygen, breathing rate, wrist temperature, and — for anyone with a body-composition scale — muscle mass, body water, and bone mass.

Each tile appears only once you have that reading, so nothing new shows up on an account that doesn't track it, and every tile can be toggled or reordered under Settings → Dashboard like the rest. Tapping one opens its detail view. Nothing changed in how the readings are stored or synced — this only surfaces what was already there.

[1.28.51] — 2026-07-16 — Chatting about a document lives in the Coach now

Asking the Coach about a document used to open a separate, half-finished chat panel that never showed up in your Coach history. It now opens the real Coach: the conversation appears in your Coach list like any other, marked with a small document badge and a "Chatting about: