Commit Graph
198 Commits
Author SHA1 Message Date
Marc-André Bombeck 62eb431d66 release: v1.32.0 — hardening and scalability (#580)
* release: prepare v1.32.0

* test: stabilize water capture re-entry
2026-07-22 04:14:15 +02:00
Marc-André Bombeck 7f55083c3c chore: release v1.31.8 2026-07-21 16:29:04 +02:00
Marc-André Bombeck b20f1b5c66 fix: harden imports and daily medication state (#577) 2026-07-21 13:08:28 +02:00
Marc-André Bombeck 403eabb888 fix: restore runtime Prisma migration loading (#574) 2026-07-21 06:36:13 +02:00
Marc-André Bombeck a30c31b25d chore: release v1.31.5 (#573)
* chore: prepare v1.31.5 release

* docs: sync OpenAPI version for v1.31.5
2026-07-21 05:22:45 +02:00
Marc-André Bombeck b38f41b99d chore(release): prepare v1.31.4 2026-07-21 01:34:29 +02:00
Marc-André Bombeck 3aa8ccd541 fix: localize document summaries and refine mobile coach 2026-07-20 22:34:56 +02:00
Marc-André Bombeck a87a144b1e chore(release): prepare v1.31.2 2026-07-20 12:29:21 +02:00
Marc-André Bombeck 4591899657 fix(release): harden v1.31 arrival contracts 2026-07-20 00:40:20 +02:00
Marc-André Bombeck 29673c1155 fix(insights): register the arrival reaction line with the tone harness
The harness landed in v1.30.35, after the slices that built this
release were already cut — so the reaction line's prompt module was
never registered, and its own docblock's warning against the
two-locale collapse was untested. Registering it surfaced a real gap:
the meaning-first instruction lived only in the system prompt's own
words, never in the user prompt in the family's shared phrasing, which
is where the rest of the surfaces carry it and where the tone contract
checks for it. The German body carried no such instruction at all.

Threads locale through the user-prompt builder so the fixed
instruction line matches the reader's own reply language, and wires
the per-user per-day opener-archetype rotation the milestone spec
called for and the first implementation had not connected.
2026-07-19 15:48:57 +02:00
Marc-André Bombeck a0b7dae8eb chore(release): v1.30.35 — the offline metric texts speak six languages 2026-07-19 13:56:39 +02:00
Marc-André Bombeck b2168df172 chore(release): v1.30.34 — the layout keeps your choices; destructive controls ask first 2026-07-19 13:01:45 +02:00
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
Marc-André Bombeck 1c2cfffdb8 fix(consent): a withdrawn AI consent stays withdrawn, and the web can withdraw
The settings mount fires an idempotent consent heal so an account that
predates the gate does not fall into the no-key state. It looked only for
an active receipt, so a deliberate withdrawal — which sets revokedAt —
read the same as never having consented, and the next visit to the AI
settings granted again, writing an audit row for an act the user never
performed. GDPR Art. 7 (3) puts withdrawal on the same footing as the
grant; one undone by a page load is not a withdrawal.

The mint now distinguishes the two callers. The mount heal grants only to
an account with no consent history at all and leaves a revocation
standing; switching document auto-read on is the consent act itself and
may supersede an earlier revocation. The revocation is re-checked inside
the transaction so a withdrawal landing mid-mint is not overwritten.

The revoke endpoint has existed since v1.4.40 but only the native client
called it: on the web, consent could be given and never taken back. The
AI settings now carry the standing decision and the control that changes
it, in all six languages.
2026-07-19 11:52:34 +02:00
Marc-André Bombeck db86b4f76d chore(release): v1.30.31 — surface a due dose on Today; ground the briefing against what the model was actually given 2026-07-19 09:23:45 +02:00
Marc-André Bombeck f61483d19b fix(sw): repair the service-worker version fallback after the release merge
The changelog merge left conflict markers inside the CACHE_VERSION
assignment. The version generator only rewrites the version string, so
it could not repair the broken structure, and every service-worker test
failed while both branches were green on their own.
2026-07-19 05:37:23 +02:00
Marc-André Bombeck 5c2bee79d8 Merge branch 'main' into release/v1.30.28
# Conflicts:
#	CHANGELOG.md
#	docs/api/openapi.yaml
#	package.json
#	public/sw.js
2026-07-19 05:32:41 +02:00
Marc-André Bombeck a3be2d7c92 chore(release): v1.30.28 — meter the briefing path, the last provider entry a sync can reach 2026-07-19 05:26:16 +02:00
Marc-André Bombeck a5631f3d12 chore(release): v1.30.27 — merge the resting read path, fix the rollup window and the batch duplicate 2026-07-19 05:20:29 +02:00
Marc-André Bombeck e9424ef0be chore(release): v1.30.26 — fence document-sourced text and fix the sanitiser that defeated itself 2026-07-19 04:58:17 +02:00
Marc-André Bombeck 81e66cbc5c chore(release): v1.30.25 — meter the status tier, bound the locale fan-out, restore snapshot-once 2026-07-19 04:27:54 +02:00
Marc-André Bombeck 3d776a236a chore(release): v1.30.24 — one locale-aware safety screen on every model-output boundary 2026-07-19 04:05:37 +02:00
Marc-André Bombeck 4b60852f9f chore(release): v1.30.23 — module switches hold on the connector wire 2026-07-19 03:27:30 +02:00
Marc-André Bombeck a6600f0215 chore(release): v1.30.22 — background work stops piling up on itself 2026-07-19 03:00:36 +02:00
Marc-André Bombeck 81e564a724 chore(release): v1.30.21 — dose reminders land on the right hour across a clock change 2026-07-19 02:44:42 +02:00
Marc-André Bombeck 3ffaf1906c fix(admin): refuse a restore whose payload names a different owner
The restore target came from the encrypted payload's own userId field
while the audit entry recorded the backup row's userId. A file declaring
a different account would therefore have been written into that account,
with the trail naming the one the operator selected.

Both values are in hand at that point, so compare them and refuse on
mismatch, recording both ids. Adds the first test coverage for this
route; the mismatch case fails without the guard.

Also correct the CLAUDE.md claim that every API route wraps apiHandler -
the three MCP OAuth routes deliberately use withBackgroundEvent so they
can return RFC-format error bodies, and carry the same protections by
hand.

chore(release): v1.30.20
2026-07-19 02:16:54 +02:00
Marc-André Bombeck 6b08ffd7c5 chore(release): v1.30.19 — module switches hold on the aggregate surfaces; consent required before assistant egress 2026-07-19 01:58:47 +02:00
Marc-André Bombeck e31c66e959 fix(security): scrub wide events at the builder, encrypt the replay cache
Three leaks of the same class - data leaving the process unprotected.

addMeta and addExternalCall wrote their values verbatim while setError
and setHttp scrubbed, so the builder's redaction contract held for two
entry points out of four. The finished event is serialised whole to
stdout and the log store, which made meta a trusted sink by accident: a
Telegram request timeout put the bot token there, and the AI provider
chain fed upstream error bodies through it. Both now scrub, at any depth.

Scrubbing cannot help with the second half. A gateway that echoes the
request it rejected echoes the prompt, and on that path the prompt
carries the health snapshot; no pattern separates that from any other
text. Upstream bodies are therefore classified rather than quoted - the
diagnostic question is which rejection occurred and whether it repeats,
which a classification answers without the content.

The idempotency replay cache stored response bodies unencrypted. The
create paths echo their own decrypted payload, so cycle notes, mood text
and allergy reactions sat in cleartext for 24 hours in a column that
lands in every backup; the existing guard missed it because it matches
secret shapes and health data is not secret-shaped. Bodies are encrypted
now, and when encryption is unavailable the response is not cached at
all rather than written in the clear.

chore(release): v1.30.18
2026-07-19 01:28:20 +02:00
Marc-André Bombeck 586c4318ca chore(release): v1.30.17 — enforce Bearer token scope 2026-07-19 01:13:11 +02:00
Marc-André Bombeck 20ff0c1868 fix(measurements): stop the derived resting mint from suppressing itself
The dense-intraday fold tombstones the raw heart-rate readings a proxy
account's resting figure is derived from, and mints one derived
RESTING_HEART_RATE row per folded day to carry the figure forward. The
mint is skipped for accounts that already hold native resting rows,
because the read resolver ignores the proxy for them.

The probe behind that skip matched every source, including the COMPUTED
rows the mint itself writes. From the second run onward it therefore
reported native resting data for every proxy account, skipped the mint,
and let the fold delete that day's raw readings anyway - losing the
resting figure permanently, once per run.

Scope the probe to non-computed sources. Adds a regression test that
reproduces the second-run state and fails on the previous code.

chore(release): v1.30.16
2026-07-19 00:13:54 +02:00
Marc-André Bombeck d0a74e37da fix(ai): keep the output-language directive terminal on composed prompts
getBaseSystemPrompt appended the directive, then each metric module
appended its own section after it, so the directive was no longer the
last instruction the model reads - the one property that makes it
authoritative. The base now exposes its body, and every composing module
wraps the finished prompt so the directive ends it.

Also drops a duplicate import that survived the merge of the two prompt
branches.
2026-07-18 23:54:09 +02:00
Marc-André Bombeck 5b464b26de chore(release): v1.30.14 — server-prefetch the workouts list; correct the aggregated heart-rate bucket docs
Move the workouts list read into a shared cached module so the RSC
wrapper and GET /api/workouts share one deduplicated projection pass,
and dehydrate it under the client cell's exact query key so the page
paints its rows instead of a loading skeleton.

Correct the aggregated heart-rate externalId documentation: the example
and the min/max field notes still described the original hourly bucket,
while the accepted contract has been 10-minute buckets since v1.30.7.
2026-07-18 22:42:23 +02:00
Marc-André Bombeck 7802903d93 chore(release): v1.30.13 — server-prefetch the insights, medications, and coach pages 2026-07-18 20:38:35 +02:00
Marc-André Bombeck 9e2c445ad7 chore(release): v1.30.12 — index the dashboard medication reads 2026-07-18 18:04:41 +02:00
Marc-André Bombeck c2420d7f02 chore(release): v1.30.11 — native OIDC SSO sign-in 2026-07-18 17:03:35 +02:00
Marc-André Bombeck 57e855eeb3 chore(release): v1.30.10 — localized correlation insights + richer workout detail 2026-07-18 16:13:53 +02:00
Marc-André Bombeck f3d92520d1 chore(release): v1.30.9 — faster dashboard first load 2026-07-18 14:43:57 +02:00
Marc-André Bombeck d4f78811d9 chore(release): v1.30.8 — harden aggregated heart-rate bucket validation 2026-07-18 11:59:38 +02:00
Marc-André Bombeck 0b37c15a52 chore(release): v1.30.7 — 10-minute aggregated heart-rate upload contract 2026-07-18 11:21:17 +02:00
Marc-André Bombeck cf3f217e01 chore(release): v1.30.6 — accessibility pass 2026-07-18 10:29:43 +02:00
Marc-André Bombeck 0f5b18d946 chore(release): v1.30.5 — reachable ECG surface and a sync-status fix 2026-07-18 09:26:58 +02:00
Marc-André Bombeck c93d02c089 chore(release): v1.30.4 — read-only assistant connector reaches the full record 2026-07-18 08:07:35 +02:00
Marc-André Bombeck aaa05241b3 chore(release): v1.30.3 — quality re-check follow-ups 2026-07-18 06:49:16 +02:00
Marc-André Bombeck de8368cf01 chore(release): v1.30.2 — coach history, navigation, correctness, and performance 2026-07-18 05:08:06 +02:00
Marc-André Bombeck ccfda544f8 chore(release): v1.30.1 — mobile, feedback, and consistency polish 2026-07-18 03:55:34 +02:00
Marc-André Bombeck efdcafb08d chore(release): v1.30.0 — quality pass, document summaries, efficacy chart
Correctness (multi-source dedup, ordering, Strava sport, PR/correlations), medical-safety fixes, faster Today view, onboarding, app-wide year-on-dates, a metric catalog, a fuller backup, accessibility, and internal refactors — plus document auto-summaries and the medication efficacy chart.
2026-07-18 02:12:52 +02:00
Marc-André Bombeck 85a05efb4d chore(release): v1.29.5 — OIDC redirect base + same-origin redirect hardening 2026-07-17 17:30:54 +02:00
Marc-André Bombeck 05f80ad378 fix(workouts): order the list newest-first; show the year on past-year dates
The canonical picker re-sorts its output to startedAt ASC for its rollup callers, so the list route sliced the oldest page and recent workouts never surfaced on a long history — re-establish newest-first after the pick. And a workout from a previous year now shows its year so it is not read as this year's.
2026-07-17 16:53:06 +02:00
Marc-André Bombeck c527cd5aa4 chore(release): v1.29.3 — hydration and micronutrients surface
Fluid intake and device-recorded vitamins/minerals get a home: a dashboard water tile with quick-add, and a Nutrients page under Insights showing hydration over the last month, caffeine, and the micronutrients with data — each against its reference intake as context. Manual and device-synced water coexist via a source-aware day-total key.
2026-07-17 16:13:41 +02:00
Marc-André Bombeck 2b5fb617ba chore(release): v1.29.2 — WHOOP workout sport, intraday history, Today dismiss, vitals info-tip
WHOOP workouts map to canonical sports (bike rides show as cycling) with a history backfill and a richer cross-source merge; the intraday heart-rate view gains a day navigator with 90-day dense retention and an hourly fallback for older days; milestone/ECG/tension cards can be dismissed from Today; vitals context moves into an (i) popover and the VO2max heading no longer clips; Oura cycle phases feed the cycle tracker where available.
2026-07-17 14:35:06 +02:00