Commit Graph
61 Commits
Author SHA1 Message Date
Marc-André Bombeck e8c96eea88 docs: say what deletion keeps and for how long, and drop stale claims
The reproductive-health page promised that deleting all data or the
account erases at once. Both wait for the grace window now, the nightly
dumps keep a record until they age out, and the old pgdata volume is a
full copy until it is removed; the page says so and how to make deletion
final. The README, the getting-started note on majors, the move guide and
the Unraid and Portainer templates catch up with Postgres 18, the backups
volume and the recovery kit. The demo seed's spread bound leaves room for
the seed's own one-in-150 spread.
2026-10-25 16:47:00 +01:00
Marc-André Bombeck 12f8265fdb docs: correct claims and anchors the code no longer backs
Fix two dead anchors (the MCP trust model, the README quick start) and the
statements that no longer match the app: a guardian can connect a managed
profile's services, the environment module is on by default and reaches the
air-quality host too, its readable location columns were dropped in 0409,
settings sections carry their current names, the Withings card lives on
/settings/integrations, the TrueNAS start period matches the image, the
deploy check names HEALTHLOG_PROD_URL, and the docs index describes what
self-hosting/ and integrations/ hold.
2026-10-25 12:12:15 +01:00
Marc-André Bombeck 72d90282d5 fix(settings): open the docs pages themselves, not their old addresses
The integration runbooks, the Garmin note, the guardian note on a managed
record's connections, the backups admin link and the README's self-hosting
link all pointed at retired docs paths that only resolve through redirects
(or not at all, on a host without them). They now go to the final guide
pages, and the settings tests assert the new addresses.
2026-10-25 12:12:15 +01:00
Marc-André Bombeck 27c8533f40 docs: describe the Postgres 18 move and the versions HealthLog runs on 2026-10-25 11:18:14 +01:00
Marc-André Bombeck 8d9386d73a docs(truenas): cover the Community train app, its backups, and when the catalog needs a pull request
The TrueNAS page now walks through the catalog install field by field, how
to generate the two secrets, where TrueNAS keeps the key and why that is not
a backup, a dump and restore from the postgres container shell, and updates
with forward-only migrations. The listing is marked as pending review, and
the install-via-YAML values stay for the time until it is live.

The deploy notes list what needs a matching truenas/apps pull request in the
same release cycle: a new or stricter required variable, a port or health
check change, a Postgres major or extension, and any other compose-shape
change.
2026-10-24 10:32:17 +02:00
Marc-André Bombeck 2d16183b58 feat(i18n): let translators edit the catalogues in the browser with Fink
Add an inlang project so Fink opens the repository directly:
English as the source, the nine shipped locales, and the
message-format plugin pinned to 4.5.5 reading messages/{locale}.json.
That plugin keeps the nested keys and {placeholder} tokens as they
are; the i18next and next-intl plugins Fink also supports either
rewrite numeric-keyed objects as arrays or misread quotes around a
placeholder as ICU escapes.

Checked against these files with the inlang CLI and SDK: all nine
locales load with no errors and no findings, a load and save without
edits leaves every file byte-identical, and an edit changes exactly
the edited lines. inlang's working files beside settings.json ignore
themselves in git and are kept out of the format check.

CONTRIBUTING gains the browser workflow (fork, push, pull request),
what a reviewer checks, and that machine translation is a draft a
reader of the language goes through before the pull request; the
new-locale steps now name the real locale list. A test keeps the
project's locales equal to the shipped ones and the plugin pinned.
2026-10-22 15:03:16 +02:00
Marc-André Bombeck 764794496c feat(i18n): register Brazilian Portuguese as a locale
Add pt-BR to the locale list and everything that hangs off it: the
client loader, Intl and date-fns mapping, the reply-language footers of
the Coach and insights prompts (English body, pt-BR footer, the same
path Korean and Dutch take), the citation-coverage normative words, the
no-key fallback copy, the AUDIT-C standard-drink line, country labels,
period search, CSV yes/no cells and the Telegram bare-word aliases.

