Commit Graph
4203 Commits
Author SHA1 Message Date
Marc-André Bombeck ecf2d1a7d5 style: run prettier over the import-worker reconcile addition 2026-07-23 10:01:09 +02:00
Marc-André Bombeck 6715c4a69a observability(intraday-pulse): surface per-shape row counts and HR-bucket write outcomes
Related to #585. Investigated the intraday pulse chart's read path
end to end: the ZIP-import raw-sample fold, the live-sync uploaded
10-min bucket fold, their merge, and the chart's render logic. All of
it treats the two ingestion shapes consistently — the chart has no
shape awareness at all, it paints whatever bucket series it receives.
No read-side or display-side defect explains a systematic density
difference between the two ingestion paths.

What was missing is visibility into the write and read sides that
would let a future report actually be diagnosed instead of
re-investigated from scratch. The batch endpoint's wide-event trace
never broke a skip down by reason, and never distinguished an
HR-bucket write from any other entry in the same request. The
intraday route's own trace surfaced only the final bucket count, with
no visibility into which shape (raw sample rows vs uploaded bucket
rows vs folded hourly rows) a day's data actually came from. Both
traces now carry that breakdown, so the next report of "the chart
looks sparser after a given date" is answerable by grep instead of by
re-deriving the whole read path again.
2026-07-23 09:55:23 +02:00
Marc-André Bombeck fb50335459 fix(insights): stop the pulse page from silently swapping to resting heart rate
Closes #584. `/insights/pulse` computed hasRestingHr from
RESTING_HEART_RATE presence and used it to swap the ENTIRE primary
chart and the Coach read strip to that series whenever any resting
row existed, while the title, stat strip, capture action, and
"show all values" link kept saying PULSE. A user could see a
resting-only chart under a page that claimed to be pulse throughout.

The chart now always includes PULSE as its first series; when resting
data exists, RESTING_HEART_RATE renders as a second, clearly-labelled
series (the same multi-series pattern the blood-pressure page uses for
systolic/diastolic) instead of replacing it. The Coach read strip
stays pinned to PULSE. The page also drops the resting-calibrated
target band it used to paint behind the chart — that band is
calibrated to steady-state resting readings and would misreport an
expected workout spike in raw PULSE data as "out of target"; the
personal target belongs on the dedicated resting-pulse page against
the clean resting series.
2026-07-23 09:38:03 +02:00
Marc-André Bombeck 79843f8770 fix(import): stream the Apple Health ZIP extraction and sweep orphaned import jobs
Closes #588. The ZIP extractor decompressed export.xml synchronously
into one in-memory buffer, then wrote it with a blocking writeFileSync
— on the same event loop the web server runs in the default
single-container deployment. A real multi-year export could pin the
Node main thread for minutes, freezing every other request, while
briefly holding roughly 2x the decompressed size in heap. A process
that died mid-extraction never reached the failure path, so the
ImportJob row stayed stuck in "unpacking" with "0 rows imported"
forever and no error ever surfaced.

Extraction now streams through zlib's async inflate into a file write
stream, moving the decompression work off the event loop and bounding
memory to the chunk size. The zip-bomb ceiling moves from
inflateRawSync's maxOutputLength to an equivalent byte-counting
transform on the streamed output.

