style(docs): prettier sweep on audit summary tables

Whitespace-only changes from the same pretter pass that cleared
the rest of the tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-10 11:10:09 +02:00
1 parent 395129a18c
commit 481403dc3d
4 files changed
+202 -199

No files matched your search

+51 -51
View File
@@ -62,51 +62,51 @@ Multi-Provider-AI-Abstraktion + `MockAIProvider`.
### Quick fixes (Phase A1–A5 + B-mobile)
| SHA | Summary |
| --------- | ---------------------------------------------------------------------------------- |
| `85aa15b` | fix(nav): hide bug-report entry in nav when admin disables the toggle |
| `786b395` | fix(a11y): skip-link no longer blocks logo click outside focus |
| `bde167d` | fix(feedback): hide user-facing feedback link when admin disables the feature |
| `c63e4de` | fix(feedback): gate ErrorDetails report-bug button on bugReportEnabled |
| SHA | Summary |
| --------- | ----------------------------------------------------------------------------------- |
| `85aa15b` | fix(nav): hide bug-report entry in nav when admin disables the toggle |
| `786b395` | fix(a11y): skip-link no longer blocks logo click outside focus |
| `bde167d` | fix(feedback): hide user-facing feedback link when admin disables the feature |
| `c63e4de` | fix(feedback): gate ErrorDetails report-bug button on bugReportEnabled |
| `a967895` | feat(admin): replace overview section-grid with audit-log preview + system snapshot |
| `dddc18c` | fix(admin): make api-tokens table responsive on mobile |
| `3e45a7b` | fix(dashboard): disambiguate quick-add submenu labels |
| `2c227fb` | fix(dashboard): mood tile mobile shows number once + label |
| `bb4dc12` | fix(dashboard): no onboarding flicker on load; collapsed by default |
| `bffdccb` | feat(dashboard): medication compliance graph wired to layout toggle |
| `47ac14b` | feat(insights): mood chart auto-aggregates to weekly/monthly |
| `4e2386e` | feat(dashboard): 7-day-average → 7-day-trend with delta indicator per metric |
| `8ccdfac` | feat(dashboard): top tiles selectable in layout settings |
| `316c3b0` | fix(charts): chart wrappers allow vertical scroll passthrough on mobile |
| `41945b2` | fix(admin): /admin/users mobile layout — card-list at < md |
| `8370b2d` | fix(mobile): chart controls + medication buttons + mood list icons to 44px |
| `00f8cd5` | fix(settings): passkey list responsive — card view at < md |
| `c0b14f4` | fix(auth): bump login buttons to 44px tap-target on mobile |
| `dddc18c` | fix(admin): make api-tokens table responsive on mobile |
| `3e45a7b` | fix(dashboard): disambiguate quick-add submenu labels |
| `2c227fb` | fix(dashboard): mood tile mobile shows number once + label |
| `bb4dc12` | fix(dashboard): no onboarding flicker on load; collapsed by default |
| `bffdccb` | feat(dashboard): medication compliance graph wired to layout toggle |
| `47ac14b` | feat(insights): mood chart auto-aggregates to weekly/monthly |
| `4e2386e` | feat(dashboard): 7-day-average → 7-day-trend with delta indicator per metric |
| `8ccdfac` | feat(dashboard): top tiles selectable in layout settings |
| `316c3b0` | fix(charts): chart wrappers allow vertical scroll passthrough on mobile |
| `41945b2` | fix(admin): /admin/users mobile layout — card-list at < md |
| `8370b2d` | fix(mobile): chart controls + medication buttons + mood list icons to 44px |
| `00f8cd5` | fix(settings): passkey list responsive — card view at < md |
| `c0b14f4` | fix(auth): bump login buttons to 44px tap-target on mobile |
### Features (Phase B1–B6)
| SHA | Summary |
| --------- | ---------------------------------------------------------------------------------- |
| `fe85c2c` | feat(admin): restore from backup with full data replacement + triple-confirm |
| `d8c549e` | feat(admin): backup download as JSON |
| `30a74ed` | feat(admin): backup upload |
| `0805452` | fix(admin): backup audit log entries |
| `7c32d63` | feat(admin): link from backups view to docs.healthlog.dev/admin/backups |
| `4db72a8` | feat(integrations): connection-state + sync-error UI in Settings → Integrations |
| `b290a77` | fix(integrations): refresh-token failure marks integration as needing re-auth |
| `604dff0` | feat(integrations): admin Telegram alert on persistent sync failure (≥3) |
| `2fbf56d` | feat(audit): sync failures logged with structured meta |
| `87a40fd` | fix(notifications): auto-disable channels on persistent hard rejects (410, etc.) |
| `a3c0130` | feat(settings): notification channel status UI with re-enable + test |
| `34c967c` | feat(achievements): dedicated /achievements page with locked/unlocked list |
| `a242047` | feat(dashboard): recent achievements card with toggle in layout settings |
| `81f5019` | feat(dashboard): RecentAchievementsCard component + tests |
| `db5a49d` | feat(db): User.onboardingTourCompleted flag |
| `e57fc0a` | feat(onboarding): tour component with spotlight + keyboard navigation |
| `8215e25` | feat(onboarding): auto-launch tour for new users on first dashboard load |
| `fa1c6a6` | feat(settings): allow user to replay the onboarding tour |
| `d692119` | feat(doctor-report): configurable date range with default last-90-days |
| `28467b2` | feat(doctor-report): practice name on cover page (persisted as user preference) |
| SHA | Summary |
| --------- | -------------------------------------------------------------------------------- |
| `fe85c2c` | feat(admin): restore from backup with full data replacement + triple-confirm |
| `d8c549e` | feat(admin): backup download as JSON |
| `30a74ed` | feat(admin): backup upload |
| `0805452` | fix(admin): backup audit log entries |
| `7c32d63` | feat(admin): link from backups view to docs.healthlog.dev/admin/backups |
| `4db72a8` | feat(integrations): connection-state + sync-error UI in Settings → Integrations |
| `b290a77` | fix(integrations): refresh-token failure marks integration as needing re-auth |
| `604dff0` | feat(integrations): admin Telegram alert on persistent sync failure (≥3) |
| `2fbf56d` | feat(audit): sync failures logged with structured meta |
| `87a40fd` | fix(notifications): auto-disable channels on persistent hard rejects (410, etc.) |
| `a3c0130` | feat(settings): notification channel status UI with re-enable + test |
| `34c967c` | feat(achievements): dedicated /achievements page with locked/unlocked list |
| `a242047` | feat(dashboard): recent achievements card with toggle in layout settings |
| `81f5019` | feat(dashboard): RecentAchievementsCard component + tests |
| `db5a49d` | feat(db): User.onboardingTourCompleted flag |
| `e57fc0a` | feat(onboarding): tour component with spotlight + keyboard navigation |
| `8215e25` | feat(onboarding): auto-launch tour for new users on first dashboard load |
| `fa1c6a6` | feat(settings): allow user to replay the onboarding tour |
| `d692119` | feat(doctor-report): configurable date range with default last-90-days |
| `28467b2` | feat(doctor-report): practice name on cover page (persisted as user preference) |
### Hardening (Phase C1–C5)
@@ -131,19 +131,19 @@ Multi-Provider-AI-Abstraktion + `MockAIProvider`.
### QA + Reconcile (Phase D)
| SHA | Summary |
| --------- | ------------------------------------------------------------------------ |
| `cd3b890` | refactor(v1.4.15): apply simplify-review safe suggestions |
| `6465412` | fix(a11y): trap focus + restore + ring on onboarding tour |
| `66d2e07` | fix(a11y): break long Withings error strings inside status banner |
| SHA | Summary |
| --------- | -------------------------------------------------------------------------- |
| `cd3b890` | refactor(v1.4.15): apply simplify-review safe suggestions |
| `6465412` | fix(a11y): trap focus + restore + ring on onboarding tour |
| `66d2e07` | fix(a11y): break long Withings error strings inside status banner |
| `79bb167` | fix(ai): MockAIProvider DEFAULT_RESPONSE conforms to v1.4.15 strict schema |
| `d947563` | style(v1.4.15): prettier sweep on phase-D reconcile touched files |
| `d947563` | style(v1.4.15): prettier sweep on phase-D reconcile touched files |
### Release (Phase E1–E3)
| SHA | Summary |
| --------- | ---------------------------------------- |
| `4dcaa08` | chore(release): v1.4.15 |
| SHA | Summary |
| --------- | ----------------------- |
| `4dcaa08` | chore(release): v1.4.15 |
---
@@ -206,7 +206,7 @@ fährt.
### Sonstige Process-Items
- Strict-Schema-Migration für AI-Insights-Route + UI (M6 senior);
- Strict-Schema-Migration für AI-Insights-Route + UI (M6 senior);
`.passthrough()` retiren.
- `dashboard-layout.test.ts` Typecheck-Regression aufräumen (in E1
bereits gefixt — bleibt aber als „pre-existing" in mehreren STATE-
+7 -8
View File
@@ -30,12 +30,12 @@ We need: deploy fires only when the image digest actually changes.
Coolify v4.0.0-beta.470 (current production) exposes these
deploy-trigger toggles on a `dockercompose` application:
| Setting | Effect | API access |
| ---------------- | ----------------------------------------------------------------- | ------------------- |
| Auto Deploy | GitHub-App webhook listens to push events | UI only (no MCP / REST endpoint exposes it on v4-beta) |
| Watch Paths | Glob list — push-event paths must match for auto-deploy to fire | UI only |
| Deploy Webhook | Bearer-protected `GET /api/v1/deploy?uuid=…` — programmatic trigger | MCP-callable |
| Notification Webhook | Outgoing POST when deploy completes (success / failure) | UI only |
| Setting | Effect | API access |
| -------------------- | ------------------------------------------------------------------- | ------------------------------------------------------ |
| Auto Deploy | GitHub-App webhook listens to push events | UI only (no MCP / REST endpoint exposes it on v4-beta) |
| Watch Paths | Glob list — push-event paths must match for auto-deploy to fire | UI only |
| Deploy Webhook | Bearer-protected `GET /api/v1/deploy?uuid=…` — programmatic trigger | MCP-callable |
| Notification Webhook | Outgoing POST when deploy completes (success / failure) | UI only |
The MCP `application.update` action exposed by `coolify-apps01`
intentionally restricts mutations to the safe subset
@@ -47,8 +47,7 @@ guardrails the MCP wraps around the install — too far outside agent
scope.
The Coolify community has an open feature request to expose
`auto_deploy_enabled` over REST; the v4-stable line (expected July
2026) is the realistic landing window.
`auto_deploy_enabled` over REST; the v4-stable line (expected July 2026) is the realistic landing window.
## Watchtower — re-examined
+90 -86
View File
@@ -21,18 +21,18 @@ B2 owns AI provider UX, B7 just shipped Export.
## 1. Inventory
| Slug | Route | Section component | Sidebar icon | i18n title key | i18n description key |
| ----------------- | ------------------------------------------- | -------------------------------------------------------- | --------------------- | ----------------------------------------- | ----------------------------------------------- |
| `account` | `/settings/account` | `account-section.tsx` (`<AccountSection>`) | `User` | `settings.sections.account.title` | `settings.sections.account.description` |
| `integrations` | `/settings/integrations` | `integrations-section.tsx` (`<IntegrationsSection>`) | `Link2` | `settings.sections.integrations.title` | `settings.sections.integrations.description` |
| `notifications` | `/settings/notifications` | `notifications-section.tsx` (`<NotificationsSection>`) | `Bell` | `settings.sections.notifications.title` | `settings.sections.notifications.description` |
| `dashboard` | `/settings/dashboard` | `dashboard-section.tsx` (`<DashboardSection>`) ⇢ `dashboard-layout-section.tsx` | `LayoutDashboard` | `settings.sections.dashboard.title` | `settings.sections.dashboard.description` |
| `thresholds` | `/settings/thresholds` | `thresholds-settings-section.tsx` (`<ThresholdsSettingsSection>`) ⇢ `thresholds-section.tsx` | `SlidersHorizontal` | `settings.sections.thresholds.title` | `settings.sections.thresholds.description` |
| `ai` (out of scope) | `/settings/ai` | `ai-section.tsx` (`<AiSection>`) | `Sparkles` | `settings.sections.ai.title` | `settings.sections.ai.description` |
| `api` | `/settings/api` | `api-section.tsx` (`<ApiSection>`) | `KeyRound` | `settings.sections.api.title` | `settings.sections.api.description` |
| `export` (out of scope) | `/settings/export` | `export-section.tsx` (`<ExportSection>`) | `Download` | `settings.sections.export.title` | `settings.sections.export.description` |
| `advanced` | `/settings/advanced` | `advanced-section.tsx` (`<AdvancedSection>`) | `Settings2` | `settings.sections.advanced.title` | `settings.sections.advanced.description` |
| `about` | `/settings/about` | `about-section.tsx` (`<AboutSection>`) | `Info` | `settings.sections.about.title` | `settings.sections.about.description` |
| Slug | Route | Section component | Sidebar icon | i18n title key | i18n description key |
| ----------------------- | ------------------------- | -------------------------------------------------------------------------------------------- | ------------------- | --------------------------------------- | --------------------------------------------- |
| `account` | `/settings/account` | `account-section.tsx` (`<AccountSection>`) | `User` | `settings.sections.account.title` | `settings.sections.account.description` |
| `integrations` | `/settings/integrations` | `integrations-section.tsx` (`<IntegrationsSection>`) | `Link2` | `settings.sections.integrations.title` | `settings.sections.integrations.description` |
| `notifications` | `/settings/notifications` | `notifications-section.tsx` (`<NotificationsSection>`) | `Bell` | `settings.sections.notifications.title` | `settings.sections.notifications.description` |
| `dashboard` | `/settings/dashboard` | `dashboard-section.tsx` (`<DashboardSection>`) ⇢ `dashboard-layout-section.tsx` | `LayoutDashboard` | `settings.sections.dashboard.title` | `settings.sections.dashboard.description` |
| `thresholds` | `/settings/thresholds` | `thresholds-settings-section.tsx` (`<ThresholdsSettingsSection>`) ⇢ `thresholds-section.tsx` | `SlidersHorizontal` | `settings.sections.thresholds.title` | `settings.sections.thresholds.description` |
| `ai` (out of scope) | `/settings/ai` | `ai-section.tsx` (`<AiSection>`) | `Sparkles` | `settings.sections.ai.title` | `settings.sections.ai.description` |
| `api` | `/settings/api` | `api-section.tsx` (`<ApiSection>`) | `KeyRound` | `settings.sections.api.title` | `settings.sections.api.description` |
| `export` (out of scope) | `/settings/export` | `export-section.tsx` (`<ExportSection>`) | `Download` | `settings.sections.export.title` | `settings.sections.export.description` |
| `advanced` | `/settings/advanced` | `advanced-section.tsx` (`<AdvancedSection>`) | `Settings2` | `settings.sections.advanced.title` | `settings.sections.advanced.description` |
| `about` | `/settings/about` | `about-section.tsx` (`<AboutSection>`) | `Info` | `settings.sections.about.title` | `settings.sections.about.description` |
The slug list lives at `src/components/settings/section-slugs.ts` (server-safe
constant) and is mirrored by the icon/title list at the top of
@@ -60,18 +60,18 @@ The `settings.*` namespace inside `messages/{en,de}.json` mixes three styles:
### 2.1 Section-component naming drift
| Component file | Default export | Comment |
| -------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------- |
| `account-section.tsx` | `AccountSection` | OK — slug + `-section.tsx` + `<SlugSection>` |
| `integrations-section.tsx` | `IntegrationsSection` | OK |
| `notifications-section.tsx` | `NotificationsSection` | OK |
| `dashboard-section.tsx` | `DashboardSection` | OK — but ALSO has `dashboard-layout-section.tsx` (`<DashboardLayoutSection>`) as the inner card. |
| `thresholds-settings-section.tsx` | `ThresholdsSettingsSection` | **Drift** — the slug is `thresholds`, the file is `thresholds-settings-section.tsx`, AND the inner card lives at `thresholds-section.tsx` (`<ThresholdsSection>`). The "settings" infix is redundant (everything in `src/components/settings/` is already a settings component). |
| `ai-section.tsx` | `AiSection` | OK (out of scope) |
| `api-section.tsx` | `ApiSection` | OK |
| `export-section.tsx` | `ExportSection` | OK (out of scope) |
| `advanced-section.tsx` | `AdvancedSection` | OK |
| `about-section.tsx` | `AboutSection` | OK |
| Component file | Default export | Comment |
| --------------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `account-section.tsx` | `AccountSection` | OK — slug + `-section.tsx` + `<SlugSection>` |
| `integrations-section.tsx` | `IntegrationsSection` | OK |
| `notifications-section.tsx` | `NotificationsSection` | OK |
| `dashboard-section.tsx` | `DashboardSection` | OK — but ALSO has `dashboard-layout-section.tsx` (`<DashboardLayoutSection>`) as the inner card. |
| `thresholds-settings-section.tsx` | `ThresholdsSettingsSection` | **Drift** — the slug is `thresholds`, the file is `thresholds-settings-section.tsx`, AND the inner card lives at `thresholds-section.tsx` (`<ThresholdsSection>`). The "settings" infix is redundant (everything in `src/components/settings/` is already a settings component). |
| `ai-section.tsx` | `AiSection` | OK (out of scope) |
| `api-section.tsx` | `ApiSection` | OK |
| `export-section.tsx` | `ExportSection` | OK (out of scope) |
| `advanced-section.tsx` | `AdvancedSection` | OK |
| `about-section.tsx` | `AboutSection` | OK |
**Decision (refactor commit 2):** the route-level wrapper is named after the
slug (`<SlugSection>`), the inner card keeps its own descriptive name
@@ -92,19 +92,20 @@ The historic flat namespace (`settings.profile`, `settings.passkeyName`, …)
predates the section split. Result: the same concept is reachable under two
different keys.
| Concept | Modern key | Legacy key still in code |
| ---------------------- | -------------------------------------------------------------- | ------------------------------------------------------ |
| Account page title | `settings.sections.account.title` | `settings.profile` *(card heading inside the page)* |
| AI page title | `settings.sections.ai.title` (= `settings.ai.title`) | `settings.kiInsights` (card heading) — duplicate |
| Withings card title | (none — only flat key) | `settings.withings` |
| Token actions | (none) | `settings.tokenRevoke`, `settings.tokenRevoked`, … |
| Doctor report | `settings.sections.export.cards.doctorReport.title` | `settings.doctorReport` (legacy) |
| Audit log | (none) | `settings.auditLog`, `settings.auditAction`, … |
| Danger zone | (none) | `settings.dangerZone`, `settings.dangerZoneTitle`, … |
| moodLog card title | (none) | `settings.moodLogTitle` |
| Notifications channels | (none) | `settings.telegram`, `settings.ntfy`, `settings.webPush` |
| Concept | Modern key | Legacy key still in code |
| ---------------------- | ---------------------------------------------------- | -------------------------------------------------------- |
| Account page title | `settings.sections.account.title` | `settings.profile` _(card heading inside the page)_ |
| AI page title | `settings.sections.ai.title` (= `settings.ai.title`) | `settings.kiInsights` (card heading) — duplicate |
| Withings card title | (none — only flat key) | `settings.withings` |
| Token actions | (none) | `settings.tokenRevoke`, `settings.tokenRevoked`, … |
| Doctor report | `settings.sections.export.cards.doctorReport.title` | `settings.doctorReport` (legacy) |
| Audit log | (none) | `settings.auditLog`, `settings.auditAction`, … |
| Danger zone | (none) | `settings.dangerZone`, `settings.dangerZoneTitle`, … |
| moodLog card title | (none) | `settings.moodLogTitle` |
| Notifications channels | (none) | `settings.telegram`, `settings.ntfy`, `settings.webPush` |
**Decision (refactor commit 2):**
- Keep `settings.sections.<slug>.{title,description}` as the canonical
source for `<h1>` + subtitle. No change needed.
- Introduce `settings.sections.<slug>.cards.<cardName>.title` /
@@ -117,18 +118,18 @@ different keys.
### 2.3 Anti-pattern: section title vs sidebar label vs document `<title>`
| Slug | Sidebar label (DE) | Page `<h1>` (DE) | Browser `<title>` | Match? |
| ------------- | --------------------- | ----------------------- | ---------------------------- | ------ |
| account | "Konto" | "Konto" | (Next.js inherits root) | yes |
| integrations | "Integrationen" | "Integrationen" | " | yes |
| notifications | "Benachrichtigungen" | "Benachrichtigungen" | " | yes |
| dashboard | "Dashboard" | "Dashboard" | " | yes |
| thresholds | "Persönliche Zielwerte" | "Persönliche Zielwerte" | " | yes |
| ai | "KI-Auswertungen" | "KI-Insights" (legacy `settings.ai.title` = "KI-Insights") | " | **mismatch** — sidebar says "Auswertungen", page heading says "Insights" |
| api | "API & Tokens" | "API & Tokens" | " | yes |
| export | "Export" | "Export" | " | yes |
| advanced | "Erweitert" | "Erweitert" | " | yes |
| about | "Über" | "Über" | " | yes |
| Slug | Sidebar label (DE) | Page `<h1>` (DE) | Browser `<title>` | Match? |
| ------------- | ----------------------- | ---------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------ |
| account | "Konto" | "Konto" | (Next.js inherits root) | yes |
| integrations | "Integrationen" | "Integrationen" | " | yes |
| notifications | "Benachrichtigungen" | "Benachrichtigungen" | " | yes |
| dashboard | "Dashboard" | "Dashboard" | " | yes |
| thresholds | "Persönliche Zielwerte" | "Persönliche Zielwerte" | " | yes |
| ai | "KI-Auswertungen" | "KI-Insights" (legacy `settings.ai.title` = "KI-Insights") | " | **mismatch** — sidebar says "Auswertungen", page heading says "Insights" |
| api | "API & Tokens" | "API & Tokens" | " | yes |
| export | "Export" | "Export" | " | yes |
| advanced | "Erweitert" | "Erweitert" | " | yes |
| about | "Über" | "Über" | " | yes |
The AI mismatch is **out of B6 scope** — B2 owns the AI section. Flagged
for B2.
@@ -143,18 +144,18 @@ Per Marc's `feedback_settings_no_split.md`, there must be no instance where
a single concept is selected at the top of a section and configured at the
bottom. Audit:
| Section | Top-bottom split? | Notes |
| ------------------ | ----------------- | ------------------------------------------------------------------------------------ |
| Account | No | One profile card, one passkey card, one password card, one tour card. Sequential, not split. |
| Integrations | No | Withings + moodLog cards, each self-contained. |
| Notifications | **Borderline** | `<NotificationStatusCard>` lists per-channel status at the top; the per-channel config cards (`<TelegramCard>`, `<NtfyCard>`, `<WebPushCard>`) follow below. The status card has `Re-enable` + `Send test` actions per channel that arguably duplicate functionality in the config cards. **Not a top/bottom-split-of-one-concept** — the status card is a read-only summary, the config cards are mutators. Keeping it. |
| Dashboard | No | One layout card. |
| Thresholds | No | One editor card with per-metric rows. |
| AI | **YES (out of scope, B2)** | Provider dropdown at the top selects between OpenAI/Anthropic/Local; key/credential inputs for each provider live further down. This is the case Marc named explicitly. |
| API | No | Endpoints reference card + tokens card. |
| Export | No | 5 cards, each self-contained. |
| Advanced | No | Single danger-zone card (post-B7 cleanup). |
| About | No | Identity + sources + updates cards. |
| Section | Top-bottom split? | Notes |
| ------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Account | No | One profile card, one passkey card, one password card, one tour card. Sequential, not split. |
| Integrations | No | Withings + moodLog cards, each self-contained. |
| Notifications | **Borderline** | `<NotificationStatusCard>` lists per-channel status at the top; the per-channel config cards (`<TelegramCard>`, `<NtfyCard>`, `<WebPushCard>`) follow below. The status card has `Re-enable` + `Send test` actions per channel that arguably duplicate functionality in the config cards. **Not a top/bottom-split-of-one-concept** — the status card is a read-only summary, the config cards are mutators. Keeping it. |
| Dashboard | No | One layout card. |
| Thresholds | No | One editor card with per-metric rows. |
| AI | **YES (out of scope, B2)** | Provider dropdown at the top selects between OpenAI/Anthropic/Local; key/credential inputs for each provider live further down. This is the case Marc named explicitly. |
| API | No | Endpoints reference card + tokens card. |
| Export | No | 5 cards, each self-contained. |
| Advanced | No | Single danger-zone card (post-B7 cleanup). |
| About | No | Identity + sources + updates cards. |
**Decision:** AI flagged for B2 (already in roadmap). All other sections
are clean. Notifications kept as-is — the read-only-status-then-mutator
@@ -162,14 +163,14 @@ pattern is a deliberate choice, not a split anti-pattern.
### 2.5 Duplicate functionality between sections
| Concept | Owner section | Duplicated where? | Action |
| ----------------------- | ---------------------- | ------------------------------------------------------------------------------ | --------------------------------------- |
| Doctor-report PDF | Export (canonical) | Used to live in Advanced as `<ExportCard>`. **Already removed in B7.** | None. |
| CSV/JSON export buttons | Export (canonical) | Used to live in Advanced. **Already removed in B7.** | None. |
| Language selector | Account ⇢ profile card | NONE. | OK. |
| Channel test send | Notifications ⇢ status card | Each individual channel card (`<TelegramCard>`, etc.) ALSO has a test-send button. | Document — not removing; the status card surfaces it for failed channels (after auto-disable), the config cards have it as a "did I get the credentials right" smoke test. Different intents. |
| Withings credentials | Integrations ⇢ Withings card | NONE. | OK. |
| User-level settings vs admin-level settings | mostly disjoint | `settings.adminAreaTitle` / `settings.openAdminConsole` exist as keys but are **not currently rendered anywhere** in any settings section component. Stale. | Mark `settings.adminAreaTitle` etc. as deprecated in JSON. |
| Concept | Owner section | Duplicated where? | Action |
| ------------------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Doctor-report PDF | Export (canonical) | Used to live in Advanced as `<ExportCard>`. **Already removed in B7.** | None. |
| CSV/JSON export buttons | Export (canonical) | Used to live in Advanced. **Already removed in B7.** | None. |
| Language selector | Account ⇢ profile card | NONE. | OK. |
| Channel test send | Notifications ⇢ status card | Each individual channel card (`<TelegramCard>`, etc.) ALSO has a test-send button. | Document — not removing; the status card surfaces it for failed channels (after auto-disable), the config cards have it as a "did I get the credentials right" smoke test. Different intents. |
| Withings credentials | Integrations ⇢ Withings card | NONE. | OK. |
| User-level settings vs admin-level settings | mostly disjoint | `settings.adminAreaTitle` / `settings.openAdminConsole` exist as keys but are **not currently rendered anywhere** in any settings section component. Stale. | Mark `settings.adminAreaTitle` etc. as deprecated in JSON. |
### 2.6 Settings vs Admin: scope rule
@@ -196,14 +197,14 @@ in the user settings page that never shipped. **Marking deprecated.**
The following toggles or behaviours exist in the data layer but have no
end-user-visible setting today:
| Feature | Where it lives | Should it have a setting? |
| ---------------------------------------- | --------------------------------------- | -------------------------------------------- |
| Onboarding-tour replay | already shipped in `<AccountSection>` (v1.4.15 B5) | already done |
| Achievements opt-out | server-side `<UserAchievement>` table | low priority — defer |
| AI raw-data toggle (`rawData`) | already in `<AiSection>` (out of scope) | already done |
| Personal medical-references override | server-side `users.medicalReferences` | defer (B5a referenced this — not yet user-tunable) |
| Per-metric reminder schedule | `Medication.schedule` | covered by medication editor, not settings |
| Locale fallback ("auto-detect off") | `i18n/config.ts` | low priority — defer |
| Feature | Where it lives | Should it have a setting? |
| ------------------------------------ | -------------------------------------------------- | -------------------------------------------------- |
| Onboarding-tour replay | already shipped in `<AccountSection>` (v1.4.15 B5) | already done |
| Achievements opt-out | server-side `<UserAchievement>` table | low priority — defer |
| AI raw-data toggle (`rawData`) | already in `<AiSection>` (out of scope) | already done |
| Personal medical-references override | server-side `users.medicalReferences` | defer (B5a referenced this — not yet user-tunable) |
| Per-metric reminder schedule | `Medication.schedule` | covered by medication editor, not settings |
| Locale fallback ("auto-detect off") | `i18n/config.ts` | low priority — defer |
Nothing is shipped-without-UI that should be exposed in B6. The "deferred"
items belong in v1.5 product roadmap, not this audit.
@@ -231,23 +232,23 @@ Spot-checked:
Marc asked: "are settings grouped sensibly?". Current order in the
sidebar:
1. Account — personal identity + sign-in
2. Integrations — third-party data sources
3. Notifications — per-channel delivery
4. Dashboard — UI layout
5. Thresholds — target ranges (used by charts + insights)
6. AI — analysis provider
7. API — bearer tokens for headless clients
8. Export — data out
9. Advanced — danger zone
10. About — version + links
1. Account — personal identity + sign-in
2. Integrations — third-party data sources
3. Notifications — per-channel delivery
4. Dashboard — UI layout
5. Thresholds — target ranges (used by charts + insights)
6. AI — analysis provider
7. API — bearer tokens for headless clients
8. Export — data out
9. Advanced — danger zone
10. About — version + links
This order roughly reads as: **identity → data flow in → output
configuration → automation → data flow out → housekeeping → meta**.
Defensible. No re-order required.
One concept worth highlighting: **Thresholds + Dashboard are both UI
configuration**, but Thresholds also drives *insight* + *AI* outputs.
configuration**, but Thresholds also drives _insight_ + _AI_ outputs.
Putting Thresholds between Dashboard and AI keeps it close to both
concerns. Leaving as-is.
@@ -258,6 +259,7 @@ concerns. Leaving as-is.
Commit 1 (this commit) — `docs(audit): v1.4.16 settings naming + consolidation audit`
Commit 2 — `refactor(settings): consistent naming + i18n key namespace`
- Rename `thresholds-settings-section.tsx` → `thresholds-section.tsx`
(route-level wrapper).
- Rename existing inner card from `thresholds-section.tsx` →
@@ -277,6 +279,7 @@ Commit 2 — `refactor(settings): consistent naming + i18n key namespace`
until a follow-up hygiene PR.
Commit 3 — `refactor(settings): remove duplicate toggles, route to canonical owner`
- Document the settings-vs-admin scope rule in `CLAUDE.md`.
- No actual duplicate found that needs removing today; commit body
documents the audit conclusion ("no duplicate user-facing toggles
@@ -285,6 +288,7 @@ Commit 3 — `refactor(settings): remove duplicate toggles, route to canonical o
deprecated").
Commit 4 — `test(settings): coverage for renamed/consolidated sections`
- Update `src/components/settings/__tests__/sections.test.tsx` to
import the renamed thresholds component and assert the new file
layout.
+54 -54
View File
@@ -58,51 +58,51 @@ How It Works, Comparison Views, Provider Configuration.
### Wave A — Quick fixes
| Bucket | Commit | Subject |
| ------ | ------- | ------------------------------------------------------------------------------------------------------ |
| A1 | `77fe256` | fix(nav): admin sub-items don't expand from gravatar dropdown or admin-link click |
| A2 | `577d8dd` | fix(insights): BD-Zielbereich computes correctly for real measurement data |
| A3 | `277a5aa` | fix(admin): api-tokens table no horizontal overflow on Pixel-5 viewport |
| A4 | `4df6dac` | feat(charts): "7-Tage-Trend" rename + slope30 fallback for sparse metrics |
| A5 | `93e712d` | fix(dashboard): top-tile selector persists (widget-id enum drift fixed) |
| A6 | `9b01c86` | feat(charts): medication chart matches other charts (7d trend + indicator + target-range) |
| Bucket | Commit | Subject |
| ------ | ------------------ | ------------------------------------------------------------------------------------------ |
| A1 | `77fe256` | fix(nav): admin sub-items don't expand from gravatar dropdown or admin-link click |
| A2 | `577d8dd` | fix(insights): BD-Zielbereich computes correctly for real measurement data |
| A3 | `277a5aa` | fix(admin): api-tokens table no horizontal overflow on Pixel-5 viewport |
| A4 | `4df6dac` | feat(charts): "7-Tage-Trend" rename + slope30 fallback for sparse metrics |
| A5 | `93e712d` | fix(dashboard): top-tile selector persists (widget-id enum drift fixed) |
| A6 | `9b01c86` | feat(charts): medication chart matches other charts (7d trend + indicator + target-range) |
| A7 | (within `9b01c86`) | feat(insights): rate-limit 10/h + cache-evict on regenerate (cross-agent race captured A7) |
| A8a | `2da0703` | fix(geo): preserve umlauts in city names (login-overview no longer renders "Nrnberg") |
| A8b | `af77e5e` | feat(charts): split-half mean delta on long-window trend |
| A8a | `2da0703` | fix(geo): preserve umlauts in city names (login-overview no longer renders "Nrnberg") |
| A8b | `af77e5e` | feat(charts): split-half mean delta on long-window trend |
### Wave B — Quality-leap features
| Bucket | Headline commits | What it does |
| ------ | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| B1a | `74c2eb8`, `901f44e`, `8008613`, `2611bb4` | BP / weight / pulse / mood / medication chart polish |
| B1b | `4d7d074`, `9e8be4b`, `b76351e`, `0c287f1`, `d2cdf9d`, `5063ad7`, `3b0d21e` | `/insights` hero + recs grid + dashboard preview |
| B2 | (worktree `agent/b2-ai-provider-ux` ff) | Single-pulldown Settings → AI + chain editor |
| B3 | `5d1ece1`, `f1bd801`, `2877710`, (chart inside `8d9f864`) | Admin host-load chart (in-process sampler, 7d) |
| B4 | (audit-log inside `8ac5602`), `4cc3d8d`, `6520ae4` | Admin audit-log filter + CSV + app-log preview |
| B5a | `27f3933`, `466b8b5`, `7fbfca6`, `53aade9`, `a66c128` | Curated medical-reference bundle + citation footnote |
| B5b | `2611bb4` (column), runner inside `901f44e`, `613d661`, `d2bda42` | Multi-provider fallback chain + last-working cache |
| B5c | `8a438a0`, `c39a527`, `13f1ae5`, `c8b30c1`, `7f54c0c`, `680f84c`, `10a67ff`, `fed2e7e` | Per-rec rationale (window / comparedTo / deviation) |
| B5d | `0cb0373`, `af21d4d`, `7ec1030`, `63cfd8e`, `d343ab5`, `173c3e1` | Deterministic confidence meter (server-overrides) |
| B5e | `badc893`, `8255e3e`, `ca84fb7`, `47ca1e4`, `71ffe30`, `8879282` | RecommendationFeedback + daily aggregator + admin |
| B6 | `01a05e4`, `a432cb2`, `ed0cfda`, `d914f76` | Settings naming audit + i18n parity guard |
| B7 | `621109c`, `a512650`, `94c748d`, (routes inside `226cac4`), `d5c8912`, `830b2b0`, `e628f33` | Settings → Export consolidation |
| B8 | `775df8c`, `2cf7b74`, `e4a408e`, `f9f99ea` | Comparison overlay on charts + tiles + AI narration |
| Bucket | Headline commits | What it does |
| ------ | ------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| B1a | `74c2eb8`, `901f44e`, `8008613`, `2611bb4` | BP / weight / pulse / mood / medication chart polish |
| B1b | `4d7d074`, `9e8be4b`, `b76351e`, `0c287f1`, `d2cdf9d`, `5063ad7`, `3b0d21e` | `/insights` hero + recs grid + dashboard preview |
| B2 | (worktree `agent/b2-ai-provider-ux` ff) | Single-pulldown Settings → AI + chain editor |
| B3 | `5d1ece1`, `f1bd801`, `2877710`, (chart inside `8d9f864`) | Admin host-load chart (in-process sampler, 7d) |
| B4 | (audit-log inside `8ac5602`), `4cc3d8d`, `6520ae4` | Admin audit-log filter + CSV + app-log preview |
| B5a | `27f3933`, `466b8b5`, `7fbfca6`, `53aade9`, `a66c128` | Curated medical-reference bundle + citation footnote |
| B5b | `2611bb4` (column), runner inside `901f44e`, `613d661`, `d2bda42` | Multi-provider fallback chain + last-working cache |
| B5c | `8a438a0`, `c39a527`, `13f1ae5`, `c8b30c1`, `7f54c0c`, `680f84c`, `10a67ff`, `fed2e7e` | Per-rec rationale (window / comparedTo / deviation) |
| B5d | `0cb0373`, `af21d4d`, `7ec1030`, `63cfd8e`, `d343ab5`, `173c3e1` | Deterministic confidence meter (server-overrides) |
| B5e | `badc893`, `8255e3e`, `ca84fb7`, `47ca1e4`, `71ffe30`, `8879282` | RecommendationFeedback + daily aggregator + admin |
| B6 | `01a05e4`, `a432cb2`, `ed0cfda`, `d914f76` | Settings naming audit + i18n parity guard |
| B7 | `621109c`, `a512650`, `94c748d`, (routes inside `226cac4`), `d5c8912`, `830b2b0`, `e628f33` | Settings → Export consolidation |
| B8 | `775df8c`, `2cf7b74`, `e4a408e`, `f9f99ea` | Comparison overlay on charts + tiles + AI narration |
### Wave C — Catch-up (deferred from v1.4.15)
| Item | Commit | Notes |
| ------------------------------------- | --------- | ---------------------------------------------------------------- |
| CI fix (encryption-key + tour-overlay) | `fbcd106` | Both pre-existing red since `d8c549e`; both green from here on |
| H1 admin restore-failed scrub | `fdac9e2` | code-review HIGH |
| H2 moodEntry.tags JSON-array refine | `7f1a4de` | code-review HIGH |
| H4 tour-launcher per-user sessionKey | `2afe3c4` | code-review HIGH |
| H4 design 44 px tap targets sweep | `b863e2c` | tour / backups / notifications |
| i18n parity Quelle / Leitlinie | `2a7ef72` | B5a follow-on |
| MED tabs horizontal scroll | `d7c2b2a` | |
| MED bottom-nav 5+More | `072eee6` | overflow sheet |
| MED system-status retry button | `65b4bf9` | |
| Auto-deploy v1.5 doc | `4be6465` | Coolify auto-deploy stays on git-push for v1.4.16; deferred |
| docker-publish drop qemu-arm64 | `cc0f343` | qemu-SIGILL root cause; native-runner matrix in v1.5 |
| Item | Commit | Notes |
| -------------------------------------- | --------- | -------------------------------------------------------------- |
| CI fix (encryption-key + tour-overlay) | `fbcd106` | Both pre-existing red since `d8c549e`; both green from here on |
| H1 admin restore-failed scrub | `fdac9e2` | code-review HIGH |
| H2 moodEntry.tags JSON-array refine | `7f1a4de` | code-review HIGH |
| H4 tour-launcher per-user sessionKey | `2afe3c4` | code-review HIGH |
| H4 design 44 px tap targets sweep | `b863e2c` | tour / backups / notifications |
| i18n parity Quelle / Leitlinie | `2a7ef72` | B5a follow-on |
| MED tabs horizontal scroll | `d7c2b2a` | |
| MED bottom-nav 5+More | `072eee6` | overflow sheet |
| MED system-status retry button | `65b4bf9` | |
| Auto-deploy v1.5 doc | `4be6465` | Coolify auto-deploy stays on git-push for v1.4.16; deferred |
| docker-publish drop qemu-arm64 | `cc0f343` | qemu-SIGILL root cause; native-runner matrix in v1.5 |
5 of 8 deferred HIGH from v1.4.15 Wave-D landed; 3 of 5 deferred MED
from A5 mobile landed; Coolify image-digest auto-deploy deferred per
@@ -124,20 +124,20 @@ Marc's "leave it" call.
### Wave E — Release
| Item | Result |
| ------------------------------------------- | ----------------------------------------------------- |
| Pre-release verify | `pnpm typecheck` clean; `pnpm test` 1540/1540; integration 59/59 |
| Release commit | `d443c22 chore(release): v1.4.16` |
| Tag | `v1.4.16` (annotated) |
| GHCR — tag run | `25616783583` success, digest `2f1a0d6b381d…` |
| GHCR — main run | `25616782255` success, digest `4841bef396ad…` |
| Coolify auto-deploy | NO — same race as v1.4.14/v1.4.15; retag-on-host fallback used |
| Live image digest | `sha256:05f8a126d63962d9a4af4769de830d3fee022d634787e811b4339ee464420daa` |
| `/api/version` transition | `1.4.15` → `1.4.16` at `2026-05-10T03:45:58+02:00` |
| Smoke (14 routes, Marc's session) | 13/14 200; `/dashboard` 404 expected (root is dashboard) |
| GH release | https://github.com/MBombeck/HealthLog/releases/tag/v1.4.16 |
| Docs site | `8addef4` + `2a5802b` on `healthlog-docs/main` (44 pages, three new deep-dive pages) |
| Landing site | `3d17207` on `healthlog-landing/main` (1.4.15 → 1.4.16, AI cards updated) |
| Item | Result |
| --------------------------------- | ------------------------------------------------------------------------------------ |
| Pre-release verify | `pnpm typecheck` clean; `pnpm test` 1540/1540; integration 59/59 |
| Release commit | `d443c22 chore(release): v1.4.16` |
| Tag | `v1.4.16` (annotated) |
| GHCR — tag run | `25616783583` success, digest `2f1a0d6b381d…` |
| GHCR — main run | `25616782255` success, digest `4841bef396ad…` |
| Coolify auto-deploy | NO — same race as v1.4.14/v1.4.15; retag-on-host fallback used |
| Live image digest | `sha256:05f8a126d63962d9a4af4769de830d3fee022d634787e811b4339ee464420daa` |
| `/api/version` transition | `1.4.15` → `1.4.16` at `2026-05-10T03:45:58+02:00` |
| Smoke (14 routes, Marc's session) | 13/14 200; `/dashboard` 404 expected (root is dashboard) |
| GH release | https://github.com/MBombeck/HealthLog/releases/tag/v1.4.16 |
| Docs site | `8addef4` + `2a5802b` on `healthlog-docs/main` (44 pages, three new deep-dive pages) |
| Landing site | `3d17207` on `healthlog-landing/main` (1.4.15 → 1.4.16, AI cards updated) |
---
@@ -212,7 +212,7 @@ Full backlog: `.planning/v15-backlog.md`. Highlights:
first time since v1.4.13). Coolify auto-deploy fired on the
`chore(release)` commit before GHCR finished and pulled the host's
stale local `:latest` cache; retag-on-host fallback (`docker pull
:1.4.16 && docker tag :1.4.16 :latest && docker compose up -d app`)
:1.4.16 && docker tag :1.4.16 :latest && docker compose up -d app`)
flipped `/api/version` 1 s after the up command returned.
- **Coolify auto-deploy**: still fires on every git-push (deferred
per Marc's call); v1.5 plan in `docs/audit/v1416-auto-deploy-fix.md`.
@@ -236,7 +236,7 @@ Full backlog: `.planning/v15-backlog.md`. Highlights:
- `/settings/ai-providers/` — setup paths for all five providers,
fallback-chain editor, test-connection, credential storage table.
- **healthlog-landing** (Next.js) — `softwareVersion 1.4.15 →
1.4.16` in JSON-LD; AI-insights hero card rewritten as "AI insights
1.4.16` in JSON-LD; AI-insights hero card rewritten as "AI insights
that show their work" leading with explainability + confidence +
citations; capability badges row picks up two new badges.