The locale enums in the extract, managed-profile and record-settings
schemas now derive from the locale list instead of repeating it, and
the five notification and login-alert routes share one isLocale guard
instead of hand-rolled chains, so a later locale is one edit there.
A language tag is matched exactly and case-insensitively before its
primary subtag, so pt-BR and pt-PT browsers land on the Portuguese
bundle rather than English, and AUDIT-C no longer cuts a region tag to
two letters.

PHQ-9, GAD-7 and WHO-5 are not marked validated for pt-BR: the wording
has not been read against the published Brazilian forms, so readers get
the English items plus the existing note.
2026-10-22 14:50:00 +02:00
Marc-André Bombeck 79072438de docs(readme): show the current app, the timeline and the day panel
Replace the June dashboard and the old gallery with current screens: the
dashboard, the timeline, the day panel, resting heart rate, medications,
labs and the coach. The resting heart rate view stays and is extended to
the same 16:10 frame as the others.
2026-10-20 21:04:05 +02:00
Rob de Graaf c6f61fad33 feat(i18n): add Dutch as an eighth end-to-end locale
Adds messages/nl.json (AI-initial, informal je/jij register, full key
parity with en.json) and registers nl everywhere the locale list lives:
config, loaders, Intl/date-fns mapping, zod enums, OpenAPI spec, scripts,
the managed-profile picker and the notification locale guards.

Dutch follows the Korean pattern for the AI surfaces (English body plus a
Dutch reply-language footer, no safety-contract matrix) and gets its own
banks in the outbound safety screen (dose, risk, causal, continuation),
the prose-grounding and citation-coverage tables, the reference-range
prose and the no-key fallback copy, each with Dutch test fixtures.

PHQ-9 / GAD-7 / WHO-5 are not marked validated for nl yet: the Dutch
wording has not been read against the published forms, so Dutch readers
get the English items plus the existing note until it has.
2026-10-19 11:06:55 +02:00
Marc-André Bombeck 370fa10efa docs: describe the Today overview in the README hero alt text 2026-10-05 12:40:55 +02:00
Marc-André Bombeck 715df7d5d3 docs(readme): keep the contributor list without a separate line for one person 2026-10-04 08:29:43 +02:00
Marc-André Bombeck b3e18739fb docs(readme): name the people whose reports shaped HealthLog 2026-10-04 08:24:07 +02:00
Marc-André Bombeck f685cbe460 chore(release): v1.40.0 — back up the encryption key, medication courses and categories, your own symptoms, document reads in the background 2026-10-04 01:26:37 +02:00
Marc-André Bombeck 1dab2687ab docs(readme): thank contributors, reporters and supporters by what they gave 2026-10-03 17:29:34 +02:00
Marc-André Bombeck 94ac863bdf docs: describe encryption scope, PDF rendering and backups as they are
The README said everything is kept encrypted at rest, that allergies and
family history are encrypted records, that the doctor report is
generated client-side, that there is no telemetry of any kind, and that
S3 backups are switched on in the admin panel. Free text, documents,
questionnaire answers, AI-written text and credentials are encrypted;
measurement values, lab values, medication names, mood scores and the
allergy substance or family condition are not. The PDF is rendered on
the server. Off-host backups are configured through the BACKUP_*
variables, and the admin panel only shows their status. The server's own
outbound requests are named along with their switches.

