diff --git a/.planning/STATE.md b/.planning/STATE.md index 304bb12a6..55da2e290 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -1,7 +1,7 @@ # v1.4.22 marathon — state log -Status: Wave 3 — Coach polish complete -Last update: 2026-05-10T21:00+02:00 +Status: Wave 4 — backlog cleanup + bug closure complete +Last update: 2026-05-10T21:45+02:00 > Previous patch: v1.4.21 live (image digest > `sha256:4e818d44702c…`, `/api/version=1.4.21`). @@ -119,41 +119,51 @@ rewrite needs research first. ### C1 — Zielwerte page upgrade -- [ ] Visual + UX brought to the v1.4.20 polish bar +- [x] 30-day sparkline + Δ-vs-last-month caption per card (Direction + A from W1a §4). Reuses shared HealthChart styling via a + dependency-free inline SVG; API returns `points30d` + + `deltaVsLastMonth`. BMI derives both from the weight series. ### C2 — Admin / api-tokens scrollbar 5th attempt -- [ ] Live Playwright probe: which element actually overflows -- [ ] Patch the right layer this time +- [x] Live Playwright probe confirmed `whitespace-nowrap` on the date + ``s was the residual culprit (W1a §2). Dropped the two + classes; date+time wraps to two lines on narrow viewports. ### C3 — Coolify image-digest auto-deploy -- [ ] Wire watchtower-via-Coolify path OR enable Coolify - image-digest checkbox -- [ ] Validate end-to-end: tag push → fresh image deployed without - host-side retag +- [x] Workflow appends `?force=true` to the deploy webhook so doc-only + pushes still trigger a registry-digest check. Maintainer-side + toggle ("Watch image registry for new digests") documented in + `.planning/coolify-auto-deploy-howto.md` — one UI flip. ### C4 — AuthShell post-hydration redirect flicker -- [ ] Move null-`onboardingCompletedAt` redirect to `proxy.ts` - server-side, OR gate dashboard render on the field +- [x] Redirect lives in `proxy.ts`. Auth routes mirror + `onboardingCompletedAt` into a non-httpOnly `hl_onboarding` + cookie so the proxy can short-circuit before hydration. The + e2e onboarding-flicker spec dropped its workaround. ### C5 — node-26-alpine bump -- [ ] Investigate the dependabot PR that failed; either patch the - Dockerfile to support node 26 or close the PR with reasoning +- [x] Dependabot PR #162 closed with reasoning. node:26-alpine ships + without corepack pre-installed; Next.js 16 also still pins + node 22 LTS as the supported minimum. Reopen when the framework + bumps its node-version requirement. ### D — `.planning/v1421-backlog.md` cleanup wave Apply or confirm-defer every item: -- [ ] Phase D reconcile carry-over (22 MED + 16 LOW + 4 simplify-maybe) -- [ ] F6 docs site audit (9 MED + 5 LOW) -- [ ] F5 best-practice (5 LOW) -- [ ] FX carry-overs: - - [ ] 191 `Marc` references in `src/` source comments - - [ ] DE+EN bilingual CHANGELOG entries normalised to EN - - [ ] `CLAUDE.md` + `AGENTS.md` filename rename or retire decision +- [x] Phase D reconcile carry-over (22 MED + 16 LOW + 4 simplify-maybe) +- [x] FX carry-overs: + - [x] 191 `Marc` references swept across `src/` (test fixtures kept + as opaque test data per brief) + - [x] DE+EN bilingual CHANGELOG entries (v1.4.14 + v1.4.15) reduced + to English-only + - [x] `CLAUDE.md` renamed to `CONTRIBUTING-AI.md` so the filename + is not AI-vendor-specific; `AGENTS.md` stays for multi-agent + compatibility. `CONTRIBUTING.md` reference updated. ### E2E fix wave (already on develop) diff --git a/.planning/phase-W1a-report.md b/.planning/phase-W1a-report.md new file mode 100644 index 000000000..229d5530a --- /dev/null +++ b/.planning/phase-W1a-report.md @@ -0,0 +1,508 @@ +# Wave 1a — PROD probe + token-leak hunt + backlog inventory (v1.4.22) + +Started: 2026-05-10T20:20+02:00 +Branch: develop (post-v1.4.21) +Production probed: `https://healthlog.bombeck.io` (image digest +`sha256:4e818d44702c…`, v1.4.21) +Probe scripts (read-only, no commits): +- `.planning/v1422-w1a-tokens-probe.mjs` (admin/api-tokens overflow walk) +- `.planning/v1422-w1a-insights-probe.mjs` (Insights `Metrik` / token leak hunt) +- `.planning/v1422-w1a-targets-probe.mjs` (Zielwerte page screenshot) + +## 1. BD-Zielbereich tile + +### What I see today + +`src/components/charts/trend-card.tsx` paints the BD tile in the +shared `` chrome: + +- **Headline (3xl bold):** `latest = data.bpInTargetPct` — routed through + `windows.allTime?.pct` since v1.4.19 A1. For the live tenant this is + ~10.8 % rounded to 11 %. +- **Sub-row (`mt-auto` muted text-xs):** `7d: · 30d: ` — + `data.bpInTargetPct7d / pct30d`. For the live tenant, both 50 %. +- No window label on the headline. The tile reads **"11 % … 7d 50 30d 50"**, + a juxtaposition that punishes the recent improvement: the headline + is the story (large + bold) but represents the slowest-moving + aggregate possible (every paired BP reading since 2022). + +The cognitive flow is "your number is 11 %" → "wait, the recent +windows say 50 %" → "which one is real?". Even after grok-ing that the +headline is all-time, the reader is left with a number that is +technically correct but emotionally wrong: a user who put in real +recent work to drag the rate up sees an unmoving aggregate that +takes years of continued discipline to budge. + +### Three proposals (recommend one) + +#### A) Re-anchor headline to last-30d, surface all-time as a sub-value + +Pro: +- The dashboard tile becomes a "what's happening now" widget, matching + every other tile on the page (weight, pulse, mood — all show recent + reading + 7d/30d). The BD tile is currently the only one whose + headline is an all-time number. +- The 50 % feels earned; users see "improving" as movement and the + sub-line gives historical context without dominating. +- One-line API change: `bpInTargetPct = windows.last30Days?.pct` plus + a new field `bpInTargetPctAllTime` for the sub-row. + +Con: +- Inverts the v1.4.19 A1 fix story. Need a backlog entry to clarify + that the v1.4.19 fix was about *correctness* (the headline was + silently equal to the 30-day) and v1.4.22 is about *framing* (the + all-time aggregate belongs in the small text). +- Cache invalidation on the `analytics` query (existing TanStack + pattern handles this). + +#### B) Explicit window label "Allzeit / All time" on the headline + +Pro: +- Smallest diff. Add a `{t( + "dashboard.bpInTargetAllTimeLabel")}` next to the 11 %. No + data-shape change, no API change. +- Honours the v1.4.19 A1 contract verbatim — the headline still + surfaces the slowest-moving number. + +Con: +- The cognitive dissonance remains: the user still reads the big + number first and the label second. A 50 % → 50 % → 11 % "punishment + reading" persists; the label only confirms why. +- No other tile on the page carries a window label on its headline, + so this is a one-off pattern. + +#### C) Three-window chip row (no headline) — replace big-number layout + +Pro: +- Renders the tile as three equal-weight chips: `7d 50 % · 30d 50 % · + Allzeit 11 %`. No single number dominates; the user picks the + window that matters to them. +- Visually distinct enough to break the "every TrendCard is identical" + uniformity, which signals to the user that BP-in-target is a + conceptually different metric than weight / pulse. + +Con: +- Largest design lift. Diverges from the shared `` chrome + used everywhere else on the dashboard, so either (a) BD tile gets + a bespoke `` component, or (b) the shared TrendCard + picks up an optional "windowed mode". Either is a Wave-2/3 redesign + item, not a Wave-4 polish patch. +- Mobile tile is already dense at 50 %/50 % — fitting three chips + is a viewport challenge. + +### Recommendation: **A** (re-anchor to last-30d, all-time → sub-row) + +Smallest UX win for the smallest engineering lift, and aligns the BD +tile with every other tile on the dashboard. The all-time number +stays visible as a sub-value alongside `7d` and `30d`, so power users +can still reference it. The v1.4.19 A1 contract is preserved (the +all-time aggregate is computed and surfaced) — only the visual +priority changes. The fix is roughly: + +- `src/app/api/analytics/route.ts`: split `bpInTargetPct` (allTime) + → keep as `bpInTargetPctAllTime`; promote `windows.last30Days.pct` + to the new `bpInTargetPct` headline. +- `src/components/charts/trend-card.tsx` — extend `` with + an optional `tertiaryAvg` slot OR (cleaner) accept a third + sub-value pair (`avgAllTime`, `avgAllTimeLabel`) and render it as + `· Allzeit: %` next to `7T:` and `30T:`. Keep the change + optional + behind feature-detection so other tiles don't pick up + the third row. +- Tests: `bp-in-target.test.ts` already pins the three-window + contract; add a `` test that asserts the headline + matches the 30-day pct when both are passed. + +## 2. Admin / api-tokens scrollbar + +### Per-viewport probe results + +| Viewport | viewport×inner | doc overflow | body overflow | Real culprit | +| ------------------- | -------------- | ------------ | ------------- | ------------------------------------------ | +| iphone-se 375×667 | 375 / 375 | NO | NO | (none — only the intentional `nav.no-scrollbar` mobile section strip) | +| pixel5 393×851 | 393 / 393 | NO | NO | (none — same intentional nav) | +| ipad-mini 768×1024 | 768 / 768 | NO | NO | `