Commit Graph
409 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 25a56983b3 fix(medications): preserve wizard state during URL cleanup 2026-07-21 18:09:23 +02:00
Marc-André Bombeck 7f55083c3c chore: release v1.31.8 2026-07-21 16:29:04 +02:00
Marc-André Bombeck 0d5d61bc36 fix(mcp): stabilize lab search pagination (#578) 2026-07-21 16:17:16 +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 085aa3f5c5 fix: harden pnpm supply chain and runtime image (#572) 2026-07-21 04:36:53 +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 00a8c81c8d docs: include Polar and dependency fixes 2026-07-20 14:40:09 +02:00
Marc-André Bombeck f36bbc51cd docs: finalize v1.31.2 recovery notes 2026-07-20 14:05:58 +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 44add3bc1e docs(release): correct v1.31.0 migration count 2026-07-19 20:25:37 +02:00
Marc-André Bombeck c97ad3ed55 fix(release): harden Today arrival rendering 2026-07-19 19:55:40 +02:00
Marc-André Bombeck d40da09558 chore(release): v1.31.0 — the record that reacts 2026-07-19 17:04:40 +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 2ab21bb433 chore(release): v1.30.32 — consent that stays withdrawn; quotes that come from the document 2026-07-19 11:59:28 +02:00
Marc-André Bombeck 02a56047a4 Merge branch 'main' into release/v1.30.31
# Conflicts:
#	CHANGELOG.md
#	docs/api/openapi.yaml
#	package.json
#	public/sw.js
2026-07-19 10:49:00 +02:00
Marc-André Bombeck 54634a4f12 Merge branch 'main' into release/v1.30.30
# Conflicts:
#	CHANGELOG.md
#	docs/api/openapi.yaml
#	package.json
#	public/sw.js
2026-07-19 10:32:59 +02:00
Marc-André Bombeck d85e7bf63c fix(documents): tell the summary outcomes apart instead of promising one forever
A summary is enqueued only when a document is uploaded while automatic
reading is already on, and nothing backfills the ones uploaded before
that. The detail view read a null summary as 'being generated' and said
so permanently, while the manual action row was hidden precisely when
automatic reading was on - so the promise could never be met and the
repair was out of reach.

Record what actually happened. Seven outcomes previously collapsed into
the same null: never enqueued, opted out, no vision provider, spent
budget, consent refused, unreadable file, provider error, plus the
withhold. Each now names itself and offers a manual generation, and what
that produces is kept.

The withhold rule is unchanged - refused prose is still never shown or
stored - but recording the refusal as a state makes it re-attemptable
instead of a permanent dead end.

Also removes the read-by-AI footer.
2026-07-19 10:14:35 +02:00
Marc-André Bombeck e4f5aa0b09 fix(ui): drop the redundant Today briefing link, align the sheet close control
The Today hero carried a read-the-full-briefing link to /insights while
the health-score ring above it already opened the same page — two doors
to one room in a single card. The ring stays; the link and its six
locale strings are gone.

The document sheet's close control sat at the plain p-4 right edge, but
an icon button carries its own inner inset, so the glyph landed ~28px
from the frame against a title starting at 16px. Trim the right padding
by that inset when a consumer renders its own trailing control, leaving
the tap target reaching the frame.

Also rewrites the v1.30.15-v1.30.31 changelog entries: they had drifted
into summary lines, internal vocabulary and reasoning asides that do not
belong in a release note.
2026-07-19 09:46:57 +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 1679d24660 chore(release): v1.30.30 — locale completeness and honest failed-read states 2026-07-19 07:02:48 +02:00
Marc-André Bombeck 57626c9c28 chore(release): v1.30.29 — MCP write scope, fenced connector text, and the last legacy budget helper removed 2026-07-19 06:01:01 +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 ff6956decb test(auth): pin the fail-closed Bearer-scope contract
Four structural guards freeze the invariants the change rests on: the set of
files that can resolve a Bearer token to a user, the single deliberate
any-valid-token posture, the mint sites with their scope sets, and the rule
that a declared scope is an exported constant rather than a literal.

Thirteen behavioural cases run against a real Postgres and the real resolver.
Four of them go red on the pre-fix tree — a narrow token reading the
full-backup export and the labs surface, and an MCP token on a REST read.
The rest pin the flows that must NOT change: the per-medication ingest pair
still works, the token without the per-medication grant is still refused, a
wildcard token still reaches the export and the batch ingest, an MCP token
still resolves on /mcp with its write scope intact, a declared fhir:read
still opens the FHIR face and nothing else, cookie sessions are unaffected,
and requireAdmin still refuses a wildcard Bearer.

The workout-batch case that asserted narrow tokens are admitted claimed the
iOS sync depended on it. It does not: workouts:ingest has no mint site
anywhere, and iOS holds a ["*"] token. Replaced with a case that asserts both
halves of the real contract.

Documentation updated for the enforcement rule and the retired mint.
2026-07-19 01:07:43 +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 e04383f015 chore(release): v1.30.15 — write generated health texts in the account's own language 2026-07-18 23:54:56 +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