.env.example now says it is a starter and points at
.env.production.example as the complete reference, which is the file CI
checks against the env manifest. The Withings client id and secret it
listed are not read from the environment at all; they are entered in
Settings.
2026-09-26 15:34:12 +02:00
Marc-André Bombeck 50717c567a docs: keep README screenshots on one line (#1022) 2026-09-20 20:15:57 +02:00
Marc-André Bombeck 6b1b061b0b Enhance README with additional images
Added images for insights, resting pulse, medications, and coach chat to the README.
2026-09-20 17:22:36 +02:00
Marc-André Bombeck 325e94a8f9 docs(readme): point at the App Store listing
The iOS client left public beta. The badge row, the navigation lines and
the iOS section named TestFlight only, which now reads as the single way
in. Both routes stay, with the price and the free-code offer stated
rather than left for someone to discover on the store page.
2026-09-20 11:36:27 +02:00
Marc-André Bombeck e0cf6c2403 chore(release): v1.38.11 — sign out everywhere means everywhere
Version anchors, the OpenAPI document, the changelog entry, and the
README sentence that still declared immunization records out of scope
while the vaccination record has shipped since v1.37.3.
2026-09-08 20:23:30 +02:00
Marc-André Bombeck 915d5dea3c Say thank you where people can see it
The contributor graph has carried every outside author correctly, because
fork pull requests are merged as merge commits and the authorship
survives onto main. But nobody reads a graph tab. The front page now
names the practice and renders the graph inline, so a first-time
contributor sees their own avatar on the page they landed on.

The avatar strip comes from a third-party renderer, the same class of
service as the four shields badges already at the top, and GitHub proxies
it so no visitor address reaches that host. It is generated rather than
hand-written on purpose: a hand-kept list is the kind of thing that goes
stale quietly.
2026-09-02 15:56:15 +02:00
aucun6352 f1b0420479 feat(i18n): add Korean as a seventh end-to-end locale
Register `ko` in the locale list, language names, and labels, and ship
messages/ko.json so the whole UI — dashboard, settings, share views,
clinician PDF, notifications, and AI prompts — renders in Korean.

Extend the locale-parity, English-leak, plural-completeness, and copy
guard tests to cover the new locale, and update README/CONTRIBUTING to
list Korean among the supported languages.
2026-08-26 16:42:45 +09:00
Marc-André Bombeck beaf51fadd docs: describe the document vault in the README highlights
The roadmap parenthetical becomes a shipped feature bullet.
2026-07-07 14:33:20 +02:00
Marc-André Bombeck 0da11811e2 docs: bring the README in line with what actually ships
The records bullet claimed inbound document handling — the document
vault is disabled and belongs on the roadmap, not in the feature list.
The device bullet gains Google Health and Nightscout, the check-in
bullet gains WHO-5 and the SCI, the AI bullet reflects guideline-band
interpretation and acceptable plans, and the language claim grows from
two locales to the six that ship.
2026-07-07 09:26:09 +02:00
Marc-André Bombeck 0224abc419 docs: bring README and docs current for v1.25
Reflect the v1.25 surface without touching the changelog or version
numbers.

- README highlights now cover the new clinical signals (respiratory
  rate, the opt-in PHQ-9 / GAD-7 mental-wellbeing self-checks with calm
  crisis handling, grip strength, pain score, waist / body measurements,
  longevity labs), the signal registry that wires each one across charts,
  correlations, the coach, FHIR, and the API at once, structured records
  (allergies, family history; immunizations noted out of scope), the
  opt-in environmental-context module, inbound document extraction
  (transcribe, never interpret), and the self-description posture (a
  record-keeping and wellness tool, not a medical device).
- ai-providers.md gains a "what the Coach is — and isn't" section: the
  non-diagnostic standard and the calm proactive morning check-in
  behaviour.
- mcp-capabilities.md notes that the new measurement signals reach the
  assistant through the existing metric and labs tools via the registry,
  that the mental-wellbeing instruments / records / documents are
  deliberately not exposed over MCP, and ties the grounding contract to
  the project-wide self-description standard.
- getting-started.md lists the environmental module's Open-Meteo egress
  as an opt-in outbound dependency.
2026-06-28 17:43:43 +02:00
Marc-André Bombeck b9368b9a69 chore(release): v1.20.0 2026-06-22 08:36:28 +02:00
Marc-André Bombeck c903c7f1b1 docs(self-hosting): add Unraid CA and Portainer templates, harden compose quick-start
Surface the load-bearing first-run vars in docker-compose.yml and the README
quick-start: secret generation, the SESSION_COOKIE_SECURE=false LAN note, the
single persistent volume, and a /api/version verify step. Add an Unraid
Community Applications template plus a NAS walkthrough covering the external
Postgres setup, and a Portainer app template referencing the compose stack.
2026-06-21 10:32:40 +02:00
Marc-André Bombeck f3dda21917 docs: the README states the demo credentials beside the live-demo link 2026-06-12 22:51:28 +02:00
Marc-André Bombeck 159de9b78f docs: refresh the README dashboard screenshot
The current dashboard (demo account) replaces the pre-v1.16 capture;
the JPEG also weighs a tenth of the old PNG.
2026-06-12 20:59:16 +02:00
Marc-André Bombeck fdf48e4890 docs: ship a current dashboard hero in the repository
The readme hero moves from an external attachment URL to a fresh
in-repo screenshot of the v1.16 dashboard.
2026-06-10 23:33:23 +02:00
Marc-André Bombeck eb5a39a1c1 docs: a readme that sells the product in one screen
The readme drops from nearly seven hundred lines to under a hundred:
value proposition, seven highlights, a six-line quickstart, the iOS
app, one tech-stack paragraph and a documentation hub — the comparison
mega-table and reference detail move toward the docs site. Repository
description and topics are aligned with it.
2026-06-10 16:18:48 +02:00
Marc-André Bombeck eb1bbef295 chore(license): move to PolyForm Noncommercial 1.0.0
The project licence changes from AGPL-3.0 to the PolyForm Noncommercial
License 1.0.0 for this and future versions: free to use, modify and
self-host for noncommercial purposes; commercial use needs a separate
arrangement. Releases up to and including v1.15.18 were published under
AGPL-3.0 and remain available under that licence. LICENSE carries the
verbatim PolyForm text, and the SPDX identifier is updated in
package.json, the version endpoint, the OpenAPI info block, the image
labels and the README.
2026-06-10 11:18:12 +02:00
Antonius Moritz 13f6252298 fix(docs): generate URL-safe Postgres passwords
openssl rand -base64 24 can emit URI-reserved characters (/, +, =). Since POSTGRES_PASSWORD is interpolated unescaped into DATABASE_URL, such a password breaks the connection URL and blocks boot. Switch every documented example to openssl rand -hex 32 (URL-safe, 256-bit).
2026-06-07 16:13:37 +02:00
Marc-André BombeckandClaude Opus 4.8 6fdbb8d889 feat(cycle): predicted-period notifications + reproductive-data privacy docs
Add two cycle reminders through the existing notification cascade and the
honest self-hosting privacy framing for reproductive-health data.

Notifications:
- New event types CYCLE_PERIOD_SOON (fires ~2 days before the predicted
  next-period start, from the cached prediction) and CYCLE_PERIOD_CONFIRM
  (a gentle "did your period start?" on/after the predicted start while no
  period is logged, within a short grace window). Both default OFF and are
  gated again on cycleTrackingEnabled — opt in twice for a sensitive surface.
- A 15-minute pg-boss cron mirrors the mood-reminder worker: it scans users
  with cycle tracking enabled + predictionEnabled, computes who is in-window
  from the cached forecast, and dispatches one push per event per local day.
  Idempotency anchors on the push_attempts ledger (no new table). The queue
  is declared, registered in allQueues, scheduled, and wired to a boss.work
  handler, with a source-grep guard so it cannot ship unregistered.
- Honours notificationPrefs.cycle.clientManaged (skip the server push when
  the device owns local reminders, with a suppressed annotation, mirroring
  the medication path) and CycleProfile.discreetNotifications (generic
  "HealthLog reminder" body so no cycle event is named on the lock screen).
- Every attempt is recorded in push_attempts via the unchanged dispatcher.

Privacy / disclaimer:
- docs/self-hosting/reproductive-health-data.md — the "no cloud vendor to
  subpoena" framing with the honest residual-threat caveat (your own host,
  provider, backups, and devices remain part of the surface), citing the
  2021 FTC settlement and 2025 verdict as public record only.
