Largest feature delta in the v1.4.x line. Insights expands from one
page into seven dedicated metric routes; GLP-1 medication tracking
lands end-to-end across injection picker, dashboard tile, weight-chart
markers, therapy timeline, plateau detection, drug-level chart with a
Research-Mode acknowledgment gate, EMA-sourced drug knowledge, EMA
titration ladder, cadence visualisation, compliance chips, side-effect
taxonomy, pen-and-vial inventory with a 30-day in-use clock, and a
dedicated doctor-report section; cross-source priority becomes a
two-axis resolver with a per-user Settings surface; per-user timezone
threads through ten analytics and presentation surfaces; Withings
coverage doubles with twelve new measurement types, webhook-driven
BP / temperature ingestion, plus Activity (steps + distance +
active-energy) and Sleep v2 (stage-level segments) syncs; the
onboarding flow is rebuilt as a nested-route wizard with welcome
carousel, goals chip-picker, source selection grid, baseline form,
and a welcome-back resume banner; Personal Records ship end-to-end
with a 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 with a
1800-assertion refusal-probe matrix; the OpenAPI drift gate flips to
hard-fail; the GHCR image is 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 with GROUND RULE 9
(Coach refuses GLP-1 dose recommendations) and GROUND RULE 15 (Coach
refuses drug-level estimates with MDR + MDCG 2021-24 cites).
Added
- Insights sub-pages — seven dedicated metric routes with a shared
tab strip./insights/blutdruck,/insights/gewicht,
/insights/puls,/insights/stimmung,/insights/medikamente,
/insights/bmiand/insights/schlafeach render the metric's
full chart, range bands, trend annotations and correlation rows
beneath the mother-page hero. Tab strip lifts the metric switcher
above each chart for cross-metric scanning; the mother page slims
to general status and the Coach hero. - Sleep sub-page with per-night stacked-bar of sleep stages.
/insights/schlafrenders awake / REM / core / deep as stacked
columns over the last 7 / 14 / 30 nights, sourced from the
sleepStagecolumn onMeasurement. - Targets (
/insights/zielwerte) redesigned with a conditional
Coach-handoff card. New<TargetCard>primitive replaces the
v1.4.22 layout. Page-level consistency strip pins meta context
above the cards. The Coach-handoff card only renders when a
language-model provider is configured; rule-based mode hides it
cleanly. Mobile-first three-column grid; per-card cog for
visibility toggles. - GLP-1 medication tracking — full integration across ten
surfaces. NewMedication.treatmentClassenum (GLP1,
STANDARD);MedicationDoseChangehistory table;InjectionSite
enum with eight site values. A new body-map picker proposes the
next rotation site based on the last fourteen days of injections.
Medication-card grows aglp1variant (text-rich, no inline
chart per directive — chart lives on the dashboard tile and
/insights/medikamentetherapy timeline). Dashboard tile shows
next-injection schedule + week-over-week weight delta. Weight
chart gains vertical injection markers. Therapy timeline on the
insights sub-page plots titration alongside weight trace. Plateau-
detection rule in/api/insights/briefingflags four-week stalls
with referral framing. Doctor-report PDF carries a dedicated GLP-1
section. Migration 0046. - Doctor-report per-section toggles + mood default-off. New
User.doctorReportPrefsJsoncolumn (Migration 0045) +
PUT /api/auth/me/doctor-report-prefs. Each section (mood,
achievements, GLP-1, etc.) gets a per-user toggle in Settings →
Reports. Mood defaults to off (clinical-sensitivity); empty
sections hide rather than render a "no data" stub. - Cross-source priority resolution — two-axis architecture.
NewUser.sourcePriorityJsoncolumn (Migration 0048) stores a
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 for per-user priority configuration.
Drag-and-reorder list per metric, per-device override picker,
reset-to-defaults action. Audit-log entry on every write. - Per-user timezone — Option B threaded through ten surfaces.
NewUser.timezonecolumn (defaults toEurope/Berlin). The
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; the
doctor-report PDF dates use the user's tz; chart x-axes take a
timezoneprop; Coach snapshot timestamps land in the user's
tz;MoodEntry.tzrecords local-day grouping for weekday
correlation (Migration 0044); the weight-weekday correlator
buckets days in the user's tz. - Health Score provenance accordion. Each of the four scoring
components (BP, weight, mood, compliance) gets an inline
disclosure listing the canonical source (Withings / Apple
Health / manual) andasOftimestamp behind the score number.
aria-labelledbypanel pairing for screen readers. - Four additional locales — French, Spanish, Italian, Polish.
First-party translation bundles cover the full string surface
with a<MaintainershipBanner>flagging the locale as
community-maintained and pointing at the translation-feedback
issue template. Coach + insights system prompts ship as native
per-locale bodies (noREPLY LANGUAGEfooter indirection) with
the full safety-contract matrix in YAML per locale + structural
refusal-probe coverage in CI (see Tests). EN + DE stay
Marc-maintained. Locale picker covers all six; signup
browser-detect mapsfr|es|it|plto the corresponding bundle. - Coach — native first-party system prompts across six locales.
Coach + insights system prompts move from EN body +REPLY LANGUAGEfooter to native per-locale bodies; the safety contract
matrix lives as YAML per locale (single source of truth across
drafting + tests). The refusal-probe matrix in CI catches
cross-locale prompt regressions before they reach the dispatch
surface. - VO2 max dashboard trend tile (opt-in). Secondary-metric
pattern — default-invisible, surfaces only when the user has
VO2 max data from Apple Health. - Personal Records end-to-end — schema, detection worker, badge,
push opt-in. Migration 0054 introduces thePersonalRecord
table +PersonalRecordDirectionhelper;GET /api/personal-recordsis paginated (default twenty-five, max two
hundred). A pg-boss worker sweeps MAX / MIN per metric and the
workout slots on every batch-ingest + 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 theWorkout+WorkoutRoutetables; the Zod
boundary insrc/lib/validations/workout.tscovers a
twenty-member sport-type union and aGeoJSON LineStringroute
column in JSONB.POST /api/workouts/batchaccepts up to five
hundred rows per call, wrapswithIdempotency(), returns the
sameinserted | duplicate | skippedenvelope as the
measurements batch, and reconciles inserted vs duplicate counts
correctly under contention. Rate-limited and size-capped. - Onboarding rebuilt as a nested-route wizard. The legacy
v1.4.20 onboarding ships as a six-step wizard at
/onboarding/[step]powered by a new<OnboardingShell>
primitive: welcome carousel with value-prop slides, goals
chip-picker, source selection 4-card grid (with Apple Health
marked coming-soon), baseline form, source-connect step, and a
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/stepadvances the wizard with
rate-limit + audit, returns 409 on a concurrent advance to close
the read-then-write race. The marketing entry-point swaps from
the v1.4.20 single-page flow to the new wizard. Five locales
carry the shell key surface (six total). - GLP-1 — EMA drug knowledge layer. A first-party drug-knowledge
module covers five GLP-1 / GIP-GLP-1 drugs with EPAR-sourced
half-life, recommended titration step, brand-name table, and a
brand-to-id lookup. A drift-guard test pins the values against
the EPAR + Psp 4.13099 references on disk so the module cannot
silently drift. - GLP-1 — Research Mode with estimated drug-level chart, MDR
acknowledgment dialog, and Settings toggle. A pure
one-compartment pharmacokinetic helper produces qualitative
drug-level traces from the user's injection history (steady-state
approximation, observational use only). The chart renders on the
GLP-1 medication detail page behind a Research-Mode gate:
User.researchModeAck*columns (Migration 0058) record the
acknowledgment version + timestamp; a dialog explains the
EU MDR 2017/745 + MDCG 2021-24 context and cites EMA EPAR plus
Schneck / Urva 2024 before the user can opt in.
GET / POST / DELETE /api/auth/me/research-modecarries the
acknowledgment lifecycle. A Settings → Advanced toggle exposes
the opt-in plus a re-prompt banner whenever the acknowledgment
version bumps. - GLP-1 — side-effect logging with a 21-entry × 5-category
taxonomy. Migration 0059 introduces theMedicationSideEffect
table; pure helpers produce the taxonomy + a five-point Likert
severity scale; a section on the GLP-1 detail page lets the user
log entries with category + severity + free-text notes. Category
is derived server-side from the entry (no client-supplied
category) so the taxonomy cannot drift between client and server. - GLP-1 — cadence visualisation + compliance chips. Pure
helpers expand a medication's schedule into expected slots,
pair each slot with the closest actual intake, and produce a
cadence timeline + compliance chip strip on the detail page.
GET /api/medications/[id]/cadencereturns the structured
payload; the helpers honour per-user timezone so cross-midnight
doses bucket correctly regardless of locale. - GLP-1 — EMA titration ladder display. Pure helpers walk the
EMA-sourced titration schedule from the knowledge layer; the
GLP-1 detail page renders the user's current step + remaining
ladder as observational reference, framed as reference-not-advice
and bound by GROUND RULE 15.GET /api/medications/[id]/titration-ladderreturns the structured
ladder + current step. - GLP-1 — pen-and-vial inventory with a 30-day in-use clock.
Migration 0056 introduces theMedicationInventoryItemtable +
a pure state machine (SEALED → IN_USE → EXPIRED) with a 30-day
in-use clock frommarkAsFirstUseAt. An inventory card on the
medication detail page surfaces the active pen, days remaining,
and SEALED stock; intake events decrement the dose count, and a
daily 03:00 cron flips expired in-use items in a single
updateMany. Re-runs the state machine on every PATCH so a
back-dated first-use immediately moves a stale pen to EXPIRED. - Apple Health identifier mapping (server-side). Ports the
identifier table fromk0rventen/apple-health-grafanaand
dogsheep/healthkit-to-sqlitewith MIT and Apache-2.0
attribution recorded in source headers andNOTICE. Covers
the v1.4.25 ingest surface; iOS-18 long-tail mappings (sleep
apnea, GAD-7, paddle/row sports, FHIR clinical) carry inline
release-window comments. - Audio-exposure and time-in-daylight measurement types.
Migration 0052 addsENVIRONMENT_AUDIO_EXPOSURE,
HEADPHONE_AUDIO_EXPOSUREandTIME_IN_DAYLIGHTto the
MeasurementTypeenum. Doctor report mentions them when
populated; no dedicated chart yet. - Withings expanded coverage — twelve new measurement types and
the BP / temperature webhook subscription. Migration 0049
adds HRV, body temperature, SpO2 v2, VO2 max, fat-free mass,
fat mass, muscle mass, skin temperature, pulse-wave velocity,
vascular age and visceral fat asMeasurementTypevalues.
Withings webhook coverage extended to BP and temperature so
the latency goes from ~1 h polling to seconds. OAuth scope
upgraded to includeuser.activityand an in-app banner
prompts existing users to reconnect once. - Withings — Activity sync (steps + distance + active-energy).
Callsgetactivity, ingests one row per day, anchors at noon UTC
so positive-offset users bucket cleanly into their local day.
Backed by a pg-bossactivity-syncqueue plus a webhook enqueue
hook on the new subscription channel. - Withings — Sleep v2 sync (stage-level segments). Calls
sleepv2_getand writes per-night stage segments through the new
sleepStagecomposite (Migration 0055 widens the Measurement
unique index to includesleep_stagewithNULLS NOT DISTINCT).
Backed by a pg-bosssleep-v2-syncqueue plus webhook enqueue.
The sleep sub-page's stacked-bar chart renders the segments
directly. - Withings — webhook subscriptions expanded to activity + sleep
v2. Subscription registration plus webhook delivery routing for
the two new event types. - Admin Login overview — location, provider and CSV polish.
Adds a location column derived from the audit IP; adds a
provider column distinguishing passkey, password, API-token
and OAuth login paths; the CSV export drops two unused columns
and the per-row collapse affordance comes off. - DELETE
/api/measurements/by-external-idsfor iOS deletion
sync. Idempotent batch delete keyed on(user, source, externalId)tuples; rate-limited the same way as the batch
ingest endpoint. - Multi-arch Docker image. GHCR publish workflow builds
linux/amd64onubuntu-latestpluslinux/arm64on
ubuntu-24.04-armand merges the manifest. Apple Silicon
Macs and arm64 clouds now pull native; the previously-stale
README claim is accurate again. - GLP-1 endpoint hardening.
POST /api/medications/[id]/glp1now parses through bounded Zod
schemas (glp1DoseChangePostSchema,glp1InventoryPostSchema)
with length-capped notes, finite-number guards on dose value,
and boundedeffectiveFrom. Every write produces an audit-log
row and the route inherits the 30-per-minute-per-user rate
limit from the rest of the medication surface. - Translation-feedback issue template. GitHub issue template
for community translation corrections, paired with the
maintainership banner copy on FR / ES / IT / PL surfaces. - Repository polish. README hero rewrite (what / who / try
the demo, thirty-second read). Topics expanded 10 → 18.
Branch protection v2 with conversation-resolution. GitHub
Discussions enabled. Issue template + PR template carry-over
from v1.4.20 preserved.
Changed
- Insights mother page slimmed; metric depth moved to sub-pages.
/insightsbecomes a navigation hub with hero strip + Daily
Briefing + correlations + trends row; per-metric depth lives
one click away on the dedicated sub-pages. - Targets page-shell unified with the rest of Insights.
Single consistency strip + Coach handoff + per-card actions
rather than the v1.4.22 sparkline-everywhere layout. - Coach default-window preference plus chip-order rationalised.
The window picker now persists per-user (last seven / thirty /
ninety days); suggested-prompt chips reorder so health-focus
chips lead and meta-discovery chips trail. Composer auto-grows
on multi-line input. Distinct error UX for daily-limit hit vs
provider rate-limit. Microphone affordance retired (was a
placeholder). - Dashboard global comparison-overlay default removed. Per-chart
preference from v1.4.22 is the only knob now; the dashboard-level
default is gone. medication_schedules.*columns mapped to snake_case via
Prisma@map. Migration 0047 — cosmetic rename only,
convention parity with the rest of the schema. Resolves the
v1.4.24 demo-deploy schema-drift finding.- Top-page padding parity across
AuthShell, Settings and Admin
shells. Cross-page rhythm matches; the previous one-off
paddings on Admin and Settings asymmetric to Insights are gone. - Settings icon + heading convention uniform across all twenty-
three sections. Every section header pairs an icon with a
heading; the v1.4.24-era split (icon-only on some, heading-only
on others) is gone. PROMPT_VERSION4.24.0 → 4.25.0 carries two new safety
ground rules across all six locales: GROUND RULE 9 forbids GLP-1
dose recommendations (Coach falls back to a clinical-referral
framing on any dose question); GROUND RULE 15 forbids drug-level
estimates and cites EU MDR 2017/745 + MDCG 2021-24 in the
refusal copy. Coach + insights system prompts ship as native
bodies per locale rather than the legacyREPLY LANGUAGE
footer.- Dashboard top-tile polish. Tile headings collapse to a
single line per locale (BP / BF abbreviations land in EN + DE +
the four Romance locales); the trend arrow moves inline next to
the headline value; the value row baseline-aligns across tiles
regardless of font fallback. Regression guard pins the baseline
contract. - OpenAPI drift gate flips to hard-fail.
pnpm openapi:check
CI step now red-bars a PR on any drift between the Zod registry
anddocs/api/openapi.yaml. The v1.4.23 warn-only window
closes; iOS DTO codegen can rely on the spec being authoritative. - Coolify auto-deploy gate exposed as an explicit
maintainer-toggleable repo variable.vars.COOLIFY_AUTO_DEPLOY
is now atrue | falseswitch in the deploy workflow instead of
the implicit secret-presence gate that silently no-op'd in
v1.4.21-23. - Settings → Sources sentinel cleanup. The
__default__
device-type bucket sentinel is retired in favour of a null
bucket at the storage layer; the Settings UI reads cleaner and
the reorder helpers (reorderLadder) collapse the two
moveSource+moveDeviceTypepaths into one. - Section wrapper for medication detail. A new
<MedicationDetailSection>wraps the three medication-detail
shells (titration, scheduling, side-effects) plus the inventory
disclosure top so cross-section chrome stays consistent and
future sections drop in without copy-paste. - Shared route ownership helper. Nine medication routes
(titration,side-effects,inventory,cadence,intake,
compliance,phase-config,api-endpoint, plus the GLP-1
convenience route) now callassertMedicationOwnershipfrom
src/lib/medications/route-guards.tsrather than open-coding
the lookup; rate-limit headers across the same surface align
on the option-bag formapiError(..., { headers: rateLimitHeaders(rl) }).
Fixed
- Settings save regression on Zod v4 record semantics.
z.record(z.string(), …)switched toz.partialRecord(…)on
the dashboard-prefs schema so optional keys round-trip cleanly
through the PUT. The save-then-blank-load bug from v1.4.24 is
gone. - Comparison-shift baseline regression. Comparison overlay
was subtracting the wrong-period baseline on several charts;
baseline lookups now match the caption window. Three regression
guards. - Raw metric-token leaks in generated prose —
metric:<TYPE>
identifiers surfaced in three surfaces that escaped the v1.4.22
sweep (<RecommendationCard>fallback path, briefing
keyFindinghelper, and Coach in-flight bubble).
stripChartTokens()regex widened to cover lowercase prose
remnants; prompt-side GROUND RULES 8 and 13 reaffirm the
constraint. PROMPT_VERSION 4.23.0 → 4.24.0 carried the prompt
change; 4.25.0 inherits. - Withings BP and temperature webhook latency. Webhook
subscription now covers BP and temperature endpoints, so the
earliest a measurement reaches the user drops from ~1 h to
seconds. - Dev-server crash on Tailwind v4
color-mixparser + Next 16
api-handler private-field. Two unrelated dev-time crashes
surfaced together: Tailwind v4 choked on acolor-mix()
invocation in a chart token; Next 16 + Webpack fallback hit a
private-field access on a force-static route handler.
color-mixinvocation replaced; api-handler guards the field
access. Production unaffected — these surfaced only under the
dev compile path. - Insights duplicate
StatusCard. The mother page rendered
two copies of the BP status card at certain viewports due to
a hero-strip ↔ correlation-row overlap. Card hoisted to the
single canonical location. - Coach-feedback admin header layout shift. Sticky-header
contract mismatch — the section's<header>carried a different
top padding than its siblings, so navigating into the section
visibly shifted the chrome. Padding parity restored across all
admin sections. - Notification-status-card heading icon parity. Was the one
section without an icon next to its heading; aligns with the
cross-section convention now. - WCAG 2.5.5 touch-target floor across the top bar, section
strips and the injection-site picker. Several pill chips and
the injection-picker dots sat below the 44-px floor; all hit
the floor now without changing the visual density at typical
rendering sizes. - Sleep-stage chart strokes resolve through Dracula tokens.
The chart was using the legacyhsl(var(--border))form which
Tailwind v4 rejects; switched to the resolved token. - "Per night" hardcoded German in four locales. The
sleep-page subtitle suffix was hardcodedpro Nachtand
rendered as raw German across FR / ES / IT / PL. Translated
in all six locales; an i18n drift-guard covers the contract. - Cross-page top-padding asymmetry. AuthShell, Settings,
Admin all carry the same top padding now (see Changed). berlinIsoWeekday()timezone hard-code. The weekday helper
hardcoded Europe/Berlin; now takes atimezoneargument and
threads through the weight-weekday correlator.- Batch-ingest race reconciliation under contention. Two
concurrent batches with overlappingexternalIdsets returned
inconsistentinserted/duplicatecounts because the
reconciliation pass was logically inverted (no-op when the
catch fired). Fixed; replay regression test seeds the race. requireAuth()blocks narrow-scope iOS Bearer tokens on
unscoped routes. v1.4.24 closed the inverse hole (Bearer
with no scope reaching unscoped handlers); this release closes
the over-broad version — a token scoped exclusively to
medication:ingestcould not reach handlers that don't
declare arequiredPermission. Now declared-scope tokens are
allowed through provided the route's declared scope (or wildcard)
is in the token's set.createMeasurementSchemadroppeddeviceTypeon single-entry
POST. The batch route accepteddeviceTypeper row; the
single-entry POST stripped it on the Zod boundary. Mirror parity
restored.- Personal-records endpoint unpaginated.
GET /api/personal-recordsnow clamps?limit(default twenty-five,
maximum two hundred); the unbounded read is gone. - Withings activity sync bucketed positive-offset users into the
wrong day. The per-day row was anchored at 23:59:59 UTC, so a
user inPacific/Aucklandsaw activity rows attached to the
following local day. Anchored at noon UTC (T12:00:00.000Z)
with a regression suite covering Berlin / Los Angeles / Tokyo /
Auckland. - Cadence and compliance helpers ignored per-user timezone.
expandScheduleSlots,pairDoses,buildCadenceTimelineand
the compliance-chip helper now accept atimeZoneargument that
threads throughIntl.DateTimeFormat, so cross-midnight doses
bucket correctly regardless of user locale. The cadence route
resolves throughresolveUserTimeZone(user). - Inventory state-machine ignored back-dated first-use. Issuing
a PATCH that pushedmarkAsFirstUseAtmore than thirty days into
the past left the item in IN_USE; the state machine now re-runs
on every PATCH and the stale item flips to EXPIRED. Regression
test pins the contract. - Inventory expire-stale cron looped per row. Replaced the
row-by-rowprisma.updateloop inexpireStaleInUseItemswith a
singleupdateMany; bulk-update contract pinned by test. - Onboarding step write was read-then-update. A concurrent
advance could double-step the wizard. The update now conditions
on{ id, onboardingStep: current, onboardingCompletedAt: null }
and returns 409 on conflict. - Workout schema accepted
endedAt <= startedAt.
createWorkoutSchemagained a.superRefinerejecting
zero-duration and inverted windows; the PR detection worker's
MIN-direction slot also guards againstdurationSec === 0so a
malformed row cannot promote itself to a personal record. - Personal Record detection duplicate writes under contention.
Same-millisecond writes into a null workout slot now reconcile
to a single row; regression test seeds the race. - Source-priority parse failures were silent. Storage parse
errors now route through an observer breadcrumb and the resolved
blob is frozen at construction so downstream mutation is caught
immediately. - Health-Score
asOfderived non-deterministically. The
asOftimestamp now derives from the input dates instead of
the wall clock so repeated computes against the same dataset
produce stable timestamps. - Source-priority audit-log entries missed two write paths.
Doctor-report-prefs and source-priority PUTs were not landing
audit-log rows; both write paths emit now. - Personal-record badge dark-mode contrast. Lifted to WCAG-AA
contrast on the<PersonalRecordBadge>surface. - Range-bar zone backgrounds.
<RangeBar>mixed Tailwind raw
palette tokens with Dracula tokens; all zone backgrounds now
resolve through Dracula tokens with the pinned test rewritten. - Research-Mode acknowledgment dialog footer scrolled below
fold. The footer pins outside the scrolling region on small
viewports. - 44-px touch-target floor across the onboarding wizard.
Shell back/skip/next, carousel pager, source-card grid, baseline
and goals-chip CTAs all hit the WCAG 2.5.5 floor without
changing visual density at typical rendering sizes. - Workout endpoint reconciled inserted-vs-duplicate counts
incorrectly under contention. Race-recovery path produced
inconsistent envelope counts; corrected with a regression test
seeding the contention window. - Legacy Withings
?secret=webhook form had no counter. An
in-memorywithings.webhook.legacy_form_totalcounter surfaces
throughops-stats; the warning text gained the re-subscription
URL for affected accounts. - Acknowledgment dialog i18n back-button. Medication history
page back-button hard-codedZurück; routed throught(). - Sleep + audio-exposure + comparison-hint i18n keys. Backfill
pass picked up the keys missed by the v1.4.22 sweep across all
six locales. - Build resolution for safety-contract YAML. The loader now
resolves the YAML matrix path fromcwdrather than__dirname
so the build does not break when the bundler reshapes the
module-relative root. - GLP-1 drift guard self-skips when the EMA research file is
absent. Local checkouts without the reference file no longer
red-bar the test. x-axischart ticks resolved in user timezone.
xAxisTicksnow renders the tick formatter in
user.displayTimezoneinstead of hard-coding Europe/Berlin.- Chart token references switched to
var(--token)form.
Recharts strokes were still usinghsl(var(--token)); switched
to barevar(--token)so Tailwind v4's color-mix parser
resolves them. detectGlp1Plateaubranches lacked direct coverage. Added
Prisma-mocked unit tests covering the previously-uncovered
branches.
Security
- Withings webhook secret no longer leaks into structured
logs.WITHINGS_WEBHOOK_SECRETwas landing as a URL
path-segment inhttp.pathon every Wide Event the request
produced. The logging stack now carries a parameterised
PATH_SECRET_PATHSregistry seeded with the
/api/withings/webhook/[token]shape, and
WideEventBuilder.setHttprewrites path + route segments to
[REDACTED]before they reach stdout / the in-memory ring
buffer / Loki. Existing query-string redaction is unaffected. - Coach refuses GLP-1 dose recommendations. GROUND RULE 9
across all six locales — the Coach surfaces a clinical-referral
message on any dose adjustment ask, never a number.
PROMPT_VERSION 4.25.0 carries the rule. - Coach refuses drug-level estimates with regulatory cites.
GROUND RULE 15 across all six locales — the Coach refuses any
ask for an estimated drug level or pharmacokinetic prediction,
cites EU MDR 2017/745 + MDCG 2021-24 in the refusal copy, and
routes the user to the Research Mode disclosure flow.
Adversarial refusal-probe matrix exercises 20+ paraphrasings per
GROUND RULE across six locales in CI on every push, so
prompt-injection regressions surface before reaching the
dispatch surface. - Research Mode is gated by an MDR acknowledgment dialog. The
estimated drug-level chart on the GLP-1 detail page renders only
after the user opts in through a dialog that cites
EU MDR 2017/745 + MDCG 2021-24, EMA EPAR and the Schneck / Urva
2024 pharmacokinetic reference. The acknowledgment version
re-prompts the user on bump. - GLP-1 convenience endpoint hardened.
POST /api/medications/[id]/glp1parses through bounded Zod schemas
(glp1DoseChangePostSchema,glp1InventoryPostSchema), enforces
length caps on notes, finite-number guards on dose value, and
boundedeffectiveFrom; every write produces an audit-log row
and the route inherits the 30-per-minute-per-user rate limit
from the rest of the medication surface. - GLP-1 medication strings sanitised before LLM prompt
interpolation. A malicious or malformed medication name no
longer reaches the prompt body verbatim. Sanitiser passes
Latin-letter + digit + common punctuation only. - Batch-ingest rate limit (sixty per minute per user default).
POST /api/measurements/batch,POST /api/workouts/batchand
DELETE /api/measurements/by-external-idscarry a token-bucket
rate limit returning the standard 429 envelope. - Audit-log writes on source-priority and doctor-report-prefs
PUTs. Every change to either preference produces an
AuditLogrow for the user's audit-log surface and the admin
cross-section view. - Withings OAuth
user.activityscope upgrade with explicit
user reconnect. New scope only takes effect after the user
re-authorises; the reconnect banner makes the requirement
visible. - Source-priority storage is per-user. No cross-user lookup
paths; one user's priority blob never enters another user's
resolver.
Refactor / Hygiene
pickCanonicalSourcebecomes a two-axis lookup with single-
axis fallback. ReusesgetDeviceTypeLadder()so the
per-device override can be queried from the canonical-row picker
without duplicating ladder logic.- Health-Score
COMPONENT_ORDERhoisted +Intl.DateTimeFormat
memoised. Provenance accordion calls the formatter once per
render rather than four times. SubPageSlugtype + array derived fromSUB_PAGE_METRIC
record. Single source of truth for the seven sub-page slugs.source-priority__default__sentinel → null bucket.
Settings → Sources stops carrying a fake__default__device-
type entry; the null bucket reads cleaner at the storage layer.apple-health-mappingsleep branch collapsed. Three sleep-
stage cases shared the same body; collapsed to one.HK_QUANTITY_TYPE_TO_MEASUREMENTremoved. The legacy view
was a redundant projection ofAPPLE_HEALTH_TYPE_MAP; all
callers go through the canonical map now.- Dead-code cleanup pass. Drops
<InsightsPageHero>(zero
callers since v1.4.20),<IntakeTimeline>,<ComplianceCharts>
wrapper and three orphaninsightsGeneralStatusquery keys from
the v1.4.16-era hero. Removes the orphan
/api/insights/general-statusroute (superseded by
<InsightAdvisorCard>since v1.4.16). - Coach
<CoachDrawer key={prefill}>controlled-prop refactor
is fully in place — the v1.4.24 follow-up ofuseResettableValue
is now the only call path; the legacy remount hack code is gone. - 380 dead i18n keys dropped. Runtime probe across the six
locales identified 380 keys with zero call sites and zero runtime
resolutions; all six locale bundles are smaller by that count.
Top namespaces:settings,admin,classifications,
medications. The remaining ~148 keys are queued for a second
pass in v1.4.26. - Dead system-prompt constants removed.
BASE_SYSTEM_PROMPT
andINSIGHTS_SYSTEM_PROMPTconstants had no remaining
importers after the native per-locale prompt move; both deleted. useInsightStatushook extracted. The four insights
sub-page status queries collapsed into a single hook so the
duplicateduseQuery+ Suspense fallback shape lives in one
place.<MedicationDetailSection>wrapper. Three medication-detail
shells (titration, scheduling, side-effects) and the inventory
disclosure top share a single wrapper; future medication detail
sections drop in without copy-paste.- Shared helpers across the medication + onboarding surfaces.
New modulessrc/lib/api/read-error.ts,
src/lib/medications/route-guards.ts,
src/lib/medications/research-mode-types.tsand
src/lib/medications/dose-string.tsconsolidate previously
duplicated patterns. Three near-identical dose-string parsers
collapse to oneparseDoseMg; four-times-duplicated
readErrorcollapses to one; nine medication routes call the
sharedassertMedicationOwnership. - Side-effect taxonomy drift-guard.
SIDE_EFFECT_CATEGORY_VALUES
andSIDE_EFFECT_ENTRY_VALUESnow derive from the Prisma enum
viaz.nativeEnum; a drift-guard test pins Prisma enum ↔
taxonomy map ↔ validator triangle so the three cannot drift. - Side-effect category derived server-side. The
createSideEffectSchemano longer accepts a client-supplied
category; the server derives it from the entry. Removes the
defensive 422 path and the cross-tier drift surface. - Dashboard top-tile baseline alignment. Inline trend arrow +
baseline-aligned value row across all dashboard tiles, with the
baseline contract pinned by regression test. - Range-bar Dracula token swap.
<RangeBar>zone backgrounds
resolve through Dracula tokens with the pinned test rewritten;
raw palette references gone. - 44-px touch-target floor across the top-bar + section
strips. WCAG 2.5.5 alignment across the broader navigation
surface, complementing the onboarding-specific pass. - Cat-C typo + naming polish from the W10 review pass. Minor
identifier renames flagged by the dead-code probe; no functional
change. safeRequestPropwidened catch. Narrowed catch broadened to
tolerateundefinedrequests after the hygiene-review pass
surfaced the regression.pickCanonicalWorkouthelper for cross-source workout
dedup. Source-priority logic centralised for the v1.5 iOS
workout-ingest path.- Insights regenerate button relocated; tab strip lifted.
Tab-strip extraction + relocation of the regenerate button to
the top-right, paving the way for the seven sub-page surfaces.
Tests
- 2244 → 3828 passing unit tests across 344 files (+1584; one
pre-existing skip carries through). Integration suite 140 → ~170
across 11 files. 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. - Coach refusal-probe matrix. 1800+ assertions exercise 15
GROUND RULES across six locales with 20+ adversarial
paraphrasings each. CI runs the matrix on every push; any
prompt-injection or jailbreak regression surfaces before
reaching the dispatch surface. Drift-guard tests pin the
YAML-per-locale safety-contract matrix in lockstep with the
validator-derived enums. - New unit suites (selection): GLP-1 plateau-detection text
formatter; two-axis source-priority resolution (single-axis
fallback, per-device override, mixed-bucket selection);
source-priority Settings reducer (reorder, reset, validate);
medication-card GLP-1 variant + injection-site-picker (RTL);
doctor-report prefs PUT contract; personal-records pagination
clamp;berlinIsoWeekdaytimezone-aware suite; sleep-stage
chart i18n-suffix 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 +setHttprewrite; 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. - New integration suites: 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). - Migrations: 9 new (0051–0059) + 1 hardening (0060
onboarding_step_not_nullbackfill + NOT NULL flip). All
additive and forward-only; migration 0057 comment rewritten to
match PG11+ fast-path ADD COLUMN DEFAULT semantics.
Deferred to v1.4.26
See .planning/v1426-backlog.md for the full backlog with effort
estimates and source citations. Headline items:
User.onboardingGoalscolumn + server-side persistence for the
goals chip-picker selection (today the picker holds the choice
client-side only; the dashboard-widgets seed feature reads it in
v1.4.26).advance()hook extraction for the three onboarding step
components — pattern surfaced in the simplifier review, deferred
to avoid collision with v1.4.25 touch-target work.glp1-pk.tsunused-export decision (internalise vs wire
dashboard chip).- Seven orphan endpoint go / no-go decisions
(/api/admin/ai-settings,/api/admin/backup/test,
/api/admin/status-overview,
/api/monitoring/{glitchtip,umami}/test). - ~148 dead i18n keys remaining after the W10 runtime-probe sweep
(the v1.4.25 pass removed 380 of the 528 candidates; the
remaining ~148 need second-pass call-site verification). - FR / ES / IT / PL prose hand-review by a native speaker for the
Coach + insights surface (the structural refusal-probe matrix
covers safety; the user-facing prose still benefits from a
human pass). - Coach
lastYearbaseline + row-tap-to-prefill polish. - Sleep sub-page stacked-column visual polish (stage colours +
legend density). - Mood verbal-labels persistence behind a per-user toggle.
- Drug-level chart-side 90-day staleness clock wiring (the
acknowledgment dialog already re-prompts on version bump and
Coach refuses at GROUND RULE 15; the clock is defence-in-depth). - iOS-18 long-tail HK identifier mappings (sleep apnea, GAD-7 /
PHQ-9, running form, paddle / row / ski distances, pregnancy /
cycle, FHIR clinical). - VO2 max chart-row card on
/insights/<metric>(dashboard tile
shipped; chart card queued alongside the iOS body-composition
page). - Lazy-loaded locale JSON bundles (all six locales import
synchronously at present, ~675 KB to every client).
Deferred to v1.5
- iOS Swift app — P1 through P5. Login + dashboard + widget
(P1); Apple Health sync (P2); Coach extended for HRV / sleep /
resting HR / steps (P3); per-metric APNs alerts (P4); workouts- GeoJSON routes (P5). All server contracts locked in v1.4.25.
- Workout ingest API matching the v1.5 iOS contract.
Schema shipped this release; endpoint signature finalised
during the iOS sprint. - Two-Brain Coach refactor. Statistical findings produced
by a deterministic pipeline; LLM owns the narrative layer
only. Reduces hallucination surface; unblocks evidence-grounded
citations. - HRV anomaly detection against a rolling baseline.
- Mindfulness, dietary-water and symptoms-unification ingest
types. - ECG waveform ingest + FHIR / HKClinicalRecord.
- Pearson incomplete-beta replacement for the rigorous
surfacing-gate (v1.4.23 carryover).