7971 Commits
Author SHA1 Message Date
Marc-André Bombeck b787bc3176 Merge pull request #1198 from MBombeck/release/v1.42.1
Release v1.42.1
v1.42.1
2026-10-10 14:18:23 +02:00
Marc-André Bombeck 099ea514b1 chore(release): v1.42.1 2026-10-10 13:56:41 +02:00
Marc-André Bombeck 1afe0ee2e4 Merge pull request #1197 from MBombeck/fix/dashboard-top-card-gap
Keeps the gap under the dashboard's top card.
2026-10-10 13:54:47 +02:00
Marc-André Bombeck 020663d1f7 Merge branch 'main' into fix/dashboard-top-card-gap 2026-10-10 13:30:02 +02:00
Marc-André Bombeck 3ad82590c6 Merge pull request #1196 from MBombeck/fix/google-health-1194-1195
Refs #1194, #1195
2026-10-10 13:29:59 +02:00
Marc-André Bombeck 01aa4e5ba1 fix(dashboard): keep the gap under the top card 2026-10-10 13:21:59 +02:00
Marc-André Bombeck 46047e57c0 fix(google-health): keep long backfills out of reauth parking and settle overlapping sleep segments
A history backfill carried one access token through walks that outlive it.
After an hour every request answered 401, which classifies as a revoked
grant, so the account was parked at error_reauth with a refresh token that
was still valid, and every later run skipped it until a manual reconnect.
Running beside the hourly sync, the backfill also pushed the account over
Google's per-minute quota, and each 429 failed its whole collection.

- Walks take a token source that renews the access token before it expires
  and once more when Google refuses it mid-walk.
- A 429 is waited out (Retry-After, RetryInfo, else a doubling backoff) and
  the request repeated; it is never a reauth.
- error_reauth only short-circuits a run when the token endpoint refused the
  grant (needsReauth). A connection parked by a data request's 401 refreshes
  on its next run and clears itself when that run completes.
- One run per account at a time: syncs, the backfill and the sleep repair
  share an advisory lock. The hourly poll skips a held account, a manual
  sync answers 409 sync_in_progress, the backfill waits a bounded time.
- A backfill attempt records each collection it finishes cleanly
  (backfill_progress) and the next attempt resumes with the rest.
- The incremental watermark is stamped with the cycle's start, not its end.

Overlapping sleep segments that share a stage and an end share the natural
key (type, measuredAt, source, sleepStage). A re-score that moved one row
onto a sibling's slot threw P2002 and failed the sync on every run.

- A batch keeps one reading per natural key, chosen the same way each time
  (longer segment, then smaller externalId).
- A move that still collides is settled by reconcileExternalMeasurement,
  which retires the stale row, instead of failing the sync.
- Updates run before inserts, and the key-format rescue no longer re-keys a
  row this batch is moving.
- The sleep replace-by-window protects every session's fresh rows, so two
  overlapping sessions no longer tombstone and resurrect each other hourly.

