Added
- Per-target sparkline + Δ-vs-last-month caption on the Targets /
Zielwerte page. Each<TargetCard>grew a 30-day inline SVG
sparkline beneath the range bar plus a localised "Δ −2.3 kg vs.
last month" caption. The API shipspoints30dand
deltaVsLastMonthper target; both null when either window has
fewer than 3 readings so cold-start accounts don't paint a
misleading flat trace. BMI piggybacks on the weight series so its
sparkline shares the range bar's y-axis. - Sticky section navigation above the Insights hero. A pinned
strip (Allgemein / Blutdruck / Gewicht / Puls / Stimmung in DE;
General / Blood Pressure / Weight / Pulse / Mood in EN) lifts the
section tabs above the hero so they stay visible during scroll.
Active section tracks the highest-intersection observed entry;
aria-current, focus-visible ring, andmotion-reducegating
ship from day one. - Comparison-overlay as a single global preference under Settings
→ Dashboard. The on-surface<CompareToggle />retired from
/insights; the canonical picker has lived in Settings →
Dashboard since v1.4.16 phase B8 and every chart already consumed
it. Two surfaces for the same concept violated the
no-split-Settings rule. - Collapsible evidence disclosure under each Coach assistant
message. Numbers move out of the prose into a
---KEYVALUES---…---END---sentinel block that the route
parses out server-side and renders as a "Worauf bezieht sich
das?" / "What I'm looking at"<details>disclosure under each
assistant turn. Closed by default, hidden when no key-values came
back. Hard caps on the sentinel (1 KB payload, 8 lines max,
per-line Zod) so a prompt-injection attempt can't grow the
persisted envelope. - User avatar parity with the Coach avatar. The Coach drawer's
user-side bubble used a smaller initials avatar than the Coach's
gradient one. The user avatar now reuses the existing
gravatarUrlfield from/api/auth/meat the same dimensions
as the Coach avatar; initials fall back when no Gravatar is
configured.
Changed
- Coach prompt rewrite — warm, motivational-interviewing tone
with prose-first responses + evidence collapsible. PROMPT_VERSION
ratchets 4.20.2 → 4.22.0 (first minor-digit bump in v1.4.x). The
Coach used to open every reply with a number — clinical,
database-cursor energy. The new persona is "warm, neugierig,
zurückhaltend": a partner sitting alongside the user, not pushing
data. Numbers move into the collapsible evidence block; the prose
reads like a real conversation. Persona + sentinel land together
because either alone is incomplete. - BP-in-target headline re-anchored to the last-30-day window.
Fourth attempt at this metric. v1.4.19 routed the headline to
allTimeto fix the algorithmic 50/50/50 pin; v1.4.22 re-anchors
towindows.last30Days?.pctand surfaces all-time as a sub-row,
because v1.4.19's fix was emotionally wrong — the headline became
the slowest-moving aggregate possible, punishing a user who put
in real recent work. The tile also gets a synthesised trend arrow
(slope from 7d/30d delta), a 7-day-trend chip, and a
comparison-overlay caption. - "Muster" renamed to "Zusammenhänge" (DE) / "Patterns" renamed
to "Relationships" (EN). The picked-bucket-of-correlations row
read like an autopsy. The new label sits closer to what the row
actually shows. Picked "Zusammenhänge" over "Trends" because the
row directly above already uses Trends. - Onboarding redirect for users with
null onboardingCompletedAt
moves to server-side enforcement inproxy.ts. The previous
post-hydration redirect inside<AuthShell>useEffectproduced
a brief dashboard flash on incomplete onboarding. The proxy runs
in the Edge runtime so the auth routes mirror
onboardingCompletedAtinto a non-httpOnlyhl_onboarding
cookie; the proxy short-circuits before hydration. Tampering only
skips a UX hint and never bypasses a server check. setOnboardingPendingCookiefolded intocreateSession().
Issuing a session without onboarding state used to require two
call sites to stay in sync; the helper now takes
onboardingPendingas a required parameter so the contract is
type-impossible to break.
Fixed
- Raw
metric:<TYPE>token leaks in recommendation prose.
<RecommendationCard>text is now wrapped in
stripChartTokens(). The leak traced to a single missing call
on the recommendation path; chart tokens never belonged in user
copy. - DE locale
componentMood,componentBp,componentCompliance
rendered English nouns in the German bundle. Four Health-Score
component labels normalised toStimmung,Blutdruck,
Einnahmetreue,Gewicht. The i18n-integrity test pins the
contract. - Admin / API tokens horizontal scrollbar at desktop + iPad-mini
viewports (5th attempt). A live Playwright probe confirmed
whitespace-nowrapon the date<td>s was the residual
culprit. The two classes are gone; date + time wraps to two
lines on narrow viewports. The earlier four fixes had targeted
the wrong layout layer. - Sentinel-only / malformed
---KEYVALUES---block. When the
model emits a sentinel-only or malformed envelope, the fallback
now produces a polite invitation ("I'd like to look at this with
you — could you share which window you want me to focus on?")
instead of surfacing the raw marker. A new integration test
covers the empty-prose-after-strip branch. - BD-Zielbereich tile delta math period-aligned with the
comparison window. The tile's compareDelta was subtracting
bpInTargetPctAllTimewhile the caption said "vs last month",
which produced numbers the caption couldn't justify. It now
subtractsbpInTargetPctPriorMonth/bpInTargetPctPriorYear
to match. Two new bp-in-target unit tests + two
insights-polish guards. - Coach drawer settings cog removed. The cog was a dead button
in v1.4.21; per-user prompt-tuning is deferred to v1.4.23. No
dead buttons in this release. - Coach disclaimer pinned at the bottom of the message thread.
Clinical-adjacent UI must not gate the disclaimer behind a
chevron tray; the disclaimer now stays visible at the bottom of
the message thread on every viewport, and the rail-footer
duplicate kept for desktop redundancy. hl_onboardingUX-hint cookie nowSameSite=Strict. A
cross-site request couldn't usefully exfiltrate the cookie
(no auth value, only an onboarding state flag) butLaxwas
still over-permissive. Strict aligns with the cookie's
same-site-only consumer.PUBLIC_PATHSexact-match guard against future subroute
prefix bypasses./onboardingis now exact-match +
explicit-subroute, not astartsWithcheck. Two new proxy
guards pin the contract.targets/route.tsdaily buckets keyed in Europe/Berlin.
The targets sparkline was the last analytics surface still
bucketing in UTC, which produced a one-day-off trace for users
in CEST.berlinDayKey()lifted to a shared
src/lib/analytics/berlin-day.tshelper; four new DST + UTC-
midnight edge-case unit tests.- Streaming bubble vs persisted-twin race window. The
150ms grace window suppresses the persisted twin while the
in-flight streaming bubble is still rendering, so the thread
never paints two copies of the same reply for a frame. - Sticky section navigation a11y polish.
aria-currenton
the active section; focus-visible ring on keyboard navigation;
motion-reducehonoured for smooth-scroll; the glow-bleed
fixed viabg-background/95 backdrop-blur; the mobile cliff
tightened fromscroll-mt-28toscroll-mt-16so the strip
no longer eats the heading at 280px viewports. - BP tile mobile density at <sm. All-time + delta now
collapse into one secondary line on small viewports; the
full layout returns at>=sm.
Refactor
createSseStreamshared helper extracted from the chat
route. Preparation for v1.5 iOS streaming endpoints, which
will reuse the SSE primitives. Three unit tests pin sync,
async, and throw paths. Source:src/lib/sse/create-stream.ts.<TokenStatusBadge>extracted from desktop + mobile
api-token surfaces. The badge logic was duplicated verbatim
in two layouts; one component, two consumers.- Five simplify apply-yes items in one commit.
canSubmit
collapse, weekly-report<Button>dedup, and three smaller
cleanups identified in the W5 simplify pass.
Operational / hygiene
- Coolify image-digest auto-deploy. Instructions for the
one-time UI-toggle ("Watch image registry for new digests")
live at.planning/coolify-auto-deploy-howto.md. Future
releases should drop the host-side retag fallback the moment
the toggle is on. - 191 maintainer-name references in
src/source comments
swept (FX carry-over from v1.4.20). Test fixtures kept as
opaque test data. - DE+EN bilingual CHANGELOG entries (v1.4.14 + v1.4.15)
normalised to English-only. Per the English-only voice
rule. CLAUDE.mdfilename retired (FX carry-over) so the
filename is no longer AI-vendor-specific;CONTRIBUTING.md
reference updated.AGENTS.mdstays for multi-agent
compatibility.
Deferred to v1.4.23
See .planning/v1422-backlog.md for the full carry-over list.
Highlights: sentinel parser malformed-enum hardening (Sr-M5);
analytics-route unbounded findMany paging; targets-route
7-pass sparkline coalesce; CoachDrawer key={prefill}
controlled-prop refactor (Sr-HIGH-4); per-user prompt-tuning
surface; medication_schedules.days_of_week schema-drift cleanup.
Deferred to v1.5 (iOS push)
See .planning/phase-W5-v1422-product-lead-review.md for the
full v1.5 plan. Headline: iOS native client + Apple Health
ingest contract (HRV, Sleep, Resting HR, Steps, BodyFat,
Glucose); per-metric APNs alerts; OpenAPI spec drift CI gate;
Coach extension for the new measurement types
(PROMPT_VERSION 4.22.0 → 5.0.0).