github MBombeck/HealthLog v1.4.23
v1.4.23 — pre-iOS prep + hygiene

11 hours ago

v1.4.23 is a foundation release: small user-visible delta on purpose, because the weight is in the contract surface the iOS app gets to compile against on day zero.

Added

  • Apple Health measurement schema + batch-ingest contract. Seven new MeasurementType values (HEART_RATE_VARIABILITY, RESTING_HEART_RATE, ACTIVE_ENERGY_BURNED, FLIGHTS_CLIMBED, WALKING_RUNNING_DISTANCE, VO2_MAX, BODY_TEMPERATURE). APPLE_HEALTH joins MeasurementSource. A new SleepStage enum + nullable Measurement.sleepStage column scoped via CHECK constraint to SLEEP_DURATION rows. Sleep is now persisted in minutes. Composite unique index (user_id, type, source, external_id) is the Apple Health dedup key. Migration 0036 is strictly additive — no row mutations, no rename. New endpoint POST /api/measurements/batch accepts ≤500 entries per call, wraps withIdempotency(), returns per-entry inserted | duplicate | skipped status, and is idempotency-replay safe. Sleep-stage aggregation in /api/analytics rolls multi-stage nights into one Berlin-day datapoint.
  • APNs scaffolding + dispatcher cascade rewire. @parse/node-apn joins the senders cascade (APNs → Telegram → ntfy → Web Push, deterministic order). Provider is lazy-initialised per gateway, JWT auto-rotates. Permanent failures drop the dead Device row mirroring the web-push 410 cleanup. Device model gains nullable apnsToken + apnsEnvironment columns with a paired CHECK constraint plus a partial unique index for defence-in-depth. POST /api/devices accepts the paired fields with a 422 when one comes without the other and a 409 + dedicated audit reason on cross-user re-registration. Production without APNS_KEY_ID is a no-op rather than a boot failure.
  • OpenAPI 3.1 generator + drift CI gate. pnpm openapi:generate emits a byte-stable docs/api/openapi.yaml from Zod .meta() annotations. The eight iOS-touched routes are registered. pnpm openapi:check diffs generated against committed; CI is warn-only for v1.4.23, flips to hard-fail in v1.4.24.
  • Device-management endpoints. GET /api/auth/me/devices lists active devices with label, last-seen, channels, and an isCurrent marker keyed off the session's deviceId (not the forgeable header). DELETE /api/auth/me/devices/[id] revokes one device transactionally. DELETE /api/devices/[id] is the native-friendly mirror the iOS APNs-rotation flow calls.
  • Per-user Coach prefs surface (settings cog returns). User.coachPrefsJson column + GET / PUT /api/auth/me/coach-prefs. The settings cog opens a right-edge sheet for tone, verbosity, focus / exclude metrics. The snapshot pipeline reads prefs before measurement queries so excluded metrics never enter the snapshot.
  • Per-message Coach thumbs feedback + admin aggregate view. Each Coach reply renders a 👍 / 👎 affordance; RecommendationFeedback gains a polymorphic target_type discriminator. New POST /api/insights/chat/messages/:id/feedback. Admin section /admin/coach-feedback renders helpful-rate buckets by (PROMPT_VERSION, tone, verbosity).

Changed

  • Refresh-token reuse-detection scopes to the originating device. Pre-1.4.23 a replayed refresh token revoked every refresh token the user owned; v1.4.23 narrows the blast radius to the device that issued the token. Legacy null-deviceId tokens still fall back to user-wide revoke as a safety hatch.
  • Pearson surfacing gate raised from n≥14 to n≥20. Conservative patch on the low-df p-value path; the rigorous incomplete-beta replacement is queued for v1.4.24.
  • Coach drawer prefill becomes a controlled prop. useResettableValue hook + pure nextResettableValue helper replace the key={prefill} mount-cycle hack.
  • /api/analytics BP-in-target aggregate becomes cursor-paged. Unbounded findMany replaced with fetchBpSeriesChunked (5 000-row chunks); regression test seeds 6 000 rows across a chunk boundary.
  • Coolify webhook contract documented end-to-end. GHCR build → force=true Coolify deploy → /api/version poll → host-side retag fallback if the :latest digest hasn't moved.
  • Coach feedback foreign-key targets coach_messages directly. Plaintext content column on recommendation_feedback is retired.
  • PROMPT_VERSION 4.22.0 → 4.23.0 with a new GROUND RULE 12 (EN + DE): treat Apple Health categories as silent when the snapshot doesn't carry them. Strict schema's sourceMetric and trendAnnotations enums extend to admit nine additive HealthKit categories.
  • medication_schedules.days_of_week column deployed (migration 0039, NULL = daily).

Fixed

  • Admin coach-feedback sidebar entry restored.
  • APNs NotificationChannel auto-upserted on device registration.
  • Partial unique index enforces global apns_token uniqueness as defence-in-depth.
  • Apple Health source badge renders on the mobile measurement-card variant.
  • Coach prefs sheet skeleton + save toast.
  • Device-revoke cascade wraps refresh + access + channels + Device row in one transaction.
  • isCurrent device marker keys off the session's deviceId, not the forgeable X-Device-Id header.
  • Sentinel parser annotates partial-malformed entries instead of collapsing the whole block.

Security

  • APNs send-side defence-in-depth (hex format at registration, cross-user-hijack guard at the token layer, redacted send-side payload logging).
  • Coolify webhook URL scrubbed from tracked files.
  • Coach prose encryption-at-rest restored after the feedback FK migration.

Refactor

  • revokeDeviceCascade(deviceId) helper collapses three inline call sites.
  • useCoachPrefs() hook collapses fetch + mutate + invalidate triplet.
  • buildCoachSnapshot takes a single scope record instead of seven booleans.
  • OpenAPI registry uses static imports for deterministic generator output.

Deferred to v1.4.24

  • Pearson incomplete-beta p-value, OpenAPI drift gate flip to hard-fail, settings-cog vs per-message-controls UX consolidation, coach-prefs.test.ts NextRequest URL mock fix, security MED + LOW cluster.

Deferred to v1.5

  • iOS native client P1 (login + dashboard + widget), Apple Health sync P2, Coach extended for HRV / Sleep / Resting HR / Steps P3, per-metric APNs alerts P4.

Full changelog: https://github.com/MBombeck/HealthLog/blob/main/CHANGELOG.md

Don't miss a new HealthLog release

NewReleases is sending notifications on new releases.