From ecfa837dea98a0bad4bdbb7f8876712404c2e11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 24 Sep 2026 13:30:44 +0200 Subject: [PATCH] =?UTF-8?q?chore(release):=20v1.39.0=20=E2=80=94=20AI=20be?= =?UTF-8?q?comes=20optional,=20and=20a=20switched-off=20module=20leaves=20?= =?UTF-8?q?the=20whole=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 281 ++++++++++++++++++++++++++++++++++++++++++ docs/api/openapi.yaml | 2 +- package.json | 2 +- public/sw.js | 2 +- 4 files changed, 284 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01108d8b0..62190effa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,286 @@ # Changelog +## [1.39.0] — 2026-09-25 + +AI is optional everywhere now: your data loads whatever the AI settings +say, and each AI switch stops the work it names instead of hiding it. A +module you switch off leaves the whole app, and there are fixes for +heart-rate streams, charts, the cycle calendar and labs. + +### Added + +- **The weight tile judges a change against your own target.** Below your + target band a gain counts as progress, above it a loss does, and inside + the band staying steady is progress while a small move either way is + shown neutrally. The weight note, the daily briefing, the overview + analysis and the Coach know the target too, and changing the target + refreshes the weight note without a new weigh-in. Without a target the + tile works as before. Thanks to @COBO5671 for #1006 and the screenshots. +- **A document can be linked to every visit and vaccination it covers.** + The document's own view in the vault lists the visits and vaccination + doses it belongs to and lets you add or remove them, so a childhood + vaccination record can be filed against every dose it lists in a few + taps. Links made from either side are the same links. Thanks to @Cnote43 + for #1024 and @CritLoren for #1005. +- **The vaccination page shows the booster you planned.** Next to each + vaccine's heading it says when the next booster is due, marked once it + is due within a month and again once it is overdue. The app shows the + boosters you planned and does not work out dates of its own (#1005). +- **A new "Reading documents" switch for people who run a server.** It + sits under Admin, Coach and covers AI reading of stored documents, + lab report scans and turning a typed medication description into a + schedule. It is on by default. +- **Reading documents has its own consent.** Sending a document, a lab + scan or a medication description to an AI service outside your server + now needs consent for reading documents, including with your own OpenAI + or Anthropic key or your own ChatGPT account. A self-hosted model needs + none. The app asks at the place you start the read and grants exactly + that; the consent has its own withdraw button in Settings, AI. It does + not open the Coach or AI analysis, and consent for AI analysis alone + does not cover documents. A full AI consent given before this release + still covers both. +- **What the Coach stored stays yours with the Coach off.** Your + conversations, the facts it remembered, your plans and the reminders you + asked it to keep can be read and deleted in Settings whether the Coach + is switched off on the server, hidden by you, or has no provider. + Confirming, finishing or moving a plan or reminder waits until the Coach + is back. +- **One short hint instead of a message on every card.** While no AI + provider is set up and you could set one up, Insights and Settings, AI + show a single note that AI is optional, with a way to set it up and a + "Not now" that hides it on both pages. With AI switched off on the + server, setup and the getting-started list do not bring AI up. + +### Changed + +- **Your data never depends on AI.** Readings, charts, scores, + correlations, ECG recordings, heart rhythm events, the breathing and + lab-change cards, intraday pulse and the baseline strip on each metric + page load the same with AI on or off, with or without a provider, and + with or without your consent. Apple Watch ECG uploads are + stored in every case. Before, several of these reads sat behind AI + switches, and the Insights overview could fail with "Failed to load + insights". Thanks to @CritLoren for the log in #975. +- **AI parts are left out when AI is off for you.** The daily briefing, + the sentence under your greeting and under the trend charts, the notes + on metric pages, lab markers and workouts, the line after a new reading, + the Coach button and menu entry, and the "describe it in words" button + in the medication wizard only appear while they can work. Text a model + wrote earlier is hidden from the next page load and returns when AI is + back; a switch or a missing provider deletes nothing. A summary the app + composed from your numbers alone is not AI text and stays. +- **Nothing is written by a model at night unless AI is on for you.** The + nightly briefing, status notes, period summaries, arrival lines, workout + notes and Coach memory upkeep now check the server's switches, your AI + analysis switch, your provider and your consent for each person as the + run reaches them. Switching AI off during a nightly run stops the rest + of that run, and a reply that arrives after you withdrew consent is + thrown away. Week and month summaries still appear, written from your + numbers, and Coach nudges arrive in their plain form when only consent + is missing. +- **The assistant switches stop AI work instead of hiding it.** When a + switch in Admin, Coach is off, nothing it covers is sent to a model, + and that now includes document reading, lab scans and medication text, + which the switches used to leave running. A read queued before the + switch was turned off stops when it runs. "Status cards" is renamed + "Status notes". If the server cannot read the switches for a moment, + every AI feature stays off for that request. +- **The Coach has one switch for people who run a server.** It is the + Coach switch under Admin, Coach. The Coach row under module + availability now follows it and links to it; an operator who had turned + the Coach module off there has the Coach switch turned off. +- **The "Insights" module is now "AI analysis".** It decides whether a + model writes the briefing, status notes and workout notes for you. With + it off, the Insights tab stays in the menu and the bottom bar, and the + Today summary and the readiness, recovery and sleep score rings stay on + the dashboard. Breathing screening now follows the Sleep module and + "What changed since your last panel" follows the Labs module. +- **Hiding the Coach hides only the Coach.** It used to stop the nightly + briefing and status notes as well, which the setting never said. If you + had the Coach hidden, AI analysis is switched off for you in this + release so nothing new is written; switch it back on under Settings, + Modules if you want the notes. +- **The morning briefing notification keeps coming with AI analysis + off.** It carries your health score or the day's reminders without the + first sentence a model would have written. +- **Someone managing your record sees the stored AI text you would see.** + Your briefing, notes and summaries show for them whenever they show for + you, but opening your record never starts AI work on it. +- **Coach reminders wait while the Coach is unavailable.** They were + surfaced into a conversation nobody could open and dismissed after a few + unanswered rounds. They now wait and appear once the Coach is back. +- **Withdrawing AI consent deletes the notes written under it.** When no + consent for AI analysis is left, the status notes, the daily briefing, + the model-written period summaries, the arrival lines and the workout + notes are deleted in the same step. Your Coach conversations, the facts + it remembered, your plans and reminders, and your document summaries are + your records and stay. The confirmation says which of these applies to + you. When the consent card cannot read your consent, it says so with a + retry instead of showing it as withdrawn. +- **Switching a module off removes it everywhere.** Mood, Sleep, Labs and + the other modules now leave the Insights tabs, the trends row on the + overview, the add menu (including the measurement types they own, such + as sleep and glucose), the dashboard tiles, the overview layout settings + and the score rings. The trends row moves on to the next chart so it + still shows three. +- **Correlations only use what you track.** The pattern finder, and the + patterns the Coach, the status notes and the weekly and monthly review + look at, leave out mood, sleep, glucose, medication adherence, symptoms, + weather or lab results once that module is off. Turning it back on + brings them back, and patterns you had dismissed stay dismissed. Because + fewer pairs are checked, a weak pattern between two things you still + track can appear or disappear when you switch a module. +- **A link to a switched-off page says why.** A bookmark to a page whose + module is off shows a short notice with a button to Settings, Modules + when the switch is yours. When the page is outside what someone shared + with you or the server turned the module off for everyone, it says so + and leaves the button out. The Nutrients page uses the same notice and + its own "turn on" button is gone. +- **Clearer messages when AI is refused.** The Coach, document reading, + lab scans and the medication reader tell a missing consent, AI switched + off on the server, a record someone shared with you and a missing + provider apart. A spent daily budget in the medication reader no longer + shows "too many requests". Settings, AI names what the server has + switched off, and "Test connection" says AI is off on this server + instead of calling the model or showing an error code. +- **Status notes live in their own encrypted storage.** They used to be + kept as plain text in the audit log. They are now encrypted like the + rest of your health data, covered by key rotation, left out of backups + because they are rebuilt each night, and removed by "Delete All Data". +- **A year of per-minute heart rate reads in about half the time** when a + chart asks for it. +- **If you write against the API**, `GET /api/auth/me` gains + `ai: { capabilities, provider }`, which says per feature and for the + record in use whether AI is available and why not. Routes that serve + data no longer answer `403` for an AI reason; routes that mix data with + model text answer `200` with the model text as `null` and an `ai` object + giving the reason. This covers `GET /api/insights/comprehensive`, + `/api/insights/derived`, `/api/insights/narrative`, the status note + routes, `/api/workouts/{id}`, `/api/daily/digest` and + `/api/dashboard/snapshot`, among others. AI refusals carry + `meta.errorCode` with `meta.capability`, `meta.reason` and, where it + applies, `meta.module`; the new codes are `ai.record.notPermitted`, + `ai.provider.none` and `ai.unavailable`. Document reading, lab scans and + medication text refuse with `assistant.disabled.documentAi`, and + `/api/insights/generate` with `assistant.disabled.briefing` instead of + `assistant.disabled.coach`. The consent routes accept a new kind, + `ai_extraction`. `aiAvailable` on `GET /api/user/ai-provider` is false + while the master switch is off, and every card from + `GET /api/insights/cards` says `provider: "rules"`. +- **More for API clients.** New additive fields are + `tiles.weightTrend` on `GET /api/dashboard/snapshot` (`direction` is + `up-good`, `up-bad` or the new `hold`), `renewals` on + `GET /api/vaccinations`, `vaccinationLinks` on + `GET /api/documents/inbound/{id}` and `vaccinationIds` on its `PATCH`, + and `cycleDay` and `periodEndable` on each day of + `GET /api/cycle/calendar`. `GET /api/medications/{id}/dose-history` + reports `from` as the requested start rather than the medication's + creation time. `GET /api/auth/me` reports the server's default time + zone when the stored one cannot be used. + +### Removed + +- **The "Correlations" assistant switch.** It only ever turned off + statistics, which no model writes, so correlations now always load. + +### Fixed + +- **The worker no longer runs out of memory on a watch that streams heart + rate.** On an account with heart rate every minute or faster, the worker + grew until it crashed with "JavaScript heap out of memory" within 15 to + 40 minutes, because the heart-rate status card read three months of + readings one by one. The card and the other background summaries, + including the Coach's cycle summary, now read one total per day. If you + raised `NODE_OPTIONS=--max-old-space-size` to keep the worker up, you + can leave it or remove it. Thanks to @ckaotik for the report, the logs + and the patient testing in #1023. +- **Daily totals stay on the same day across chart ranges.** Steps, + active energy, flights climbed and walking distance could move to the + neighbouring day between the 7-day and 30-day ranges, because the longer + ranges cut days at midnight UTC. Every range of single days, up to a + year, now cuts them at your own midnight; longer ranges still group + weeks, months and years by UTC days. Thanks to @mills1975 for #1026 and + the screenshots. +- **The 7-day chart shows the whole week on a streaming source.** It read + individual readings up to a limit that per-minute heart rate reaches + after about three and a half days. Every range now reads one value per + day, and the 7-day and 30-day ranges agree (#1026). +- **Charts on the medication page, in the Coach and in recovery use your + time zone.** They cut days in Berlin for everyone. When a stored time + zone cannot be used, the server and the app both fall back to the + server's default. A bare offset such as `+05:30` is no longer accepted + as a time zone, because the database would cut days on the wrong side of + UTC; pick a named zone such as Asia/Kolkata (#1026). +- **Recent doses appear under "Last 90 days" in a medication's history.** + A dose logged for a time before the medication was added, a dose taken + shortly before its scheduled time, and a dose added while the tab was + open were missing until later or until a reload. Thanks to + @sreeramachandramurthy for #1028 and for spotting the time pattern. +- **Past dates in the cycle calendar show their own cycle day.** The log + sheet labelled any date with today's cycle day, and "My period ended + this day" only appeared while today was a period day. It now shows the + day of the cycle the date belongs to and offers the period end on the + first days of any logged cycle. Thanks to @sreeramachandramurthy for + #1004 and the step-by-step notes. +- **Save in the cycle log needs something to save.** An empty Save stored + an empty entry the calendar could not show, which looked like a failed + save. It now asks you to pick a flow, a symptom or another sign (#1004). +- **Cycle history entered out of order lands on the same cycles.** + Logging the second day of a period before the first no longer opens a + separate one-day cycle; the earlier day becomes the start, and days + logged before a back-filled start move to the right cycle. If you + already have such a one-day cycle, open the real first day and tap "My + period started this day" again. A start tapped by mistake a few days + early can be removed again, and the start it replaced comes back + (#1004). +- **Selecting a cycle symptom no longer moves the list.** Symptoms are one + per line with the optional 1 to 4 intensity beside them. Thanks to + @sreeramachandramurthy for #1001. +- **Labs "Save and add another" keeps your biomarker.** The second save + said "Pick a biomarker first" although one was shown. The form now keeps + the biomarker, the date and the visit and clears only the value and its + note. Thanks to @sreeramachandramurthy for #1029. +- **Manual entry has the rest of the scale's readings.** Visceral fat, fat + mass, fat-free mass, lean body mass and muscle mass can be entered by + hand, in kilograms or pounds as you set. French, Italian and Polish + labels that gave two of these one name are distinct. Thanks to @ocurero + for #1013. +- **The "Link a document" picker offers your whole vault.** It said "No + documents yet." on vaccinations and visits even with a full vault, + because it asked for more documents per page than the server accepts. + Documents dated within a week come first, the rest are listed by month + and searchable, and a failed load shows a retry. The condition picker on + visits had the same problem (#1024, #1005). +- **The Insights overview no longer fails as a whole.** Each section loads + on its own and a failed one offers a retry. "Today's signal", the heart + rhythm events and the mood patterns card say when they could not load + instead of hiding or spinning, a score page no longer says "not enough + data" when it could not read the score, and with the cycle module on but + no cycle in progress the scores row no longer leaves a gap (#975). +- **Restoring instance settings from a backup made before this release + keeps the Coach off** when the backup had the Coach module switched off. + +### Upgrade notes + +- **Take a database backup before upgrading.** Three migrations run. + `0343` adds the "Reading documents" switch, moves the old Coach module + setting into the Coach switch, switches AI analysis off for everyone who + had the Coach hidden and removes the Correlations switch. It cannot be + undone automatically; going back to an earlier version means restoring + that backup. `0344` creates the encrypted status note storage and + deletes the old status note rows from the audit log without copying + them, so the next night writes every note again, about one normal night + of extra model use. `0345` adds one column to cycle starts and changes + nothing else. +- **The Correlations switch is gone** from Admin, Coach. Correlations + always load now. +- **`GET /api/feature-flags` is deprecated.** It still answers, with a + `Deprecation` header pointing to `GET /api/auth/me`, and the web app no + longer reads it. The iPhone app and other clients should read `ai` on + `GET /api/auth/me` instead, expect `200` with `null` model text on the + mixed reads listed under Changed, and handle the three new error codes. + The route is removed in the first release after the next iPhone app. + ## [1.38.25] — 2026-09-20 Three readings that never arrived, and one import that turned away a diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 5f1f7a8ee..009d20da6 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.38.25 + version: 1.39.0 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 dfdbab53c..796fdf300 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "healthlog", - "version": "1.38.25", + "version": "1.39.0", "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 c140864a3..b7f0d39e6 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.38.25"; + /* @sw-version-fallback */ "v1.39.0"; 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/*`