Mostly table column normalisation under docs/audit/ + planning, object- literal expansion in src/lib/ai/coach/snapshot.ts after the W4 F6 Apple Health metric block landed, and small wrap/unwrap line adjustments in the device + APNs routes / tests touched across W3/W4/W5 sessions. No behavioural change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 KiB
HealthLog v1.4.20 — Release Summary
Release brief
Live: v1.4.20 (image digest
sha256:b112a31947b9…, /api/version=1.4.20).
v1.4.20 is the deliberately-loud release that cashes in the runway
v1.4.19 set up. The redesigned /insights page lands a hero strip
with greeting + suggested-prompt chips, a Daily Briefing card, an
AI Coach drawer with SSE-streaming chat and encrypted conversation
history, three pre-defined correlation cards (BP × medication
compliance, mood × pulse, weight × weekday), a Trends row with AI
annotations, a printable Weekly Report at /insights/report/[week],
and a Personal Health Score panel (composite 0-100 with three
bands). The polish-vs-feature ratio inverted to roughly 30/70 — the
right ratio for a release that means to ship new product.
PROMPT_VERSION ratcheted 4.19.0 → 4.20.2 across three controlled
bumps; aiInsightResponseSchema gained five optional blocks
(dailyBriefing, trendAnnotations, weeklyReport,
storyboardAnnotations, healthScore). Every new field is
nullable + optional, so insights cached before v1.4.20 still parse
without a re-run — the v1.4.17 lesson is wired in by construction
now, not by audit.
Hard-Reload (Cmd+Shift+R) for SW reset.
v1.4.20 is also the first release shipped through the new
develop → main branch model introduced in this milestone's
foundation phase: feature work lands on develop and stays out of
the GHCR build path; only the release-merge to main plus the
annotated tag trigger image publication. The model is documented in
CONTRIBUTING.md and on the public docs site under
/contributing/branch-model/.
Production deploy
| Field | Value |
|---|---|
| URL | https://healthlog.bombeck.io |
/api/version |
1.4.20 (flipped at 2026-05-10T16:49:25Z) |
| Image digest BEFORE | sha256:b48f93874cdbcd6c2d729f1b8eeb63a6d1bbb90d56f629846ef6eab6cf272aa9 (v1.4.19) |
| Image digest AFTER | sha256:b112a31947b91f2fdb4bb01b60c6b17a0b8ec128ae116d37e1094da5c3decaa6 (v1.4.20) |
| Release-prep commit (develop) | aac8b5e chore(release): v1.4.20 |
| Develop ↔ main sync commit | dd2ef57 Merge main into develop — sync Dependabot dep bumps before v1.4.20 tag |
| Release-merge commit (main) | 666f6ee Release v1.4.20 |
| Tag | v1.4.20 (annotated) at 666f6ee |
| GHCR — tag run | 25633807964 success (4m02s) |
| Coolify auto-deploy | NO — webhook secret still missing on the deploy server (same fault mode as v1.4.19); host-side retag fallback used |
| Smoke (curl, no session) | /api/version 200 · /auth/login 200 · all gated routes return 307 → /auth/login (expected proxy.ts behaviour without a session cookie) |
| GH release | https://github.com/MBombeck/HealthLog/releases/tag/v1.4.20 |
| Docs site | 0b83430 on healthlog-docs/main (updates.mdx + scaling.mdx version pins; ai-insights.mdx v1.4.20 callout) |
| Landing site | 43ce4c7 on healthlog-landing/main (softwareVersion JSON-LD bumped, three new featureList entries) |
The tag-build pipeline succeeded; the deploy webhook still isn't wired on apps-01, so v1.4.20 was promoted via the documented host-side fallback path:
docker pull ghcr.io/mbombeck/healthlog:1.4.20
docker tag ghcr.io/mbombeck/healthlog:1.4.20 ghcr.io/mbombeck/healthlog:latest
cd /data/coolify/applications/pg8wggwogo8c4gc4ks0kk4ss
docker compose up -d --force-recreate app
/api/version flipped to 1.4.20 on the first poll cycle after
recreate — well inside the 5-minute cap. The Coolify-managed
restart-and-redeploy path was tried twice first (d10hrd3he27w…
and tgkn9ewch46c…); both ran cleanly to "finished" but neither
pulled a fresh manifest, so the running container kept serving
v1.4.19. Wiring an image-digest auto-deploy hook on apps-01 is
captured in .planning/v1421-backlog.md.
Smoke results
The previous releases' brief showed 200s because they ran with a
live session cookie that's not persisted on this run. Without the
cookie, every authenticated route correctly returns 307 →
/auth/login, so the smoke verifies the proxy gate is healthy
end-to-end:
| Path | Status (no session) | Notes |
|---|---|---|
/api/version |
200 | Returns version: "1.4.20" |
/auth/login |
200 | Public surface, renders normally |
/ |
307 → /auth/login |
Gated dashboard root |
/insights |
307 → /auth/login |
Redesigned Insights shell |
/insights/report/2026-W19 |
307 → /auth/login |
New v1.4.20 weekly-report route |
/admin/api-tokens |
307 → /auth/login |
Admin section |
/settings/integrations |
307 → /auth/login |
Settings section |
/achievements |
307 → /auth/login |
Achievements page |
/dashboard |
307 → /auth/login |
Expected: root is the dashboard, no /dashboard route |
The Coach drawer and Health Score panel can't be smoked without a
live session — neither carries a public surface. Confirmed live via
the /api/version check; Wave-D reviewers exercised both paths
under integration coverage before tag.
What shipped
Hero strip + Daily Briefing + Suggested Prompts
/insights opens with a redesigned hero strip that pairs a
time-of-day greeting and primary action row with a horizontal strip
of suggested-prompt chips. A Daily Briefing card mounts full-width
below the hero, surfacing an AI-generated paragraph plus three
keyFindings drawn from the last 24-hour window. Suggested-prompt
chips drive the Coach drawer with the prompt prefilled, so the
shortest path from "I want to know X" to a streaming answer is one
tap. The four-vitals tile row from the design artboard was
intentionally deferred — the per-section status cards already
render the same numbers, and the briefing fills the at-a-glance
role.
AI Coach drawer with streaming chat + encrypted persistence
The biggest piece of the release. POST /api/insights/chat is an
SSE endpoint that walks the existing AI provider chain, persists
user + assistant turns under AES-256-GCM, and emits token →
provenance → done frames. A per-user daily token budget
(25 000 tokens, ≈13 turns for a heavy user) caps shared-key spend;
a refusal pattern blocks prompt-injection attempts and obvious
off-topic asks before the provider call. The drawer mounts from the
hero strip's "Ask the coach" button and from any suggested-prompt
chip; layout is three columns on lg+ (history rail · message
thread · sources rail) and full-screen single-column on mobile with
chevron-button trays for the rails. Source-chip provenance attaches
to every assistant turn (metric, window, n-count — labels only,
never raw values). parseSseChunk() is exported as a pure helper
so the streaming parser is unit-testable without a live network.
Correlation cards + Trends row with AI annotations
Three pre-defined hypothesis cards mount between the Daily Briefing
and the Advisor card: BP × medication compliance, mood × pulse, and
weight × weekday. Pearson r is rendered with a Fisher-z confidence
interval; surfacing gate is n ≥ 14 paired observations and p < 0.05.
The Trends row alongside it carries short AI-generated annotations
tied to specific dates in the analyzed window; the BP timeline
gains storyboard markers at the same positions via an additive
annotations[] prop on <HealthChart>.
Weekly Report at /insights/report/[week]
A newsletter-style printable surface with five sections: Summary,
Going-well, Worth-watching, Tips, Data-quality. window.print()
export via Tailwind print: variants gives a clean A4 page without
a server round-trip. The hero strip surfaces fresh weekly reports
with Read · Share · Export PDF actions; Share uses Web Share API
with a clipboard fallback, Export deep-links to ?print=1.
Personal Health Score (composite 0–100)
A deterministic composite score with three bands (green ≥75, yellow
50–74, red <50). Weights: 30% BP-target rate + 20% weight-trend
alignment + 20% mood stability + 30% medication compliance. When
a sub-component is null, remaining weights scale to 100 so the
score stays interpretable. The <HealthScoreCard> renders alongside
the hero strip on lg+ (220 px desktop, full-width mobile) with a
band-tinted progress bar, four sub-bars, "vs last week" delta line,
and a score-aware "Ask the Coach" CTA that opens the drawer with
"Why is my health score X out of 100?" prefilled.
Quality bar
- Test counts moved hard. 1672 → 2026 unit tests (+354 net), 67 → 81 integration (+14). Five new test files for the Coach alone (use-coach, source-chips, message-thread, coach-input, sources-rail, history-rail). Test files 217 → 237.
- typecheck + lint clean. 0 type errors, 13 baseline warnings
unchanged (every warning is the
_request/_params/ intentional-unused pattern). - Multi-reviewer Wave-D pass. Six reviewers (code, security,
design, senior-dev, simplify, product-lead) filed 0 CRIT · 12
HIGH · 28 MED · 22 LOW + 5 simplify-apply-yes + 4 simplify-apply-
maybe + 3 simplify-apply-no. All 12 HIGH items + the design
pushback on the disabled weekly-report button + 6 MED items +
every simplify-apply-yes item landed inline across 11 atomic
commits on
develop(e632e26→31fbf98). 22 MED + 16 LOW + 4 simplify-apply-maybe deferred to.planning/v1421-backlog.mdunder "Phase D — v1.4.20 reconcile carry-over". - One known integration-test flake.
coach-chat.test.ts/ "round-trips" was observed to flake on a single run during Phase D and again during pre-flight; consistent re-runs pass. Tracked in the v1.4.21 backlog. - format:check clean on the source tree. A small style commit
(
73e51d7 style: prettier on touched insights/coach surfaces) ran prettier across the new B-phase additions before tag, so the only remainingformat:checkwarnings are the.planning/*baseline noise carried over since v1.4.16.
Branch model
v1.4.20 is the first release shipped through the new
develop → main model:
- All B1 → B5 phase work, every Wave-D reconcile commit, the
release-prep commit, the prettier sweep, and the merge-back of
Dependabot's day-of dep bumps landed on
develop. - A single
Release v1.4.20no-ff merge commit onmainplus the annotatedv1.4.20tag triggered the GHCR tag-build job (25633807964). - The GHCR workflow is gated to
branches: [main]+tags: ['v*']—developpushes neither build nor publish images, which removes the v1.4.13-era image-churn problem on GHCR. - Two Dependabot dep-bump PRs (
#161,#165) auto-merged intomainafter the tag build started but before the host-side fallback ran. They were absorbed intodevelopahead of the release-merge via aMerge main into developcommit, so the v1.4.20 tag includes them. Three more dep-bump PRs (#153, #154, #157, #158, #159, #160) had auto-merged earlier the same day and were absorbed the same way.
Carry-over
Full backlog: .planning/v1421-backlog.md.
- 22 MED + 16 LOW + 4 simplify-apply-maybe items from the
multi-reviewer Phase-D pass. Headline items: senior-dev call to
consolidate the duplicated Pearson / linear-regression maths
layer (Sr-HIGH-2 — too large for inline reconcile); refactor
<CoachDrawer key={prefill}>into a controlled prefill prop (Sr-HIGH-4); transactionalrecordSpend()(Sec-M-4); refusal accounting and lexicon expansion (Sec-M-2 / Sec-M-3). - Schema drift on
medication_schedules.days_of_week. Worked around twice in v1.4.20 (B5 analytics, B2a coach snapshot); the migration needs to either deploy the column or drop it fromschema.prisma. src/app/insights/page.tsxnow composes nine distinct surfaces. Splitting the component is on the v1.4.21 cleanup short-list; pre-emptive splitting before the multi-reviewer pass ran was deliberately avoided.- Coolify image-digest auto-deploy hook on apps-01. Tag-build
pipeline is fine; the deploy step needs the webhook wired so
/api/versionflips on its own without the manual host-side retag. - Source-comment sweep (191 maintainer-name references in
src/), DE+EN bilingual CHANGELOG entries (v1.4.15 era), andCLAUDE.md/AGENTS.mdfilename hygiene are tracked for a hygiene PR.
Strategic next
v1.5 stays reserved for the iOS native app + Apple Health
integration + per-metric APNs alerts. The redesigned /insights
shell, AI Coach drawer, and Health Score panel were all built with
that next milestone in mind: the Coach SSE endpoint accepts the
same Bearer-token auth that ships for the existing native API
clients (hlk_<64hex>); the Health Score formula is deterministic
and pure, so an iOS port can compute the same number locally if it
wants to; the source-chip provenance contract is JSON-only and
labels-only, ready for an APNs payload. Strategic plan and the
artboard / API-shape sketches at
.planning/phase-D-v1420-product-lead-review.md.
GH release URL
https://github.com/MBombeck/HealthLog/releases/tag/v1.4.20
Hard-Reload reminder
Cmd+Shift+R (Mac) / Ctrl+Shift+R (Linux/Win) — the new service
worker otherwise still serves v1.4.19 chunks from cache. One-shot
is enough.