- docs/security/cycle-data-threat-model.md — a compact "protects against /
  does not protect against" note, plus README pointers.
- CyclePredictionDisclaimer component (reusing the GLP-1 estimate-disclaimer
  pattern) for every fertility/prediction surface: descriptive, not a
  contraceptive method, not medical advice, never a safe-to-have-unprotected
  -sex claim. 6-locale copy under cycle.disclaimer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 10:00:42 +02:00
Marc-André BombeckandClaude Opus 4.8 46cecc8127 docs(readme): embed all five architecture diagrams, add docs pointer table
Reference the data-flow, coach-pipeline, source-priority, and security-model
SVGs alongside the self-hosting topology already present, so the architecture
story is complete. Add a Documentation section with a pointer table into docs/
and the new code-directory READMEs. Drop the four iOS screenshot references
that pointed at an uncommitted scratch directory. Correct the model count to 61.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 15:26:34 +02:00
Marc-André Bombeck c319d81663 docs: README presentation polish + community-file gaps
Add a table of contents to the README, extend the comparison matrix with
WHOOP and Google Health/Fitbit columns plus rows for multi-source dedup,
provider aggregation, and encryption at rest, and add a CI status badge.
Reconcile two stale claims against the current code: the Prisma model
count (now 60) and the AI architecture note (add Codex). Strengthen the
Security and Privacy section with the bring-your-own-key / local-endpoint
AI posture.