Refs #1194, #1195
2026-10-10 12:59:25 +02:00
Marc-André Bombeck f0b4e902bb Merge pull request #1193 from MBombeck/fix/maintenance-shm
Refs #1192
2026-10-10 12:19:02 +02:00
Marc-André Bombeck a2ee79a746 fix(maintenance): run the measurement vacuum without a /dev/shm segment 2026-10-10 09:33:17 +02:00
dependabot[bot] e1eebebd1d chore(deps-dev): bump the development-weekly group across 1 directory with 3 updates (#1191)
Bumps the development-weekly group with 3 updates in the / directory: [@next/bundle-analyzer](https://github.com/vercel/next.js/tree/HEAD/packages/next-bundle-analyzer), [lefthook](https://github.com/evilmartians/lefthook) and [shadcn](https://github.com/shadcn-ui/ui/tree/HEAD/packages/shadcn).


Updates `@next/bundle-analyzer` from 16.3.3 to 16.3.8
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/commits/v16.3.8/packages/next-bundle-analyzer)

Updates `lefthook` from 2.2.1 to 2.1.17
- [Release notes](https://github.com/evilmartians/lefthook/releases)
- [Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evilmartians/lefthook/compare/v2.2.1...v2.1.17)

Updates `shadcn` from 4.21.4 to 4.21.3
- [Release notes](https://github.com/shadcn-ui/ui/releases)
- [Changelog](https://github.com/shadcn-ui/ui/blob/main/packages/shadcn/CHANGELOG.md)
- [Commits](https://github.com/shadcn-ui/ui/commits/shadcn@4.21.3/packages/shadcn)

---
updated-dependencies:
- dependency-name: "@next/bundle-analyzer"
  dependency-version: 16.3.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-weekly
- dependency-name: lefthook
  dependency-version: 2.1.17
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-weekly
- dependency-name: shadcn
  dependency-version: 4.21.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-weekly
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-10 02:07:07 +00:00
Marc-André Bombeck b10a1132e6 Merge pull request #1190 from MBombeck/deps/vitest-5
chore(deps): vitest 5
2026-10-10 03:37:09 +02:00
Marc-André Bombeck 91c358c56d Merge remote-tracking branch 'origin/main' into deps/vitest-5 2026-10-10 02:44:39 +02:00
dependabot[bot] c1309fba33 chore(deps): bump the production-weekly group with 2 updates (#1186)
Bumps the production-weekly group with 2 updates: [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) and [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `@aws-sdk/lib-storage` from 3.1146.0 to 3.1147.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1147.0/lib/lib-storage)

Updates `lucide-react` from 1.53.0 to 1.52.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.52.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: "@aws-sdk/lib-storage"
  dependency-version: 3.1147.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-weekly
- dependency-name: lucide-react
  dependency-version: 1.52.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-weekly
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-10 00:15:35 +00:00
Marc-André Bombeck 6b11739a51 Merge pull request #1187 from MBombeck/deps/dotenv-18
chore(deps): dotenv 18
2026-10-10 02:12:14 +02:00
Marc-André Bombeck e4879522c0 Merge pull request #1185 from MBombeck/deps/dev-2026-10-09
chore(deps): weekly development updates
2026-10-10 01:43:51 +02:00
Marc-André Bombeck 013a3fb86a chore(deps): move the test runner to vitest 5
vitest and @vitest/coverage-v8 move from 4.1 to 5.0.3 together. Neither
config uses an option the major removed or changed: no projects, so the
new setupFiles concatenation does not apply; no browser mode; and the
integration config's forks pool, serial files and isolation keep their
meaning. Node 22.12 and Vite 6.4 are the new floors, and both are met.
2026-10-10 01:15:10 +02:00
Marc-André Bombeck 369775e039 chore(deps): move dotenv to 18
The breaking changes in 18 do not reach this repository: nothing preloads
dotenv with -r, nothing reads a .env.vault, and both entry points in use
(the default export in prisma.config.ts and the side-effect dotenv/config
import in the one-shot scripts) keep their behaviour. dotenv/config stays
quiet by default.

What does change is the load message, which now goes to stderr. Prisma
reads prisma.config.ts on every migrate deploy at container start, so
both loads there pass quiet: true rather than adding two stderr lines to
each boot. The image's /opt/prisma-cli install, which the config resolves
dotenv from, moves to the same 18.0.6.
2026-10-10 01:08:57 +02:00
Marc-André Bombeck 6d2f22e922 Merge pull request #1184 from MBombeck/deps/production-2026-10-09
chore(deps): weekly production updates, next and zod held back
2026-10-10 01:00:02 +02:00
Marc-André Bombeck 1d782d88e2 chore(deps): take the weekly development updates
@testcontainers/postgresql 12.2.0, @types/node 26.6.4, @vitejs/plugin-react
6.1.2, knip 6.40, lefthook 2.2, pdfjs-dist 6.4.299 and shadcn 4.21.

pdfjs-dist is a development dependency that the image ships: the runner
hoists the pinned copy for document rendering. 6.4 asks for
@napi-rs/canvas ^1.0.10, which the production updates bring, so the store
keeps a single canvas build for it. eslint-config-next and
@next/bundle-analyzer stay on the 16.3 line with next.
2026-10-10 00:49:17 +02:00
Marc-André Bombeck ff0a81154c chore(deps): keep next on 16.3.8, 16.4.0 breaks history-driven URL state
With 16.4.0 a document sheet opened through ?doc= stays open after Back,
and the [ / ] day keys sometimes leave ?day= where it was. Three end-to-end
journeys fail 3 of 40 repeated runs on 16.4.0 and 0 of 80 on 16.3.8 with
every other update in place, and CI hit the same journeys on both
viewports. 16.3.8 already carries every published security fix, so
Dependabot skips 16.4.0 for next and the two packages that move with it.
2026-10-10 00:36:36 +02:00
Marc-André Bombeck c2ac1428e5 chore(deps): take the weekly production updates, zod held on 4.4
Next 16.4.0, pg-boss 12.37.1, the AWS SDK 3.1146, @napi-rs/canvas 1.0.10,
@node-rs/argon2 2.2.2, @simplewebauthn/server 14.0.3, TanStack Query and
Virtual, lucide-react, mmdb-lib, nodemailer, otpauth, pg, radix-ui,
react-day-picker, react-hook-form, tailwind-merge and yaml.

pg-boss moves its schema from 42 to 45 (three additive migrations, nullable
columns and one partial index, no table rewrite); the worker applies them on
start as before. The retention test pins the installed version, so it moves
to 12.37.1 after re-checking every source line it asserts.

zod stays on 4.4.3. 4.5 requires seconds in `z.iso.datetime()` and 4.6
rewrites nullable unions in the generated JSON Schema into `type` arrays,
which reshapes the OpenAPI document and the MCP tool schemas. Dependabot
skips zod 4.5 and later until that move is made on purpose.
2026-10-09 23:03:03 +02:00
Marc-André Bombeck 90050815eb Merge pull request #1178 from MBombeck/release/v1.42.0
Release v1.42.0
v1.42.0
2026-10-09 20:46:02 +02:00
Marc-André Bombeck e6a2429df9 fix(documents): keep four tiles across a 1440 px window with the day strip docked 2026-10-09 20:21:10 +02:00
Marc-André Bombeck 3becc235ae docs(release): retake the timeline images with the lab count as its own chip 2026-10-09 19:55:11 +02:00
Marc-André Bombeck d5a153893c Merge v142/release-images into release/v1.42.0 2026-10-09 19:44:09 +02:00
Marc-André Bombeck 27781ef12c Merge v142/last-chips into release/v1.42.0 2026-10-09 19:44:09 +02:00
Marc-André Bombeck d068d6c9e6 fix(day,timeline): put an intake's dose in its name and set a lab day's count apart
Under "What happened" an intake now reads "Ramipril 5mg" on one line, as
the same medication does under Running that day, instead of the name over a
muted dose line. A lab day's analyte count moves from the run-on second part
to the entry's tag: a chip of its own in the selection bar, brackets in the
screen-reader line and the hover title, the muted line in the chronicle.
2026-10-09 18:55:34 +02:00
Marc-André Bombeck 8a5589c32b docs(changelog): bring the v1.42.0 day view and timeline entries to the shipped state 2026-10-09 18:46:21 +02:00
Marc-André Bombeck 6a27bdfc2d docs(release): retake the v1.42.0 release images with the panel strips
The dashboard and the Coach now show the fixed strips at the right edge
with the day docked beside them, and the search field in the top bar.
Adds the command palette searching for sleep and the Health Score
history page. The timeline keeps All with one row per medication, five
value lines and a selected quarter. Each in light and dark, from the
demo seed on a local instance.
2026-10-09 18:44:33 +02:00
Marc-André Bombeck 1319c63825 docs(release): retake the v1.42.0 release images on the final state
Day view docked beside the dashboard with its running line, values and
the five scores, and as a phone sheet; the timeline at All with one row
per medication, five value lines and a selected quarter; the phone
chronicle; the air quality and pollen card; the readiness inventory;
and the Coach beside the docked day with the new-chat button. Each in
light and dark, from the demo seed on a local instance.
2026-10-09 18:30:29 +02:00
Marc-André Bombeck 891c85a82b chore(demo): seed readiness and health-score history and a start for magnesium
The day view reads readiness from the nightly COMPUTED recovery row and
the health score from the record of each local day; neither job reaches
back, so a fresh seed showed both for today at most. Mint the rows the
jobs would have written over the window, and give magnesium the start
date its running line needs.
2026-10-09 18:30:29 +02:00
Marc-André Bombeck be51db56bb docs(release): add the v1.42.0 release images
Day view docked beside the dashboard and as a phone sheet, the timeline
at All with a selected quarter, the phone chronicle, the air quality and
pollen card, and the readiness inventory, each in light and dark. Taken
from the demo seed on a local instance.
2026-10-09 18:30:29 +02:00
Marc-André Bombeck 1199e009b2 chore(demo): carry ragweed well into October in the seeded pollen 2026-10-09 18:30:29 +02:00
Marc-André Bombeck 145d138c66 chore(demo): seed a multi-year history for the timeline and day view
The demo account carried 90 days, which leaves the timeline's All zoom
and the day view with nothing to show before last quarter. The seed now
writes about four and a half years before that window: blood pressure in
bursts with a silent quarter and a thin one, weekly weight, phone steps,
watch resting heart rate, HRV and sleep from the third year, yearly
blood panels with HbA1c, lipids and hemoglobin, a resolved chronic
condition and three infections, ramipril with a stated start, a dose
change and a sick-day pause, a finished antibiotic course, flu shots and
a Tdap booster, visits, life events at day, month and year precision, a
sealed home location with a holiday period, and the stored weather, air
quality and pollen for every day. The timeline module is switched on.

All of it is invented and relative to the seed run; rows are written in
chunks so the seed stays quick.
2026-10-09 18:30:29 +02:00
Marc-André Bombeck 96ee0c7821 test(dashboard): assert the top card switch on its region, not on the day's digest 2026-10-09 18:23:48 +02:00
Marc-André Bombeck 25d0839296 Merge the right-side panel strips, lazy day panel and calendar, and the layout write lock into release/v1.42.0 2026-10-09 18:22:48 +02:00
Marc-André Bombeck d8fb922228 feat(shell): give each docked panel a fixed strip at the right edge
From 1280 px the day and, on /coach, the conversations each keep a
narrow strip at the right edge, open or not. A click on the strip opens
its panel left of the strips and a second click closes it; the strip
never moves. Strips start below the top bar's band, side by side,
conversations first. Below 1600 px one panel is open at a time.

The day slides like the conversations (width, 200 ms, none under reduced
motion), stays open through a page change instead of folding and
reopening, and does not slide in on arrival. Its content loads when a day
first opens, so the scores, sections and calendar leave the shell's first
load: every watched route falls back under its unchanged ceiling. The
aggregate cap is re-measured for the chunk double count this causes.
2026-10-09 18:22:11 +02:00
Marc-André Bombeck 41a7f4daea perf(ui): load the date field's calendar when it first opens
react-day-picker and the seven date-fns locales rode the first load of
every page with a date field, the dashboard and the settings sections
among them. The calendar now lives in its own module that the popover
loads on first open; the locale map moves beside it.
2026-10-09 18:22:10 +02:00
Marc-André Bombeck 86fa57d8bf fix(dashboard): queue concurrent layout writes behind a row lock
Parallel PUTs to /api/dashboard/chart-overlay-prefs from one account ran
in Serializable transactions, which aborted all but one with a
serialization failure and answered 500. Lock the user row with FOR UPDATE
instead, so each writer waits for the previous commit and reads it. The
layout reset on DELETE /api/dashboard/widgets takes the same lock.
2026-10-09 17:42:00 +02:00
Marc-André Bombeck 09f84ef418 test(coach): give the live scenario runner room on a loaded machine 2026-10-09 17:33:19 +02:00
Marc-André Bombeck 018841e6d6 Merge the final display polish into release/v1.42.0 2026-10-09 17:25:50 +02:00
Marc-André Bombeck dcfec1f35d Merge the final display polish into release/v1.42.0 2026-10-09 17:25:35 +02:00
Marc-André Bombeck ac95c9c7dd fix(day): Back on the same page still closes the day
Keeping the day open across pages read every loss of ?day= as a page
change, so Back past the step that opened the day on the same page
reopened it at once. A loss of ?day= with the page unchanged is the
person leaving the day, and it closes it as before; only a change of
page carries the open day along.
2026-10-09 17:16:10 +02:00
Marc-André Bombeck fbcf099718 test(analytics): expect the latest pulse as its latest day's value
The paged read still has to see every row; the latest pulse is now the
value of its latest local day, computed in the test through the same
day-mean helper.
2026-10-09 17:09:42 +02:00
Marc-André Bombeck 3a5953bda2 Merge the score history, score ranges and top card toggle into release/v1.42.0 2026-10-09 17:02:56 +02:00
Marc-André Bombeck b94101d0fe chore(i18n): drop the keys of the removed count line, usual label and open-day link
The day's count line, the usual-range label beside the focus value and
the selection bar's "Open <day>" link are gone, and so are their strings
in all seven bundles. The pulse latest-day read is listed in the dense
read guard with why it stays bounded: one type over one day, the rows the
day view reads anyway. The summaries test's database stub answers that
read.
2026-10-09 16:57:08 +02:00
Marc-André Bombeck 67d8657336 Merge keyboard shortcuts and the command palette into release/v1.42.0 2026-10-09 16:53:21 +02:00
Marc-André Bombeck 668f568289 feat(dashboard): let the top card be switched off
The layout settings gain a switch for the dashboard's top card (the Today
card above the tiles). Stored server-side on the layout blob as
`todayCardVisible`: missing means shown, only an explicit false hides it,
the default is omitted from storage, a save that omits the field keeps
the stored choice, and a layout reset shows the card again. Hidden, the
card leaves no skeleton or error card behind. The flag rides the
account-settings backup with the rest of the layout.

The field is deliberately not `heroVisible`, the retired opt-in hero's
flag that older stored layouts may still carry.

An end-to-end journey on its own account switches the card off and on
and walks the score histories' range tabs.
2026-10-09 16:52:39 +02:00
Marc-André Bombeck ebad3665fd perf(shell): load the keyboard shortcut list on first open 2026-10-09 16:52:34 +02:00
Marc-André Bombeck 8d1283a32a style: format the separator guard 2026-10-09 16:52:30 +02:00