From bb79d289110caf52c2d371dc3e0872ef0f0b40ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 24 Jul 2026 23:18:26 +0200 Subject: [PATCH] =?UTF-8?q?chore(release):=20v1.32.25=20=E2=80=94=20surfac?= =?UTF-8?q?e=20mirrored-medication=20provenance=20and=20cap=20external=20m?= =?UTF-8?q?irror=20growth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 17 +++++++++++++++++ docs/api/openapi.yaml | 30 +++++++++++++++++++++++------- package.json | 2 +- public/sw.js | 2 +- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b42cf3fd..e5c8f92b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [1.32.25] — 2026-07-24 + +Mirrored medications are now marked as such, and the server limits how many +medications one external source can mirror so a client bug can no longer fill +the list with duplicates. + +- **Mirrored medications are marked on the list.** The medications list now + reports whether each entry was mirrored from an external source (today Apple + Health) or created in HealthLog directly. A synced row is no longer + indistinguishable from one you added yourself. +- **One external source can no longer flood the list.** The server refuses to + mirror more than 30 medications from a single external source per account. A + client that keeps re-sending the same medication under a changing identifier + can no longer create a fresh row on every sync. A repeat of a medication + already mirrored still resolves to the existing row, and medications you add + yourself are never affected. + ## [1.32.24] — 2026-07-24 Polar sync is more resilient when one data type is unavailable. A single diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 0f7ef5f94..37001e20a 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: HealthLog API - version: 1.32.24 + version: 1.32.25 description: >- Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest. @@ -3526,7 +3526,12 @@ paths: summary: Create a medication with at least one schedule description: Validates the body against `CreateMedicationRequest`, applies the v1.5 cross-field invariants (one-shot consistency, recurring default `FREQ=DAILY`, `timesOfDay` dual-write), and creates the medication + its - schedules in a single Prisma write. Audits as `medication.create`. + schedules in a single Prisma write. Audits as `medication.create`. v1.28 — a mirror create (`externalSource` + + `externalId`) re-posting a pair the caller already holds returns the existing medication with 200 instead of a + duplicate. v1.32.25 — a NEW mirror create is refused with 422 (`meta.errorCode = + medications.mirror.limit_exceeded`) once the caller already holds 30 medications mirrored from the same external + source; an idempotent re-post of a medication already mirrored is unaffected, and native creates are never + gated. requestBody: required: true content: @@ -19794,6 +19799,15 @@ components: $ref: "#/components/schemas/MedicationScheduleOutput" category: $ref: "#/components/schemas/MedicationCategory" + externalSource: + anyOf: + - type: string + const: APPLE_HEALTH + - type: "null" + description: v1.32.25 — provenance of the medication. `APPLE_HEALTH` marks the row a read-only mirror of the iOS 26+ + HealthKit Medications sync; NULL is a native HealthLog medication. Read-only echo of the stored provenance — + the create route sets it, the list read surfaces it so mirrored-vs-native is visible without inspecting the + iOS client's local registry. lastTakenAt: anyOf: - type: string @@ -19851,16 +19865,18 @@ components: - updatedAt - schedules - category + - externalSource - lastTakenAt - todayEventCount - stockUnitsRemaining - stockDosesRemaining additionalProperties: false - description: List-row variant of the medication resource enriched with the joined `category`, `lastTakenAt`, - `todayEventCount`, and the v1.16.10 aggregated stock fields (`stockUnitsRemaining`, `stockDosesRemaining`) the - dashboard + iOS client consume. The base medication fields (`id`, `name`, `dose`, `treatmentClass`, - `dosesPerUnit`, `active`, `notificationsEnabled`, `pausedAt`, `snoozedUntil`, `startsOn`, `endsOn`, `oneShot`, - `createdAt`, `updatedAt`, `schedules`) are inlined; see the `Medication` component for their semantics. + description: List-row variant of the medication resource enriched with the joined `category`, the v1.32.25 + `externalSource` provenance echo, `lastTakenAt`, `todayEventCount`, and the v1.16.10 aggregated stock fields + (`stockUnitsRemaining`, `stockDosesRemaining`) the dashboard + iOS client consume. The base medication fields + (`id`, `name`, `dose`, `treatmentClass`, `dosesPerUnit`, `active`, `notificationsEnabled`, `pausedAt`, + `snoozedUntil`, `startsOn`, `endsOn`, `oneShot`, `createdAt`, `updatedAt`, `schedules`) are inlined; see the + `Medication` component for their semantics. MedicationCategory: type: string enum: diff --git a/package.json b/package.json index 1ef486bb1..42b589920 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "healthlog", - "version": "1.32.24", + "version": "1.32.25", "description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.", "license": "PolyForm-Noncommercial-1.0.0", "homepage": "https://healthlog.dev", diff --git a/public/sw.js b/public/sw.js index 0ad558d70..c27c3ffc0 100644 --- a/public/sw.js +++ b/public/sw.js @@ -36,7 +36,7 @@ try { // v1.4.38.4 → v1.4.42. Do not hand-edit; bump `package.json` and rebuild. const CACHE_VERSION = (typeof self !== "undefined" && self.__APP_VERSION__) || - /* @sw-version-fallback */ "v1.32.24"; + /* @sw-version-fallback */ "v1.32.25"; const STATIC_CACHE = `healthlog-static-${CACHE_VERSION}`; const PAGE_CACHE = `healthlog-pages-${CACHE_VERSION}`; // v1.18.6 — read-only data cache for a curated allowlist of safe GET `/api/*`