Add .github/FUNDING.yml so the GitHub Sponsor button renders, and fix the
contributor prerequisite to Node.js 22 to match the Dockerfile base image.

(cherry picked from commit 7cfe326709814dd0ce16d1554d5afd5933de448c)
2026-06-05 03:01:36 +02:00
Marc-André Bombeck 9b094fc4a2 docs(readme): document Google Health/Fitbit, WHOOP, mood tracking, and source weighting
Refresh the README for the v1.12 line: add the experimental Google
Health/Fitbit connection (Fitbit/Pixel over the Google Health API, BYO
OAuth client, server-side activity/health-metric/sleep/workout sync) and
flesh out the existing WHOOP entry with its connect/test/sync/resume
controls. Rebuild the Mood section around the five-face capture, the
structured tag catalog (feelings/sleep/health/social/work/hobbies plus
nutrition tags), and the rated daily-life factors, and note the moodLog
webhook is deprecated. Add a Source priority section explaining why a
per-metric source ladder picks one canonical reading when providers
overlap. Bring the status line, integrations table, env-var table, API
reference, and roadmap current.

(cherry picked from commit 66d0987895e9a9d560afc456d91f38d54f7423ce)
2026-06-05 01:05:47 +02:00
Marc-André BombeckandClaude Opus 4.8 65c7012590 docs: document the v1.11.0 features
Add WHOOP as a synced provider (per-user BYO-keys OAuth2 + HMAC-signed
webhooks), the longitudinal coach (narrative + per-metric trajectory),
and the read-only FHIR R4 REST API plus the scoped, time-limited
clinician share-link record across README, the self-hosting and ops
runbooks, and a new WHOOP integration page. Note the two optional
WHOOP instance env vars, that migrations 0110-0114 are additive, and
the two known limitations (dual-source standard-vital dedup deferred;
coach durable-memory deferred).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 04:25:22 +02:00
tarantila 81b9606f6f fix(docs): improve README language 2026-06-03 08:11:07 +02:00
Marc-André Bombeck 4ddf385211 docs(readme): add wearable-ecosystem positioning and v1.10 derived metrics
Add an honest "Versus WHOOP / Oura / Fitbit ecosystems" subsection that
positions HealthLog on self-hosting, data ownership, multi-provider
aggregation, and the blood-pressure / glucose / medication context the
wrist bands don't hold — without claiming to be or match a wearable
device. Refresh the comparison table with BP+glucose, medication
compliance, derived metrics, and FHIR rows.

Fold the v1.10 derived-metrics tier into the status line, Key Features,
and roadmap: fitness-age, vascular-age delta, HRV balance, sleep score,
readiness and the stored recovery score, and the coincident-deviation
early-strain flag — each computed from the user's own measurements,
citing its inputs, degrading to "not enough data" rather than
fabricating a value. Stress and strain scores stay out of the copy: the
types are reserved but no engine computes them in v1.10.
2026-06-02 22:39:40 +02:00
Marc-André BombeckandClaude Opus 4.8 50e0a65228 chore(release): v1.7.0
v1.7.0 — health-record export (PDF + FHIR), flexible medication schedules, first-paint dashboard snapshot, full HealthKit coverage, offline sync delta feed, Coach data clustering. See CHANGELOG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 14:36:17 +02:00
Marc-André BombeckandClaude Opus 4.7 8ee7aaac44 chore(release): v1.6.0
Medication editor overhaul, route of administration, today-tile read-flip.

