# HealthLog v1.4.25 — release summary ## Release brief v1.4.25 is the largest feature delta in the v1.4.x line and the first time the loud / polish / foundation three-beat ran through a multi-cluster expansion on top of the v1.4.20-introduced develop-to-main release-merge model. Insights expands from a single mother page into seven dedicated metric sub-pages with a shared tab strip; GLP-1 medication tracking lands end-to-end across the dashboard tile, the weight-chart marker overlay, the therapy timeline, the plateau-detection rule in the daily briefing, the dedicated doctor-report section, a Research-Mode drug-level chart behind an MDR acknowledgment dialog, an EMA-sourced drug knowledge layer, the EMA titration ladder, cadence visualisation, compliance chips, a 21-entry by 5-category side-effect taxonomy, and a pen-and-vial inventory with a 30-day in-use clock; the cross-source priority resolver becomes a two-axis metric × deviceType lookup with a per-user Settings surface; per-user timezone threads through ten analytics and presentation surfaces; Withings coverage roughly doubles with twelve new measurement types, webhook-driven BP / temperature ingestion, plus the new Activity sync (steps + distance + active-energy) and Sleep v2 sync (stage-level segments); the onboarding flow rebuilds as a nested-route wizard with a welcome carousel, goals chip-picker, source selection grid, baseline form, and a welcome-back resume banner; Personal Records ship end-to-end with a pg-boss detection worker, push opt-in, and a metric-trend badge; Health Score gains a per-component provenance accordion; the Coach runs on native first-party prompts across all six locales bound by a 1800-assertion refusal-probe matrix on every push; the OpenAPI drift gate flips to hard-fail so the v1.5 iOS DTO codegen can rely on `docs/api/openapi.yaml` being authoritative; and the GHCR image is now multi-arch so Apple Silicon Macs and arm64 clouds pull native. Migrations 0043–0060 are additive and forward-only. `PROMPT_VERSION` 4.24.0 → 4.25.0 carries GROUND RULE 9 (Coach refuses GLP-1 dose recommendations) and GROUND RULE 15 (Coach refuses drug-level estimates with explicit EU MDR 2017/745 + MDCG 2021-24 cites in the refusal copy). ## Live state | Field | Value | | ------------------ | ------------------------------------------------------------------------------- | | URL | `https://healthlog.bombeck.io` | | `/api/version` | `1.4.25` | | GH release | https://github.com/MBombeck/HealthLog/releases/tag/v1.4.25 | | Branch model | `develop` → `main` release-merge model, sixth tag through it (v1.4.20 onwards) | | Tag commit | `17b8d8d2 chore(release): v1.4.25` | | Predecessor | `v1.4.24` at `ca03c609` | ## What shipped ### Insights expansion — seven dedicated metric sub-pages `/insights` becomes a navigation hub. `/insights/blutdruck`, `/insights/gewicht`, `/insights/puls`, `/insights/stimmung`, `/insights/medikamente`, `/insights/bmi` and `/insights/schlaf` each render the metric's full chart, range bands, trend annotations and correlation rows beneath the mother-page hero. Tab strip lifts above each chart for cross-metric scanning. Targets (`/insights/zielwerte`) redesigns around a new `` primitive with a conditional Coach-handoff card that hides cleanly in rule-based mode. Sleep sub-page renders awake / REM / core / deep as a stacked column over the last 7 / 14 / 30 nights, sourced from `Measurement.sleepStage`. ### GLP-1 medication tracking — end-to-end across ten surfaces `Medication.treatmentClass` enum (`GLP1`, `STANDARD`), `MedicationDoseChange` history table, an `InjectionSite` enum with eight site values, and a body-map picker that proposes the next rotation site from the last fourteen days. Dashboard tile carries next-injection schedule plus week-over-week weight delta. Weight chart gains vertical injection markers. Therapy timeline plots titration alongside the weight trace. Plateau-detection in `/api/insights/briefing` flags four-week stalls with referral framing. Doctor-report PDF gains a dedicated GLP-1 section. Drug-level chart on the medication detail page renders behind a Research-Mode gate; the acknowledgment dialog cites EU MDR 2017/745 + MDCG 2021-24, EMA EPAR and Schneck / Urva 2024 before the user can opt in. EMA titration ladder helpers walk the sourced schedule and frame as reference-not-advice. Cadence + compliance helpers expand the schedule into expected slots, pair each with the closest actual intake, and render a timeline + chip strip; honour per-user timezone so cross-midnight doses bucket correctly regardless of locale. Side-effect taxonomy (Migration 0059) covers 21 entries × 5 categories, severity Likert 1-5, with `category` derived server-side so the taxonomy cannot drift between client and server. Pen-and-vial inventory (Migration 0056) carries a SEALED → IN_USE → EXPIRED state machine with a 30-day in-use clock from `markAsFirstUseAt`; a daily 03:00 cron flips expired in-use items in one `updateMany`. State machine re-runs on every PATCH so a back-dated first-use immediately moves a stale pen to EXPIRED. ### Cross-source priority — two-axis architecture `User.sourcePriorityJson` (Migration 0048) stores the per-user resolver: a single-axis ladder by metric type plus an optional per-device-type override. `pickCanonicalSource()` walks the metric axis first; a per-device override (e.g. "BP from Withings BPM Connect, weight from Withings Body+") wins within that ladder. Defaults: cumulative + sleep + HRV + RHR favour Apple Health → Withings → manual; point measurements favour Withings → Apple Health → manual. The `__default__` sentinel is retired in favour of a null bucket. Settings → Sources screen exposes the ladder for drag-reorder per metric and per-device override picker. ### Per-user timezone — Option B threaded through ten surfaces New `User.timezone` column defaults to `Europe/Berlin`. CSV exporter emits ISO-8601 with offset; formatters honour the user's tz; Profile picker covers all IANA zones; admin sets the default for new accounts; signup detects the browser tz; doctor-report PDF dates use the user's tz; chart x-axes take a `timezone` prop; Coach snapshot timestamps land in the user's tz; `MoodEntry.tz` records local-day grouping for weekday correlation (Migration 0044); the weight-weekday correlator buckets in the user's tz. Cadence + compliance helpers honour the same argument. ### Withings — expanded coverage + Activity + Sleep v2 Twelve new `MeasurementType` values (HRV, body temperature, SpO2 v2, VO2 max, fat-free mass, fat mass, muscle mass, skin temperature, pulse-wave velocity, vascular age, visceral fat — Migration 0049). Webhook coverage extended to BP and temperature so the latency collapses from ~1 h polling to seconds. OAuth scope upgraded to include `user.activity` with an in-app reconnect banner. Activity sync calls `getactivity`, ingests one row per day, anchors at noon UTC so positive-offset users bucket cleanly into their local day. Sleep v2 sync calls `sleepv2_get` and writes per-night stage segments through the new `sleepStage` composite (Migration 0055 widens the Measurement unique index to include `sleep_stage` with `NULLS NOT DISTINCT`). Backed by pg-boss `activity-sync` and `sleep-v2-sync` queues plus webhook enqueue hooks on the new subscription channels. ### Onboarding rebuild — six-step nested-route wizard `/onboarding/[step]` powered by a new `` primitive: value-prop carousel, goals chip-picker, source 4-card selection grid (Apple Health marked coming-soon), baseline form, source-connect step, done step. `User.onboardingStep` (Migration 0057, with Migration 0060 backfilling and flipping the column to `NOT NULL DEFAULT 0`) drives resume-state. A welcome-back banner surfaces on the entry page when an in-progress wizard is detected. `POST /api/onboarding/step` conditions the update on `{ id, onboardingStep: current, onboardingCompletedAt: null }` and returns 409 on concurrent advance to close the read-then-write race. ### Personal Records — schema, worker, badge, push opt-in Migration 0054 introduces the `PersonalRecord` table + `PersonalRecordDirection` helper. `GET /api/personal-records` clamps `?limit` (default twenty-five, max two hundred). A pg-boss worker sweeps MAX / MIN per metric and the workout slots on every batch-ingest plus a thirty-minute fallback cron (concurrency five; warmup gate so the very first datapoint per metric does not promote itself). Metric-trend tiles render a PR badge when the record landed in the last thirty days, with WCAG-AA contrast in dark mode. A per-user push opt-in toggle (default off) wires into the existing dispatcher cascade. ### Workouts — schema + typed batch-ingest endpoint Migration 0053 introduces `Workout` + `WorkoutRoute` tables; the Zod boundary in `src/lib/validations/workout.ts` covers a twenty-member sport-type union and a `GeoJSON LineString` route column in JSONB. `POST /api/workouts/batch` accepts up to five hundred rows per call, wraps `withIdempotency()`, returns the same `inserted | duplicate | skipped` envelope as the measurements batch, and reconciles inserted vs duplicate counts correctly under contention. Rate-limited, size-capped. ### Coach — native first-party prompts across six locales Coach + insights system prompts move from EN body + `REPLY LANGUAGE` footer to native per-locale bodies. The safety contract matrix lives as YAML per locale (single source of truth across drafting + tests). GROUND RULE 9 forbids GLP-1 dose recommendations; GROUND RULE 15 forbids drug-level estimates and cites EU MDR 2017/745 + MDCG 2021-24 in the refusal copy. The refusal-probe matrix in CI exercises 15 GROUND RULES across six locales with 20+ adversarial paraphrasings each — 1800+ assertions on every push. Drift-guard tests pin the YAML matrix in lockstep with the validator-derived enums. ### OpenAPI drift gate — hard-fail flip `pnpm openapi:check` CI step now red-bars a PR on any drift between the Zod registry and `docs/api/openapi.yaml`. The v1.4.23 warn-only window closes; iOS DTO codegen can rely on the spec being authoritative. ### Multi-arch GHCR image + Coolify auto-deploy toggle GHCR publish workflow builds `linux/amd64` on `ubuntu-latest` plus `linux/arm64` on `ubuntu-24.04-arm` and merges the manifest. Apple Silicon Macs and arm64 clouds pull native. The Coolify auto-deploy gate is now an explicit `vars.COOLIFY_AUTO_DEPLOY=true|false` maintainer toggle in the deploy workflow instead of the implicit secret-presence gate that silently no-op'd in v1.4.21-23. ### Security cluster `WITHINGS_WEBHOOK_SECRET` no longer lands as a URL path-segment in `http.path` on every Wide Event. The logging stack now carries a parameterised `PATH_SECRET_PATHS` registry seeded with the `/api/withings/webhook/[token]` shape; `WideEventBuilder.setHttp` rewrites path + route segments to `[REDACTED]` before they reach stdout / the in-memory ring buffer / Loki. Existing query-string redaction is unaffected. `POST /api/medications/[id]/glp1` now parses through bounded Zod schemas, enforces length caps on notes, finite-number guards on dose value, bounded `effectiveFrom`, writes an `auditLog` row on every change and inherits the 30-per-minute-per- user rate limit from the rest of the medication surface. Batch-ingest endpoints (`POST /api/measurements/batch`, `POST /api/workouts/batch`, `DELETE /api/measurements/by-external-ids`) carry a sixty-per-minute-per-user token-bucket rate limit. Source- priority and doctor-report-prefs PUTs emit `AuditLog` rows. ## Migrations | Migration | Purpose | |---|---| | 0051 `measurement_device_type` | Adds `deviceType` enum + column to `Measurement` for the two-axis source-priority resolver. | | 0052 `apple_health_enum_extensions` | Adds `ENVIRONMENT_AUDIO_EXPOSURE`, `HEADPHONE_AUDIO_EXPOSURE` and `TIME_IN_DAYLIGHT` to `MeasurementType`. | | 0053 `workout_and_route` | Introduces `Workout` and `WorkoutRoute` tables for the typed workout-batch ingest endpoint and the v1.5 iOS workout sync. | | 0054 `personal_record` | `PersonalRecord` table + `PersonalRecordDirection` helper. Unique index is `NULLS DISTINCT` by convention; the detection worker carries the application-level idempotency guard. | | 0055 `measurement_sleepstage_composite` | Widens the `Measurement` unique index to include `sleep_stage` with `NULLS NOT DISTINCT` (Postgres 16 native) so Withings sleep v2 segments dedup correctly. | | 0056 `medication_inventory_item` | `MedicationInventoryItem` table for the GLP-1 pen/vial inventory with the SEALED → IN_USE → EXPIRED state machine. | | 0057 `user_onboarding_step` | Adds `onboardingStep` nullable to `User` for the wizard resume-state. Originally nullable; superseded by Migration 0060. | | 0058 `user_research_mode` | Adds `researchModeAck*` columns to `User` for the GLP-1 Research-Mode MDR acknowledgment. | | 0059 `medication_side_effect` | `MedicationSideEffect` table for the 21-entry × 5-category taxonomy. | | 0060 `onboarding_step_not_null` | Backfills existing `User.onboardingStep` rows to 0 and flips the column to `NOT NULL DEFAULT 0`. Closes the tri-state ambiguity left by 0057. | All ten are additive and forward-only. Migration 0057's comment was rewritten to match PG11+ fast-path `ADD COLUMN DEFAULT` semantics during the reconcile pass. ## Test coverage delta | Suite | v1.4.24 | v1.4.25 | Delta | |---|---|---|---| | Unit | 2 244 | 3 828 (across 344 files) | +1 584 | | Integration | 140 | ~170 (across 11 files) | +30 | The unit-test growth is dominated by the Coach refusal-probe matrix (1800+ assertions across 15 GROUND RULES × 6 locales × 20+ paraphrasings) plus the feature-cluster helper suites: GLP-1 plateau-detection text formatter; two-axis source-priority resolution; source-priority Settings reducer; medication-card GLP-1 variant + injection-site-picker; doctor-report prefs PUT contract; personal-records pagination clamp; `berlinIsoWeekday` timezone-aware suite; sleep-stage chart i18n drift-guard; Health-Score provenance i18n drift-guard; api-handler private-field crash regression; pure cadence + compliance helpers with TZ assertions across UTC+0, UTC+9, UTC-8; side-effect taxonomy drift-guard; EMA titration ladder helpers; pen-inventory state-machine + 30-day clock helpers; PR detection worker (MAX / MIN per metric + workout slots + warmup gate + null-slot dup regression + zero-duration guard); Withings activity TZ regression across Berlin / Los Angeles / Tokyo / Auckland; Withings sleep v2 segment ingest; log-redaction path-segment rule + `setHttp` rewrite; GLP-1 endpoint Zod + audit + rate-limit; onboarding step concurrent-advance regression; inventory PATCH state-machine back-dated first-use regression; bulk-update contract for the expire-stale cron; brand-name guard + sentinel-preservation + safety-contract parity for the YAML matrix; locale auto-discovery + fallback-chain runtime guard. Integration: two-axis canonical-source resolution end-to-end; `requireAuth()` narrow-scope on unscoped route; batch-ingest race reconciliation under contention; workout-batch race + size-cap + ingest end-to-end; PR-detection end-to-end; cross-source priority for Withings Activity + Apple Health; Withings sleep-stage composite ingest; per-user timezone end-to-end (Pacific/Auckland). e2e green on the W2 CI fix (coach-prefs URL mock + Pixel-5 selector hardening) plus the Fix-I hot-fix that re-anchored the dashboard insight-card and the mobile x-axis tick locators class-independently. Typecheck clean. Lint baseline carried (no new warnings). OpenAPI drift-check clean against the regenerated spec. ## Code quality gates The W21 review ran eight read-only reviewers in parallel: code, security, design, senior-dev, simplifier, product-lead, dead-code, i18n-runtime. Combined output was 103 findings (2 Critical, 20 High, 41 Medium, 40 Low) plus a strategic product-lead assessment. **Critical (2 / 2 applied)**: sec-C1 (Withings webhook secret leaking into `http.path` on every Wide Event) and product-lead-C1 (CHANGELOG frozen at the initial release-prep surface, omitting the bulk of the later feature work). Both closed before tag — sec-C1 via Fix-J (`PATH_SECRET_PATHS` registry + `setHttp` redaction); product-lead-C1 via the W22 release-redo editorial pass. **High (20 / 20 applied)**: sec-H1 (GLP-1 endpoint missing Zod + audit + rate-limit) closed in Fix-K. Six design-Highs (onboarding tap targets, range-bar palette, PR-badge contrast) closed in Fix-L. Two code-Highs (inventory state machine bypass on PATCH, per-row expire loop) closed in Fix-M. Seven simplifier-Highs (`readError` dup, `templateFill`, `statusLabel` nested ternary, `ResearchModeStatus` shared type, `findDrugIdByBrand`, `assertMedicationOwnership` across 11 routes, `MedicationInventoryState` import) closed in Fix-N. Code-H2 (cadence + compliance ignore per-user timezone) closed in Fix-O. Dead-H4 (research-mode-staleness module never wired) resolved by deletion in Fix-O. One product-lead-H1 (test count footer stale) and product-lead-H2 (Deferred-to-v1.4.26 section lists already-shipped items) closed in W22. **Medium (17 of 41 applied, 24 deferred to v1.4.26)**: 17 ship-now Mediums landed across Fix-J through Fix-O; the deferred 24 split between schema-shape decisions (`User.onboardingGoals` column; `/api/audit-log` consumer decision), module-surface narrowing (`glp1-pk.ts`, `glp1-knowledge.ts`, `scheduling/*` unused exports), 148 remaining dead i18n keys after the W15 380-key sweep, and seven orphan endpoint go / no-go decisions that need maintainer call. Full deferrals matrix lives in `.planning/phase-W21-reconcile-plan.md` lines 220-265. **Low (40 / 40 deferred to v1.4.27)**: appendix in the reconcile plan covers them — none are user-facing; one (i18n-Low-1, e2e `healthlog_locale` vs `healthlog-locale` cookie typo) was opportunistically picked up in Fix-P because the touch was a single character. Seven touch-disjoint Fix-J through Fix-P surfaces totalling 22 atomic commits ran across `develop` between the W21 audit and the W22 release-prep. Three sequenced merge windows preserved touch-disjoint diffs without conflict (Fix-O before Fix-N on the cadence route; Fix-L before Fix-N on the onboarding components; Fix-M before Fix-O on the PR worker test). No file had two agents editing the same lines. ## Strategic posture ### v1.5 iOS readiness The iOS sprint has the contracts it needs to start Day 1. Locked server contracts after v1.4.25: - `POST /api/measurements/batch` — Apple Health bulk ingest with `inserted | duplicate | skipped` per-entry envelope, idempotency wrapper, rate-limited (v1.4.23, unchanged here). - `DELETE /api/measurements/by-external-ids` — HealthKit deletion-sync (new in v1.4.25). - `POST /api/workouts/batch` — HKWorkout-aligned typed ingest with 20-sport-type union + GeoJSON LineString routes (new in v1.4.25). - `POST /api/devices` — APNs token registration with paired `apnsEnvironment` (v1.4.23, unchanged). - `POST /api/onboarding/step` — wizard progression with rate limit + audit + 409-on-concurrent (new in v1.4.25). - `GET /api/personal-records?metricType=X` — paginated PR reads. - `User.sourcePriorityJson` two-axis resolver with `pickCanonicalSource()` — iOS sets Apple Health as the cumulative + sleep + HRV + RHR favourite; Withings stays the point-measurement default. - `Measurement.deviceType` enum with iOS-17/18 HK long-tail mappings ready to extend. - `MedicationInventoryItem` — pen/vial 30-day clock available for an iOS pen-scanner integration. - `Workout` + `WorkoutRoute` tables (the table-side; endpoint signature finalised during the iOS sprint). - `docs/api/openapi.yaml` regenerates byte-identically from the Zod registry; the drift gate is now hard-fail. ### MDR boundary preservation The GLP-1 Research-Mode surface ships with three concentric defences: chart gating (`User.researchModeAck*` columns require an active acknowledgment before render), dialog acknowledgment (cites EU MDR 2017/745, MDCG 2021-24, EMA EPAR and Schneck / Urva 2024 before the user can opt in; version-bump re-prompts), and Coach refusal at the prompt layer (GROUND RULE 15 across all six locales, refusal-probe matrix in CI). The titration ladder display is framed as reference-not-advice and bound by the same GROUND RULE 15. Drug-level chart-side 90-day staleness wiring stayed deferred for v1.4.26 (the helper module shipped tested in W19c-Safety but the chart conditional isn't wired — the unused helper was deleted during Fix-O after the reconcile pass; the 90-day gate becomes a clean v1.4.26 surface when the maintainer decides to wire it). ### Branch model `develop` is the daily target; `main` is release-only and follows tags. v1.4.25 is the sixth tag through the develop-to-main release-merge model introduced in v1.4.20 F1. GHCR auto-deploy fires only on `main`; the explicit `vars.COOLIFY_AUTO_DEPLOY=true|false` toggle closes the silent-miss pattern that hit v1.4.21 / v1.4.22 / v1.4.23. ## 0-10 score per area | Area | Score | Note | |---|---|---| | Insights | 9 | Seven-sub-page split lands cleanly; Targets redesign with conditional Coach-handoff is the tightest the layout has been; mother page slims to general status + Coach hero. Half-point off for the duplicate `StatusCard` regression that needed a Fix-I hotfix mid-release. | | Dashboard | 9 | Top-tile polish (single-line headings, inline trend arrow, baseline alignment across tiles) closes the v1.4.24 reviewer note; comparison-overlay default removed in favour of per-chart preference; VO2 max secondary-metric tile lands as opt-in. | | Coach | 9 | Native per-locale prompts replace the REPLY LANGUAGE footer across six locales; GROUND RULE 9 + GROUND RULE 15 close the MDR boundary at the prompt layer; 1800+ refusal-probe assertions in CI on every push. Half-point off for FR/ES/IT/PL prose still being LLM-drafted (structural matrix covers safety; the prose itself benefits from a native human pass — queued for v1.4.26). | | Settings | 8 | Source-priority screen ships with drag-reorder + per-device override + audit-log; doctor-report per-section toggles + mood-default-off lands; Research-Mode toggle with version re-prompt banner lands. Single point off for the settings-cog vs per-message-controls debate (v1.4.23 carryover) still unresolved. | | Admin | 8 | Login overview gains location + provider columns + CSV polish; coach-feedback header layout shift closed; notification-status-card icon parity restored; mobile admin nav gains Overview link. Two points off for the seven orphan endpoint go / no-go decisions still pending a maintainer call. | | Mobile (web) | 8 | 44-px touch-target floor across onboarding wizard, top-bar, section strips and injection-site picker hits WCAG 2.5.5; dashboard tile baseline alignment regression-pinned. Two points off because the e2e workflow needed the Fix-I hot-fix re-anchor on dashboard insight-text + chart-tick locators (now class-independent). | | Tests | 9 | 2 244 → 3 828 unit (+1 584); 140 → ~170 integration; new drift-guards across Apple Health mapping, side-effect taxonomy triangle, sleep-stage chart i18n, Health-Score provenance, safety-contract YAML matrix, plus per-user TZ regressions and the 1800-assertion refusal-probe matrix. Half-point off for the two pre-existing `coach-prefs.test.ts` `NextRequest` URL mock failures still carried from v1.4.23. | | CI | 9 | OpenAPI drift gate flips to hard-fail; refusal-probe matrix runs on every push; multi-arch GHCR builds clean after the Fix-G/H lowercase fix-up; explicit `vars.COOLIFY_AUTO_DEPLOY` toggle closes the silent-miss pattern. Half-point off for the Fix-H lowercase regression that needed catching post-PR-open. | | Docs | 9 | CHANGELOG expand in W22 brings the public artifact in lockstep with what shipped; per-section delta is complete; deferred-to-v1.4.26 / v1.5 sections are accurate. Half-point off because the W22 reconcile was the C-1 Critical of the release and only ran after the W21 product-lead caught it. | | Repo hygiene | 9 | 380 dead i18n keys dropped; dead prompt constants removed; `` + `` + `` + orphan query keys + `/api/insights/general-status` orphan all gone; `MedicationDetailSection` wrapper + shared route-guards / read-error / dose-string / research-mode-types modules collapse 11+ duplications. Half-point off for the 148 remaining dead i18n keys queued for a v1.4.26 second-pass. | | Demo readiness | 8 | Multi-arch image is live so the demo VPS pulls native arm64; W11b demo-deploy report covers the runbook; OpenAPI spec is authoritative for iOS DTO codegen. Two points off because the demo seeding still depends on the manual host-side fallback when Coolify auto-deploy is toggled off, and because the iOS Swift app itself is queued for v1.5 (the demo is web-only until then). | ## v1.4.26 backlog 24 deferred Mediums + 40 Lows + maintainer-decision items split across the following themes: - `User.onboardingGoals` schema column + server-side persistence (today the goals chip-picker holds the choice client-side only; the dashboard-widgets seed feature reads from it in v1.4.26). - `advance()` hook extraction across the three onboarding step components (simplifier surfaced; deferred to avoid collision with the v1.4.25 touch-target pass). - 148 dead i18n keys remaining after the W15 380-key sweep (need a second-pass call-site verification). - Drug-level chart-side 90-day staleness clock wiring (acknowledgment dialog already re-prompts on version bump and Coach refuses at GROUND RULE 15; the clock is defence-in-depth). - FR / ES / IT / PL Coach + insights prose hand-review by native speakers (structural refusal-probe matrix covers safety; the user- facing prose benefits from a human pass). - Sleep sub-page stacked-column visual polish (stage colours + legend density). - VO2 max chart-row card on `/insights/` (dashboard tile shipped; chart card queued alongside the iOS body-composition page). - Mood verbal-labels persistence behind a per-user toggle. - Coach `lastYear` baseline + row-tap-to-prefill polish. - Lazy-loaded locale JSON bundles (all six locales import synchronously today, ~675 KB to every client). - iOS-18 long-tail HK identifier mappings (sleep apnea, GAD-7 / PHQ-9, running form, paddle / row / ski distances, pregnancy / cycle, FHIR clinical). - Seven orphan endpoint go / no-go decisions (`/api/admin/ai-settings`, `/api/admin/backup/test`, `/api/admin/status-overview`, `/api/monitoring/{glitchtip,umami}/test`). - `glp1-pk.ts` and `glp1-knowledge.ts` unused-export decisions (internalise vs wire dashboard chip). - Pearson incomplete-beta replacement (v1.4.23 carryover already partially closed with the conservative MIN_PAIRED_N raise — the rigorous replacement is the v1.4.26 candidate before correlation auto-discovery widens in v1.5/v1.6). ## Carry-overs - The two pre-existing `coach-prefs.test.ts` `NextRequest` URL mock failures still carry through from v1.4.23. Tracked in `.planning/v1426-backlog.md`; non-blocking for the release. - Manual-workout route `findFirst` serial pattern stays in place (no manual workout UI today; contract correctness only matters once v1.5 UI lands). - The Coolify auto-deploy MCP API path still fires `force=true` and reports "finished" in ~18 seconds without a real `docker pull`, but the new `vars.COOLIFY_AUTO_DEPLOY` toggle plus the "Watch image registry for new digests" UI checkbox capture the durable fix; the host-side retag fallback recipe lives in `.planning/coolify-auto-deploy-howto.md` for the cases where the toggle is off. ## Strategic next The v1.5 sprint starts with iOS first launch (login + dashboard + widget) against locked server contracts — pure Swift work, zero server-side touches. P2 (Apple Health sync) calls the v1.4.23 `POST /api/measurements/batch` against the v1.4.25 device-type + two-axis resolver. P3 (Coach extended for HRV / sleep / resting HR / steps) ratchets `PROMPT_VERSION` 4.25.0 → 5.0.0 and copies the web `` UX to iOS. P4 (per-metric APNs alerts) leans on the v1.4.23 APNs scaffolding for the send-side. P5 (workouts + GeoJSON routes) calls the v1.4.25 `POST /api/workouts/batch` against the shipped Workout + WorkoutRoute tables.