Files
HealthLog/docs/audit/v1425-summary.md
T

464 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `<TargetCard>` 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 `<OnboardingShell>` 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; `<InsightsPageHero>` + `<IntakeTimeline>` + `<ComplianceCharts>` + 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/<metric>` (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
`<CoachDrawer>` 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.