- Route-of-administration column (ORAL | INJECTION | OTHER) decoupled from
  treatmentClass; injection-site picker surfaces for any injection.
  Migration 0088 backfills ORAL via a constant column default.
- One-time injection (oneShot + deliveryForm = INJECTION) editor path.
- Profile-photo upload card on Settings → Account.
- Today-tile read-flip: /api/medications/intake?scope=today and
  /api/dashboard/summary gate through the canonical recurrence engine,
  matching the reminder worker for bi-weekly, rolling, RRULE, and one-time
  cadences.
- Measurement note cap 25 → 200; dashboard range colors on semantic tokens;
  admin sidebar icons + shared-helper consolidation; API-tokens multi-language
  snippets restored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 00:10:12 +02:00
Marc-André Bombeck d01d1d1dc9 Update README.md 2026-05-29 22:11:05 +02:00
Marc-André Bombeck 189d162dff Update README.md 2026-05-29 22:08:53 +02:00
Marc-André Bombeck edfe43d391 Update README.md 2026-05-29 22:08:35 +02:00
Marc-André Bombeck 1cd28acebc docs(readme): accuracy + strength pass
Verified the README's concrete claims against the codebase and fixed
every mismatch found, then surfaced features the codebase delivers
that the README was burying.

Accuracy fixes:
- Reference-range citation `ESC/ESH 2018` → `ESH 2023`. The 2023
  guideline withdrew the joint ESC/ESH framing; medical-citations
  module already used the 2023 variant.
- Prisma model count `26 models` → `47 models`.
- Route file count `100+` → `180+` in the architecture overview.
- Achievement count `38+` → `30+` to match the actual array length.
- Translation key count `1500+` → `2500+` per locale.
- Off-host backup cadence `weekly` → `daily` in two places. The
  cron + .env.example were always daily; the README had drifted.
- API reference table corrected:
  • `/api/auth/codex/authorize` → split into the three real children
    `device-start`, `device-poll`, `disconnect`.
  • `/api/monitoring/{glitchtip,umami}/test` → moved to the admin
    section under their actual `/api/admin/monitoring/*-test` paths.
  • `/api/auth/refresh/revoke` collapsed — refresh + revoke share
    one route (POST body opts in).
- Removed the README pointer to `docs/audit/` since that directory
  is no longer in the public docs surface.

