From 1e3eed23abdcf29c8482ea2190a44c8d65354a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 20 Oct 2026 11:14:20 +0200 Subject: [PATCH] chore(release): v1.43.7 --- CHANGELOG.md | 146 ++++++++++++++++++++++++++++++++++++++++++ docs/api/openapi.yaml | 2 +- package.json | 2 +- public/sw.js | 2 +- 4 files changed, 149 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4d1d33cd..4a6df7858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,151 @@ # Changelog +## [1.43.7] — 2026-10-20 + +### Added + +- **Dutch.** HealthLog speaks Dutch now, the eighth language, translated and + contributed by @robdegraaf (#1246). Friends in the Netherlands are always + welcome here. Cheers with a Grolsch! 🍺 +- **Readings you enter here count for Apple Health freshness.** Weight, blood + pressure and glucose are no longer called quiet while you keep entering them + in HealthLog for the phone to pick up, and the card shows when you last did + (`lastEnteredHereAt`, Refs #1247). + +### Fixed + +- **Error states say what happened.** When a session has ended, a card offers + to sign in again instead of a retry that cannot work; when there were too + many requests just now, it says so without a pointless retry; a section you + are not allowed to see on a shared record stays quiet. Some thirty cards that + used to show a bare red line, a "still learning" note, an endless skeleton + or nothing at all when their data failed to load now say so and offer a + retry, and charts follow the same rule. Every read failure goes through the + same row, card and chart state, deciding by the error code first and the + status second. +- **A short database outage no longer signs you out.** A session that could not + be checked because the database was unreachable, out of connections or + otherwise failing answers 503 (`auth.session.unavailable`) and the client + keeps its credential and tries again. A malformed session cookie is treated + as signed out (401) without a database lookup. +- **A nap is not a night, but it is sleep.** A day whose only sleep is a short + daytime nap has no main night (`main: null` on `/api/sleep/night`). A day's + sleep in the list, the export and the correlations counts its naps, the same + total the chart and the sleep score show; the main night alone is + `mainAsleepMinutes`. The night view opened without a date shows the last + night with main sleep. The sleep list, the night view, the chart and the + sleep score name the same day for every night. A REM segment of zero minutes + no longer drops the night around it. +- **Deleted entries stay deleted for the AI.** Deleted mood entries and + readings no longer shape the status texts written about you, and the general + overview looks at every metric again instead of the newest few thousand rows + of the densest one. +- **Days logged for a chronic condition count** in the symptom correlations. +- **Inviting someone whose name or e-mail matches no account** says so + (`sharing.invite.no_account`) and is never confused with a profile you no + longer look after. +- **Old nights open, and "All" means all.** The night view answers any date, + however old, and the sleep series reaches back to the first night. The sleep + status read the last few weeks of stages; it now reads the last year of + nights. +- **Days are your days.** The dashboard streak and the day a dose counts for + were placed on the wrong side of midnight for readings close to it; both + follow the profile timezone now. Skipping a dose from Telegram snoozes until + the end of your own day, not the server's. +- **One number for one thing.** The spread of a series is the same in the app, + the API, MCP and AI tools. Correlations on the insights overview and in AI + data appear only when they are statistically sound (at least 20 pairs, + p < 0.05), as everywhere else; below that the value is `null`. A chronic + condition no longer fills the symptom series with zeros. +- **Medication adherence on the cadence chips** counts pauses, courses and + missed doses like the detail page; a week's pause read 77 % instead of 100 %. +- **A workout Apple Health sends twice is stored once,** and every workout + reader uses the same source order. +- **Provider histories do not end early without saying so.** A WHOOP, Oura, + Withings, Fitbit or Strava history that stops at its page limit is no longer + marked as fully imported. A Strava history of more than about 4,000 + activities imports in stages from where the last stage stopped, one stage per + rate window, instead of re-reading the newest activities after every + restart. WHOOP and Oura no longer read the first page twice when a list ends + with an empty page token. Imported values from every provider and from + Health Connect are rounded the same way, with halves rounded up (a weight of + 72.345 kg now stores as 72.35, not 72.34). +- **A long Google Health backfill keeps its place.** Only attempts that end at + the same edge count toward the final end, and a restart during the 24-hour + wait no longer starts a second chain. +- **The recovery page** says in one sentence why it is still empty, decided on + the server (`recoveryEmpty`), and always links to the integrations. +- **Lists that stop at a limit say so** (`meta.truncated`, `meta.limit`) instead + of passing the oldest loaded entry off as the beginning, and the web shows a + note under each such list. The activity feed keeps its `data.truncated` for + existing clients; it is deprecated in favour of `meta.truncated`. +- Reconnecting Strava, even to a different account, reads that account's full + history again instead of continuing where the previous one stopped. +- The day view's "highest since" notes pick up a new or corrected reading right + away. +- **Old days and long histories are read whole.** The sleep summary covers the + whole history, the pulse assessment reads at least a month of pulse instead + of a few hours of a dense stream, and the mood fallback reads the same five + years as the rollup path. +- The coach's daily limit on follow-up questions resets at your own midnight, + and the weather and air-quality refresh includes today east of UTC. +- **Deleting a large account** no longer aborts after five seconds. +- Workout dates follow your date format in the list, the detail and the + dashboard tile. "Not enough nights yet" names the number actually needed. + `groupBy=day` together with `valueMin`/`valueMax` answers 422 instead of + ignoring the filter. + +### Changed + +- **Faster reads.** The latest value per type is read through one bounded query + instead of a sort over every reading (about 60 ms down to 1 to 2 ms on a large + account), the dashboard summaries share one cache cell, and paging through the + day view computes the two-year comparison once. +- **One heavy job at a time per process.** Imports, restores, backups, rollup + backfills, provider backfills and the nightly runs queue behind each other + instead of stacking up in one heap. A job that finds the lane busy goes back + to the queue and starts once it is free, so a backup waiting behind a large + import no longer runs out of time while it waits. A long Google Health + backfill makes room at its next slice boundary for a job waiting behind it, + and a job that waits more than two hours is reported as a warning + (`job.heavy_lane.waiting_long`). Building rollups for a large account no + longer runs a second time when it takes longer than fifteen minutes. The six per-metric status + crons are gone (the nightly batch already covers them), and a queue fed only + by its cron polls every 30 seconds. +- **`measurements` stays small.** An update that changes nothing keeps its + timestamps, so it stays on its page and is not sent to the phone again; the + table leaves room on each page and autovacuum starts at 5 % dead rows. A + nightly check warns when dead rows or bloat grow past that (see + `docs/ops/measurement-maintenance.md`). +- **Migrations that wait for a lock no longer hold writers back or stop the + boot.** `prisma migrate deploy` runs a migration file statement by + statement, not in one transaction, so the lock-sensitive part of migration + 0412 runs as one block that gives up after ten seconds when another process + holds `measurements`. The container then retries a migration marked safe to + re-run, with a growing pause, instead of stopping at boot; any other + migration failure still stops the boot. See "A migration that timed out on a + lock" in `docs/ops/deploy.md`. +- The day view's "highest since" notes cache fixed 90-day blocks and use less + memory when paging back through older days. +- An insights card whose refresh keeps failing stops polling after its usual + number of attempts. +- Failed provider syncs and refused uploads are logged as signals instead of + audit entries; a provider's error text no longer lands in the audit log in + plain text. +- **The cached AI health report is encrypted at rest,** existing rows included. +- Every API error carries a stable `meta.errorCode`; a route without its own + code gets an `http.*` code from the status. +- Logs group requests by route template (`/api/medications/[id]`) instead of by + concrete path. +- Failed inputs no longer fill the audit log; only attempts by someone with a + grant appear there. +- The browser bundle no longer pulls in server modules, and a test keeps it + that way. +- The Coach stops polling a document that will never be indexed. +- On an out-of-memory crash the process writes a diagnostic report to stderr + (`--report-on-fatalerror`, without environment or network details), and the + heap monitor adds a 95 % tier. + ## [1.43.6] — 2026-10-19 ### Fixed diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index a967cc72b..89c03a66b 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: HealthLog API - version: 1.43.6 + version: 1.43.7 description: >- Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest. diff --git a/package.json b/package.json index 5b0f5b5db..ee1992755 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "healthlog", - "version": "1.43.6", + "version": "1.43.7", "description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.", "license": "PolyForm-Noncommercial-1.0.0", "homepage": "https://healthlog.dev", diff --git a/public/sw.js b/public/sw.js index 2a71e0136..4fa12b5ee 100644 --- a/public/sw.js +++ b/public/sw.js @@ -36,7 +36,7 @@ try { // v1.4.38.4 → v1.4.42. Do not hand-edit; bump `package.json` and rebuild. const CACHE_VERSION = (typeof self !== "undefined" && self.__APP_VERSION__) || - /* @sw-version-fallback */ "v1.43.6"; + /* @sw-version-fallback */ "v1.43.7"; const STATIC_CACHE = `healthlog-static-${CACHE_VERSION}`; const PAGE_CACHE = `healthlog-pages-${CACHE_VERSION}`; // v1.18.6 — read-only data cache for a curated allowlist of safe GET `/api/*`