Orphan-ImportJob reconciliation used to run once, at worker boot.
docker-compose's restart:unless-stopped does bring a crashed worker
back, but a fast restart can land before the stuck row's heartbeat
goes stale or pg-boss stops reporting the job as live — and because
the boot-time pass never re-runs on a healthy worker, that row was
never revisited again. A 15-minute cron now re-runs the same reconcile
so a genuinely abandoned import converges to a visible failure instead
of staying silently stuck.
2026-07-23 09:23:28 +02:00
Marc-André Bombeck 62eb431d66 release: v1.32.0 — hardening and scalability (#580)
* release: prepare v1.32.0

* test: stabilize water capture re-entry
v1.32.0
2026-07-22 04:14:15 +02:00
Marc-André Bombeck b01822be48 Merge pull request #579 from MBombeck/release/v1.31.8
chore: release v1.31.8
v1.31.8
2026-07-21 18:20:40 +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) v1.31.7 2026-07-21 13:08:28 +02:00
Marc-André Bombeck 403eabb888 fix: restore runtime Prisma migration loading (#574) v1.31.6 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
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 e38179e854 Merge pull request #571 from MBombeck/release/v1.31.4
release: v1.31.4 operational hardening
2026-07-21 02:41:45 +02:00
Marc-André Bombeck 5fe952b63d fix(ci): harden release and e2e verification 2026-07-21 02:30:40 +02:00
Marc-André Bombeck b38f41b99d chore(release): prepare v1.31.4 2026-07-21 01:34:29 +02:00
Marc-André Bombeck 3ce6726646 fix(ci): harden test determinism and dependencies 2026-07-21 00:36:40 +02:00
Marc-André Bombeck 9adeb8fc19 fix(runtime): harden process startup and health 2026-07-21 00:05:03 +02:00
Marc-André Bombeck 914e7658db ci: harden image publishing 2026-07-20 23:47:19 +02:00
Marc-André Bombeck be1d0fb728 Merge pull request #570 from MBombeck/fix/document-summary-coach-mobile
fix: localize document summaries and refine mobile coach
v1.31.3
2026-07-20 22:45:51 +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 f1a1887a01 Merge pull request #569 from MBombeck/release/v1.31.2-data-integrity
release: HealthLog v1.31.2 data integrity hardening
v1.31.2
2026-07-20 15:26:16 +02:00
Marc-André Bombeck ad5806c53a chore: format Polar client fix 2026-07-20 14:43:54 +02:00
Marc-André Bombeck 00a8c81c8d docs: include Polar and dependency fixes 2026-07-20 14:40:09 +02:00
Marc-André Bombeck 1840779dd7 fix(polar): align collection response fields 2026-07-20 14:36:21 +02:00
Marc-André Bombeck 3c5afea912 test(documents): format PDF cleanup coverage 2026-07-20 14:30:44 +02:00
Marc-André Bombeck b9425de40e fix(polar): use AccessLink collection endpoints 2026-07-20 14:25:24 +02:00
Marc-André Bombeck 74a386fcfd chore(deps): update July dependency bundle 2026-07-20 14:18:48 +02:00
Marc-André Bombeck f36bbc51cd docs: finalize v1.31.2 recovery notes 2026-07-20 14:05:58 +02:00
Marc-André Bombeck d966355f83 chore: format data integrity fixes 2026-07-20 14:02:08 +02:00
Marc-André Bombeck a846bbc90d fix(import): make mood collision probing order independent 2026-07-20 13:55:22 +02:00
Marc-André Bombeck 4124c483eb test(import): type legacy mood retry mocks 2026-07-20 13:53:31 +02:00
Marc-André Bombeck ca1ba8b128 test(whoop): type morning refresh mock 2026-07-20 13:53:06 +02:00
Marc-André Bombeck 6ebc278c2c fix(import): preserve repeated legacy mood rows 2026-07-20 13:47:28 +02:00
Marc-André Bombeck e9507a0c93 fix(backups): address restore review findings 2026-07-20 13:42:38 +02:00
Marc-André Bombeck 7eb3b91925 fix(import): resolve legacy mood timestamp collisions 2026-07-20 13:35:37 +02:00
Marc-André Bombeck cd6b5f0cc6 fix(backups): preserve canonical restore identity 2026-07-20 13:25:37 +02:00
Marc-André Bombeck 8b7cbddd72 fix(import): derive stable legacy mood timestamps 2026-07-20 13:25:13 +02:00
Marc-André Bombeck 9484240688 fix(whoop): settle backfill side effects 2026-07-20 13:24:49 +02:00
Marc-André Bombeck 1b717b4ca0 fix(whoop): fold committed backfill chunks 2026-07-20 13:16:34 +02:00
Marc-André Bombeck 007ea76327 fix(import): stabilize legacy mood retries 2026-07-20 13:12:55 +02:00
Marc-André Bombeck 197f7bf5a1 fix(whoop): persist backfill chunks durably 2026-07-20 13:08:48 +02:00
Marc-André Bombeck e47d02702d fix(import): stabilize legacy mood retries 2026-07-20 13:03:36 +02:00
Marc-André Bombeck 5fc0b1eead docs(api): sync spec for v1.31.2 2026-07-20 12:32:03 +02:00
Marc-André Bombeck a87a144b1e chore(release): prepare v1.31.2 2026-07-20 12:29:21 +02:00
Marc-André Bombeck 2716f302c7 chore: align hardening tests and formatting 2026-07-20 12:24:15 +02:00
Marc-André Bombeck 5d784a639c fix(integrations): serialize refresh and cursor writes 2026-07-20 12:09:32 +02:00
Marc-André Bombeck a8ac376cf7 fix(measurements): reconcile dual identities atomically 2026-07-20 11:55:09 +02:00
Marc-André Bombeck f8b0a64d69 fix(backups): preserve canonical recovery integrity 2026-07-20 11:44:29 +02:00
Marc-André Bombeck 5f4017d039 fix(withings): queue ECG webhooks durably 2026-07-20 11:37:23 +02:00