Strength surfacing:
- Notification fan-out brag now mentions APNs as the fourth channel
  (the iOS native client's delivery surface).
- AI Coach provider list now includes ChatGPT via Codex device-OAuth
  (no API key needed) alongside OpenAI / Anthropic / local.
- New `Sub-second dashboard` feature bullet describes the persistent
  rollup tier.
- Medication Compliance bullet now calls out the day-of-week +
  `intervalWeeks` cadence support (so once-weekly GLP-1 injections
  score honestly) plus the pen-inventory + side-effect tracking
  surfaces.
- Security section now lists key versioning + zero-downtime rotation
  CLI as a first-class control, plus offline MaxMind GeoLite2 geo
  resolution and wide-event structured logging.
- Tech-stack table now flags APNs alongside the other notification
  channels.
2026-05-25 23:46:44 +02:00
Marc-André Bombeck 15440a4a9f docs(readme): cut release-badge cache TTL to 60s
The shields.io release badge inherits Cloudflare's default 5-minute edge
cache, and GitHub Camo proxies on top of that with another 5-minute TTL
— so a fresh tag (v1.5.0) can sit 10 minutes behind on the rendered
README. Adding `cacheSeconds=60` does two things at once: it creates a
new Camo hash for an immediate fresh fetch, and it pins the future
TTL at 60s so the next tag-push propagates to the badge within a
minute instead of a quarter-hour.
2026-05-24 22:54:04 +02:00
Marc-André Bombeck 394f0b0795 chore(release): v1.5.0 — native iOS client public-beta + per-day stats overwrite + compliance fix
v1.5.0 cut. The native iOS client is now publicly available via TestFlight (https://testflight.apple.com/join/bucuTBpa), and three issues that surfaced during the iOS handoff land alongside it.

## Features

- **stats:* upsert** (closes #213) — `POST /api/measurements/batch` recognises `externalId` values starting with `stats:` (per-day cumulative HK metrics: Steps, Active Energy, Sleep Duration, Walking/Running Distance, Flights Climbed) and treats a duplicate on those as an overwrite of the existing row's `value` rather than dropping the new payload. Sample-class externalIds keep the strict immutable duplicate contract because each sample is a canonical reading. New per-entry status `"updated"` on the response envelope, new `measurement.batch.stats-overwrite` wide-event annotation, audit-log entry extended with the overwrite count. Three integration tests pin the contract.

## Fixes

- **Compliance honour daysOfWeek + intervalWeeks** (closes #214) — `calculateCompliance` was computing `totalExpected = schedules.length * days`, silently ignoring `MedicationSchedule.daysOfWeek` and `intervalWeeks`. Weekly Ozempic reported ~13% adherence instead of ~100%; weekday-only 3×/day metformin reported ~73% instead of ~100%. Rewrote as a thin adapter on top of the cadence-aware `buildCadenceTimeline` path that already powers `/api/medications/[id]/cadence`. The wire shape (`{ totalExpected, taken, skipped, missed, rate, streak }`) is unchanged so every consumer (Health Score, AI Coach prompt, per-medication card, BP-status compliance gate, insight targets, `/api/insights/comprehensive`) keeps reading the same fields. Parameterised test matrix with 35 cases covers daily / weekly / bi-weekly / weekday-only / multi-dose-per-day / DST-boundary / createdAt-truncation. Health-Score regression test pinned.

## Tests

- 8 stale integration assertions retired across `withings-oauth.test.ts`, `withings-oauth-flow.test.ts`, `analytics-bp-aggregate-paged.test.ts`, `analytics-sleep-stages.test.ts`, `apns-dispatch.test.ts` — drift from the v1.4.47.x OAuth fine-grained reason tags, the v1.4.47.2 ES256 PEM verify guard, and the v1.4.49.1 analytics slim-slice annotation rename. Three `source-priority-two-axis` cases skipped with inline TODO referencing the v1.4.49.1 commit and the relocation candidate.
- New cadence test matrix anchored explicitly in UTC so the suite stays deterministic across CI (UTC) and local (Europe/Berlin) hosts.

## Docs

- README rewrite for the v1.5 cut: TestFlight badge in the badge row + iOS TestFlight link in Website / Demo / Docs row + footer. Buy Me A Coffee badge added. Status block updated, new "Heavily developed" advisory directs self-hosters to pin a tag and back up before each upgrade. Tech-Stack flags the iOS app as TestFlight-available. Roadmap promotes v1.5 from "in active development" to "current". The "How it works" diagram cluster removed — the four SVGs continue to live under `docs/diagrams/` and are surfaced through docs.healthlog.dev where they render reliably across themes.

## Final verify

- typecheck clean
- lint clean (one pre-existing warning in `withings/resume/__tests__`)
- pnpm test: 5285 passed / 1 skipped
- pnpm test:integration: 253 passed / 3 skipped
- GHA CI on PR: build amd64 + build arm64 + lint/typecheck/test + integration + e2e + dependency audit + secret scanning + dead-code gate + env check → all green
2026-05-24 22:22:30 +02:00
Marc-André Bombeck def9ae6f39 Update README.md 2026-05-24 20:42:25 +02:00
Marc-André Bombeck 88028fa082 v1.4.37 — final web polish before iOS focus
Carries the v1.4.36 perf carry-over for the full /api/analytics
route (correlations + healthScore + bp_in_target on the rollup-
coverage probe, 111s cold worst-case down to ~1.5-3s on power-user
accounts), consolidates Apple Health step samples into a one-row-
per-day list view with chevron drill-down and a nightly drain
(~50x table compression once it converges), restores
IntakeHistoryListV2 to the pre-v1.4.28 contract, converges every
Coach affordance behind a single feature gate with an invariant
test pin, lifts the medication-detail Mounjaro card to byte-
equivalent parity with Ramipril, brings the Insights overview
hero row to height parity, hardens getClientIp behind a new
TRUST_CF_CONNECTING_IP env flag, ships the Arztbericht hero card
on Settings -> Export, adds the Medikamenteneinnahme quick-add
overlay to the dashboard, drops the timezone override toggle for
a silent browser-zone auto-seed, and fixes the v1.4.36 red CI
workflows. Documentation refreshed across all three repos with
five new architecture diagrams in the Dracula palette. Unit suite
4354 -> 4486 passing; typecheck + lint clean.
2026-05-17 15:36:38 +02:00
Marc-André Bombeck f5962497d6 v1.4.34.4 — audit-driven hotfix bundle (security, UX, code-quality, docs)
Consolidated hotfix landing the findings from a six-axis audit
covering mobile security, UX + accessibility, code quality + docs,
README + discoverability, web performance, and mobile-deep responsive
behaviour. None of the changes break the public API or the existing
data shape; every fix is additive and SAFE under the v1.4.34.x web
freeze. Full audit reports live in `.planning/round-audit-*.md`.

Security
  - Password rotation now wipes API tokens + refresh tokens in the
    same `$transaction` as session revocation. A stolen iOS Bearer no
    longer survives a user-initiated password change or an admin reset.
  - `/.well-known/` proxy bypass tightened to an explicit-allowlist
    `Set`. Stale `/api/auth/codex/callback` PUBLIC_PATHS entry dropped.
  - HSTS gains `preload`. Withings host removed from the global CSP
    and gated to the integration's own routes.
  - `getClientIp` warns once per process on `TRUST_PROXY_HOPS`/XFF
    mismatch + exposes a tagged helper for tighter universal buckets.
  - Service worker rejects off-origin notification-click URLs. Offline
    fallback body switched to a locale-neutral icon. Manifest gains
    `scope`, `id`, `display_override`.

UX + accessibility
  - i18n leaks closed on Dialog/Sheet close-X, ChartSkeleton's loading
    announcement, and the notifications-section breadcrumb.
  - Focus rings on bottom-nav, the More overflow button, and the
    mobile top-bar user menu.
  - Tap-target floor: Input / NativeSelect / Select / DateTimeInput
    switch to `h-11 sm:h-10`.
  - Admin chip-strip auto-scroll-into-view (matches v1.4.33 settings).
  - List-page subtitles visible on mobile.
  - Insights empty-state CTA vocabulary normalised so the link opens
    the right measurement form (was silently failing for
    BODY_TEMPERATURE / BLOOD_GLUCOSE / PULSE).

Mobile-deep
  - `viewport.themeColor` reacts to light/dark mode.
  - TopBar + Coach FAB reserve safe-area-inset-top / -bottom for
    notched iPhones.
  - Sonner toaster switches to `theme="system"`.

Code quality
  - `SESSION_SECRET` dropped — the env var was documented as required
    but never read by code. Three secrets, not four. Self-hosters
    upgrading can delete the existing line.
  - `toJson<T>()` helper consolidates seven Prisma JSON-cast sites.
  - `db-compat.ts` gains a header docstring naming it the
    schema-bootstrap path.
  - Passkey response types adopted from `@simplewebauthn/server`;
    `any` + eslint-disable pragmas drop out.
  - Zero-TODO CI gate added at `.github/workflows/no-todo-markers.yml`.

Documentation + discoverability
  - README rework: How-it-compares matrix, Apple Health + AI Coach
    promoted to standalone Key-Features bullets, status block, latest
    release + GHCR badges, OpenAPI pointer, three-secret Quick Start.
  - openGraph + twitter cards now carry `images` and a
    `summary_large_image` Twitter card.
  - GitHub repo metadata: description tightened from 290 to 156
    chars + keyword-front, `homepageUrl` flipped from a personal
    tenant to the demo site, six generic / PII-adjacent topics
    swapped for six high-intent ones.
  - Five new user-facing docs: `docs/self-hosting/getting-started.md`,
    `docs/self-hosting/reverse-proxy.md`,
    `docs/integrations/apple-health.md`,
    `docs/integrations/withings.md`,
    `docs/integrations/ai-providers.md`.
  - `docs/README.md` clarifies the docs tree as internal playbooks
    vs the user-facing `docs.healthlog.dev` site.
2026-05-17 00:39:58 +02:00