Commit Graph
8 Commits
Author SHA1 Message Date
Marc-André Bombeck 9a5206ee5f docs(openapi): give rhythm-events its own six-value classification enum
GET /api/insights/rhythm-events was never in the spec, so a client had
nothing to generate a type from but the three-value ECG classification
next door — reusing it would drop VERY_LOW / FIRED. Register the route
with its own RhythmEventClassification carrying all six
RhythmClassification values; the ECG enum stays untouched at three.
2026-07-30 16:06:53 +02:00
Marc-André Bombeck 3aff2c16db fix(insights): scope generation guard to prompt fields, clear cache on restore 2026-07-28 21:11:43 +02:00
Marc-André Bombeck 666a85d311 feat(insights): persist correlation patterns 2026-07-28 14:01:11 +02:00
Marc-André Bombeck 49ed0f41df feat(insights): view ECG recordings
ECG recordings synced from a compatible watch were stored encrypted but
shown nowhere. They now have a surface in Insights: a list of recordings,
each opening the full single-lead waveform on a familiar ECG grid, with the
date, duration, average heart rate, and the result the device recorded.

The read routes narrow ownership in the query and decrypt the waveform
through the existing fail-closed codec; the overview is min/max-decimated so
the R-wave peaks survive the reduction, with a raw path for a calibrated
view. The result shown is the recording device's own classification,
attributed to the device — HealthLog does not read or interpret the trace
and produces no verdict of its own. A permanent note states this, and a
non-normal result carries a line to discuss the recording with a clinician.
2026-07-16 09:55:35 +02:00
Marc-André Bombeck cd0fbddd1d chore(release): v1.28.44 — loose ends: broken link, API contract, module guards
From the campaign's dead-code + code-health audits (grep-verified; the
custom-metrics query-key finding was verified-false and excluded):

- sleep-overview empty-state CTA linked to /settings/devices, not a valid
  settings slug (dynamicParams=false → 404). Repointed to
  /settings/integrations (where every device OAuth callback already lands).
- six live, iOS-consumed routes were absent from docs/api/openapi.yaml —
  auth/check-user, insights/cards, integrations/healthkit (GET+PATCH),
  measurement-categories, medications/intake/bulk, mood-entries/bulk —
  registered in the Zod OpenAPI registry mirroring each handler's real shape.
- mood/labs/medications module pages lacked the redirect-when-off self-guard
  their siblings have; added, mirroring cycle/achievements exactly.

No migration. Verifier REJECTED the custom-metrics bare-key finding (they are
invalidateQueries prefix filters, not colliding useQuery keys).
2026-07-16 04:59:27 +02:00
Marc-André Bombeck 1212ed91d5 chore(release): v1.28.21 — sleep history heals itself; plateau context; sync diagnostics
A one-time background pass re-reads each connected account's Google Health
sleep history so nights stored twice before the sleep fix collapse to their
true total (new sleep_repaired_at marker, migration 0238; watermark-safe,
enqueued once at boot per un-repaired connection).

The sleep night read reports when two writers disagree about the same night —
the served total is unchanged, the disagreement rides along as an annotation
(sourceDiscrepancy on the night DTO) so clients can mark the number instead of
silently picking a side.

A GLP-1 medication whose weight has plateaued on the current dose says so next
to the estimated drug level (efficacy tab + insights) — a factual note from the
existing detector, association only, verb-guard covered.

Google Health diagnostics: the sync log now separates a daily-totals read that
returned nothing from one the importer could not parse
(googleHealth.rollup.emptyResponse vs droppedAll), and the connection test's
structure probe reports the raw response envelope key names when a rollup type
parses to zero — a per-account naming drift becomes visible from one report.
2026-07-10 21:23:00 +02:00
Marc-André Bombeck 0790a96ca4 feat(labs): add an AI assessment and stat strip to the biomarker detail page
Give each biomarker a detail page that matches the metric sub-pages: a
numbers-first Min/Max/Median/Mean strip over the numeric readings and a
data-grounded AI assessment card.

The assessment is served by a new read-only endpoint
GET /api/insights/biomarker-assessment?biomarkerId=&locale=, returning the
same envelope as /api/insights/metric-status so InsightStatusCard consumes
it unchanged. A new generator reads the marker's LabResult history and
reuses the shared provider runner, the stale-while-revalidate read-only
path, and the persist helper. Regeneration is gated on a fingerprint of the
latest reading, so the assessment is rebuilt only when a new reading lands;
an idle marker re-stamps the cached text without an LLM call. The
biomarker: scope rides the existing on-demand status queue.

The stat strip and the assessment card both self-gate on numeric data, so a
qualitative-only or brand-new marker paints neither.
2026-06-28 13:43:14 +02:00
Marc-André Bombeck d1be2a9c6b refactor(openapi): split insights route registry into schemas + paths behind a barrel 2026-06-21 01:06:59 +02:00