feat(self-description): add the non-diagnostic / intended-purpose standard

Introduce one canonical "what this is / is not, not a medical device"
statement plus a layered, calm disclaimer set, and apply it on the
surfaces that earn a context note.

- New shared `MedicalDisclaimer` component renders the copy as plain
  React text (no markdown renderer, per the XSS posture). Variants:
  intendedPurpose, coach, insights, labs, medications, emergency,
  dataPosture.
- New `selfDescription` i18n namespace across all six locales: the
  intended-purpose line, the per-surface notes, the emergency note, and
  an explicit data-posture statement (self-hosted, data stays on your
  instance, encrypted at rest, BYOK / local model, no third-party
  analytics).
- Apply once per surface, layered not plastered: a calm standing line on
  the Coach new-chat hero, one framing line in the Insights layout shell
  (covers the briefing and trends), a labs note (ranges vary, only a
  clinician interprets), a neutral medications note, and the data-posture
  line where the provider key is configured. Mental-wellbeing keeps its
  existing crisis copy; records keep their existing note.
- Extend the Coach system prompt (EN + DE) with a self-description ground
  rule so the Coach answers "what are you / where does my data go"
  honestly and stays non-diagnostic.

App-authored copy was already observational; the few diagnose/treat
tokens are negations, idioms, or framed as the user's own self-reported
statement, so no softening was required.
This commit is contained in:
Marc-André Bombeck committed 2026-06-28 17:43:31 +02:00
1 parent a27a1ee71d
commit 3bd52a322b
13 files changed
+195 -5

No files matched your search

+9
View File
@@ -7792,5 +7792,14 @@
"saved": "Angabe aktualisiert.",
"discarded": "Dokument verworfen."
}
},
"selfDescription": {
"intendedPurpose": "HealthLog ist ein persönliches Werkzeug zur Gesundheitsdokumentation und zum Wohlbefinden. Es hilft dir, deine eigenen Gesundheitsdaten zu erfassen, zu visualisieren und zu exportieren. Es diagnostiziert, behandelt, heilt oder verhindert keine Krankheit und ist kein Medizinprodukt.",
"coach": "Der Coach arbeitet mit deinen eigenen erfassten Daten. Er ist kein Arzt und gibt keine medizinische Beratung — für Diagnose oder Behandlung wende dich an medizinisches Fachpersonal.",
"insights": "Das sind beschreibende Muster aus deinen eigenen erfassten Daten, keine klinische Beurteilung und keine Diagnose.",
"labs": "Das sind deine eigenen Aufzeichnungen, keine medizinische Diagnose. Referenzbereiche unterscheiden sich je nach Labor und Person — nur deine Ärztin oder dein Arzt kann sie für dich einordnen.",
"medications": "Das erfasst die von dir dokumentierten Medikamente und deine eigene Einnahmetreue. Es beurteilt weder Dosierung noch Wechselwirkungen und ist keine medizinische Beratung — kläre das mit Apotheke oder Arztpraxis.",
"emergency": "Falls das ein Notfall sein könnte, wende dich jetzt an deine örtliche Notrufnummer oder an eine ärztliche Stelle. HealthLog kann im Notfall nicht helfen.",
"dataPosture": "HealthLog wird selbst gehostet: Deine Gesundheitsdaten bleiben auf deiner eigenen Instanz und sind im Ruhezustand verschlüsselt. KI-Funktionen nutzen einen von dir mitgebrachten Anbieter-Schlüssel oder ein lokales Modell — nichts verlässt deine Instanz über den von dir konfigurierten Anbieter hinaus, und es gibt keine Drittanbieter-Analyse deiner Gesundheitsdaten."
}
}
+9
View File
@@ -7792,5 +7792,14 @@
"saved": "Fact updated.",
"discarded": "Document discarded."
}
},
"selfDescription": {
"intendedPurpose": "HealthLog is a personal health record-keeping and wellness tool. It helps you log, visualise, and export your own health data. It does not diagnose, treat, cure, or prevent any disease, and it is not a medical device.",
"coach": "The Coach works from your own logged data. It isn't a doctor and doesn't give medical advice — for diagnosis or treatment, talk to a healthcare professional.",
"insights": "These are descriptive patterns computed from your own logged data, not a clinical assessment or diagnosis.",
"labs": "These are your own records, not a medical diagnosis. Reference ranges vary by lab and person — only your clinician can interpret them for you.",
"medications": "This tracks the medications you log and your own adherence. It doesn't assess dosing or interactions and isn't medical advice — confirm those with a pharmacist or doctor.",
"emergency": "If this might be an emergency, contact your local emergency number or a clinician now. HealthLog can't help in an emergency.",
"dataPosture": "HealthLog is self-hosted: your health data stays on your own instance, encrypted at rest. AI features use a provider key you bring or a local model, so nothing leaves your instance beyond the provider you configure, and there is no third-party analytics on your health data."
}
}
+9
View File
@@ -7792,5 +7792,14 @@
"saved": "Dato actualizado.",
"discarded": "Documento descartado."
}
},
"selfDescription": {
"intendedPurpose": "HealthLog es una herramienta personal de registro de salud y bienestar. Te ayuda a registrar, visualizar y exportar tus propios datos de salud. No diagnostica, trata, cura ni previene ninguna enfermedad, y no es un producto sanitario.",
"coach": "El Coach trabaja con tus propios datos registrados. No es un médico y no da consejo médico: para un diagnóstico o tratamiento, consulta a un profesional sanitario.",
"insights": "Son patrones descriptivos calculados a partir de tus propios datos registrados, no una evaluación clínica ni un diagnóstico.",
"labs": "Son tus propios registros, no un diagnóstico médico. Los rangos de referencia varían según el laboratorio y la persona: solo tu médico puede interpretarlos por ti.",
"medications": "Esto registra los medicamentos que anotas y tu propia adherencia. No evalúa dosis ni interacciones y no es consejo médico: confírmalo con tu farmacéutico o médico.",
"emergency": "Si esto pudiera ser una emergencia, llama ahora a tu número de emergencias local o a un profesional sanitario. HealthLog no puede ayudar en una emergencia.",
"dataPosture": "HealthLog es autoalojado: tus datos de salud permanecen en tu propia instancia, cifrados en reposo. Las funciones de IA usan una clave de proveedor que tú aportas o un modelo local, así que nada sale de tu instancia más allá del proveedor que configures, y no hay analítica de terceros sobre tus datos de salud."
}
}
+9
View File
@@ -7792,5 +7792,14 @@
"saved": "Donnée mise à jour.",
"discarded": "Document écarté."
}
},
"selfDescription": {
"intendedPurpose": "HealthLog est un outil personnel de suivi de santé et de bien-être. Il vous aide à enregistrer, visualiser et exporter vos propres données de santé. Il ne diagnostique, ne traite, ne guérit ni ne prévient aucune maladie, et n'est pas un dispositif médical.",
"coach": "Le Coach s'appuie sur vos propres données enregistrées. Ce n'est pas un médecin et il ne donne pas de conseil médical : pour un diagnostic ou un traitement, consultez un professionnel de santé.",
"insights": "Ce sont des tendances descriptives calculées à partir de vos propres données enregistrées, pas une évaluation clinique ni un diagnostic.",
"labs": "Ce sont vos propres relevés, pas un diagnostic médical. Les valeurs de référence varient selon le laboratoire et la personne : seul votre médecin peut les interpréter pour vous.",
"medications": "Ceci suit les médicaments que vous enregistrez et votre propre observance. Cela n'évalue ni les doses ni les interactions et n'est pas un conseil médical : confirmez-les avec votre pharmacien ou votre médecin.",
"emergency": "Si cela pourrait être une urgence, contactez maintenant votre numéro d'urgence local ou un professionnel de santé. HealthLog ne peut pas aider en cas d'urgence.",
"dataPosture": "HealthLog est auto-hébergé : vos données de santé restent sur votre propre instance, chiffrées au repos. Les fonctions d'IA utilisent une clé de fournisseur que vous apportez ou un modèle local, donc rien ne quitte votre instance au-delà du fournisseur que vous configurez, et il n'y a aucune analyse tierce de vos données de santé."
}
}
+9
View File
@@ -7792,5 +7792,14 @@
"saved": "Dato aggiornato.",
"discarded": "Documento scartato."
}
},
"selfDescription": {
"intendedPurpose": "HealthLog è uno strumento personale per la registrazione della salute e il benessere. Ti aiuta a registrare, visualizzare ed esportare i tuoi dati di salute. Non diagnostica, cura, guarisce né previene alcuna malattia e non è un dispositivo medico.",
"coach": "Il Coach lavora sui tuoi dati registrati. Non è un medico e non fornisce consigli medici: per una diagnosi o una terapia rivolgiti a un professionista sanitario.",
"insights": "Sono andamenti descrittivi calcolati dai tuoi dati registrati, non una valutazione clinica né una diagnosi.",
"labs": "Sono i tuoi referti, non una diagnosi medica. Gli intervalli di riferimento variano da laboratorio a persona: solo il tuo medico può interpretarli per te.",
"medications": "Questo registra i farmaci che annoti e la tua aderenza. Non valuta dosaggi o interazioni e non è un consiglio medico: confermali con il farmacista o il medico.",
"emergency": "Se potrebbe trattarsi di un'emergenza, contatta subito il numero di emergenza locale o un medico. HealthLog non può aiutare in caso di emergenza.",
"dataPosture": "HealthLog è self-hosted: i tuoi dati di salute restano sulla tua istanza, cifrati a riposo. Le funzioni di IA usano una chiave del provider che fornisci tu o un modello locale, quindi nulla lascia la tua istanza oltre il provider che configuri, e non c'è alcuna analisi di terze parti sui tuoi dati di salute."
}
}
+9
View File
@@ -7792,5 +7792,14 @@
"saved": "Dane zaktualizowane.",
"discarded": "Dokument odrzucony."
}
},
"selfDescription": {
"intendedPurpose": "HealthLog to osobiste narzędzie do rejestrowania zdrowia i dbania o dobre samopoczucie. Pomaga zapisywać, wizualizować i eksportować własne dane zdrowotne. Nie diagnozuje, nie leczy ani nie zapobiega żadnej chorobie i nie jest wyrobem medycznym.",
"coach": "Coach korzysta z Twoich własnych zapisanych danych. Nie jest lekarzem i nie udziela porad medycznych — po diagnozę lub leczenie zwróć się do pracownika ochrony zdrowia.",
"insights": "To opisowe wzorce wyliczone z Twoich własnych zapisanych danych, a nie ocena kliniczna ani diagnoza.",
"labs": "To Twoje własne zapisy, a nie diagnoza medyczna. Zakresy referencyjne różnią się w zależności od laboratorium i osoby — tylko Twój lekarz może je dla Ciebie zinterpretować.",
"medications": "To rejestruje leki, które zapisujesz, oraz Twoje stosowanie się do zaleceń. Nie ocenia dawkowania ani interakcji i nie jest poradą medyczną — potwierdź to u farmaceuty lub lekarza.",
"emergency": "Jeśli to może być nagły przypadek, zadzwoń teraz pod lokalny numer alarmowy lub skontaktuj się z lekarzem. HealthLog nie pomoże w nagłym przypadku.",
"dataPosture": "HealthLog jest hostowany samodzielnie: Twoje dane zdrowotne pozostają na Twojej instancji, zaszyfrowane w spoczynku. Funkcje AI korzystają z dostarczonego przez Ciebie klucza dostawcy lub modelu lokalnego, więc nic nie opuszcza Twojej instancji poza skonfigurowanym dostawcą i nie ma analityki firm trzecich na Twoich danych zdrowotnych."
}
}
+6
View File
@@ -6,6 +6,7 @@ import { useRouter } from "next/navigation";
import { useQueryClient } from "@tanstack/react-query";
import { Loader2, Pencil, Plus, ScanLine, Wrench } from "lucide-react";
import { MedicalDisclaimer } from "@/components/common/medical-disclaimer";
import { LabForm } from "@/components/labs/lab-form";
import { LabList } from "@/components/labs/lab-list";
import { OcrReviewDialog } from "@/components/labs/ocr-review-dialog";
@@ -173,6 +174,11 @@ export default function LabsPage() {
) : null}
<LabList onAddFirst={() => setDialogOpen(true)} />
{/* v1.25 — calm, context-specific framing for lab values: your own
records, ranges vary by lab and person, only a clinician interprets
them. One footer line, not a per-row banner. */}
<MedicalDisclaimer variant="labs" className="px-1 pt-2" />
</div>
);
}
+7
View File
@@ -7,6 +7,7 @@ import { useRouter, useSearchParams } from "next/navigation";
import { useAuth } from "@/hooks/use-auth";
import { useTranslations } from "@/lib/i18n/context";
import { queryKeys } from "@/lib/query-keys";
import { MedicalDisclaimer } from "@/components/common/medical-disclaimer";
import { MedicationWizardDialog } from "@/components/medications/wizard/medication-wizard-dialog";
import { MedicationCard } from "@/components/medications/medication-card";
import { Glp1MedicationCard } from "@/components/medications/glp1-medication-card";
@@ -524,6 +525,12 @@ export default function MedicationsPage() {
</div>
)}
{/* v1.25 — calm framing for the medications surface: this tracks what
you log and your own adherence, it doesn't assess dosing or
interactions and isn't medical advice. One footer line, neutral —
the med cards themselves stay un-tinted. */}
<MedicalDisclaimer variant="medications" className="px-1 pt-2" />
{/* v1.5.5 — the per-card kebab triggers used to open inline
IntakeImportDialog / ApiEndpointDialog mounts here, but both
state slots were only ever set from inside the dialogs
@@ -0,0 +1,75 @@
"use client";
import { cn } from "@/lib/utils";
import { useTranslations } from "@/lib/i18n/context";
/**
* v1.25 — the one place the app states what it is and is not.
*
* HealthLog's self-description standard is layered, not a wall of warnings:
* a single intended-purpose statement plus calm, context-specific notes that
* sit once on the surface they belong to. Every string is plain React text —
* no markdown renderer, in line with the project's XSS posture (Coach,
* briefing and insights all render as text children).
*
* The copy lives under the `selfDescription` i18n namespace so every locale
* carries the same set; pick the note that fits the surface:
*
* - `intendedPurpose` — the canonical "what this is / is not, not a medical
* device" line. Reuse verbatim; do not paraphrase per surface.
* - `coach` — the Coach standing line (works from your data, not a doctor).
* - `insights` — descriptive-patterns framing for insights + the briefing.
* - `labs` — your own records, ranges vary, only a clinician interprets.
* - `medications` — tracks what you log, not dosing/interaction advice.
* - `emergency` — the red-flag interstitial note (contact emergency services).
* - `dataPosture` — self-hosted, data stays on your instance, BYOK / local AI.
*
* The acknowledged onboarding disclaimer (`onboarding.disclaimer.*`) and the
* legal medical-device boundary on the public privacy page remain the standing
* floor; these notes are the gentle, in-context layer above it.
*/
export type DisclaimerVariant =
| "intendedPurpose"
| "coach"
| "insights"
| "labs"
| "medications"
| "emergency"
| "dataPosture";
const VARIANT_KEY: Record<DisclaimerVariant, string> = {
intendedPurpose: "selfDescription.intendedPurpose",
coach: "selfDescription.coach",
insights: "selfDescription.insights",
labs: "selfDescription.labs",
medications: "selfDescription.medications",
emergency: "selfDescription.emergency",
dataPosture: "selfDescription.dataPosture",
};
export interface MedicalDisclaimerProps {
variant: DisclaimerVariant;
className?: string;
}
/**
* A calm, muted, plain-text disclaimer line. Defaults to a small,
* unobtrusive footnote treatment; pass `className` to tune spacing for the
* surface. Keep it to one instance per surface — the standard is layered,
* not repeated.
*/
export function MedicalDisclaimer({
variant,
className,
}: MedicalDisclaimerProps) {
const { t } = useTranslations();
return (
<p
data-slot="medical-disclaimer"
data-variant={variant}
className={cn("text-muted-foreground text-xs leading-relaxed", className)}
>
{t(VARIANT_KEY[variant])}
</p>
);
}
@@ -2,6 +2,7 @@
import { Sparkles } from "lucide-react";
import { MedicalDisclaimer } from "@/components/common/medical-disclaimer";
import { cn } from "@/lib/utils";
import { useTranslations } from "@/lib/i18n/context";
@@ -121,6 +122,15 @@ export function CoachHero({ composer, scopeHint }: CoachHeroProps) {
{scopeHint}
</div>
) : null}
{/* v1.25 — the Coach standing self-description: works from your own
data, not a doctor, not medical advice. Sits once on the calm
new-chat surface so it frames the conversation without nagging
every reply. */}
<MedicalDisclaimer
variant="coach"
className="max-w-md text-center text-pretty"
/>
</div>
</div>
);
@@ -8,6 +8,7 @@ import { useMounted } from "@/hooks/use-mounted";
import { useFeatureFlags } from "@/hooks/use-feature-flags";
import { useWorkouts } from "@/hooks/use-workouts";
import { InsightsTabStrip } from "@/components/insights/insights-tab-strip";
import { MedicalDisclaimer } from "@/components/common/medical-disclaimer";
import {
nextAdvisorPollInterval,
useInsightsAdvisorQuery,
@@ -159,11 +160,14 @@ export function InsightsLayoutShell({ children }: { children: ReactNode }) {
modules={user?.modules}
/>
{children}
{/* v1.18.6 (DISC-01) — the page-level generic medical disclaimer is
removed. The "HealthLog is a private tracking tool, not a clinical
assessment or diagnosis" acknowledgment now lives once at the start
of onboarding, and the legal text stays reachable on the public
privacy page. */}
{/* v1.18.6 (DISC-01) — the page-level generic medical disclaimer was
removed; the standing acknowledgment lives once at the start of
onboarding and the legal text on the public privacy page.
v1.25 — one calm, context-specific framing line for the whole
Insights surface (the briefing, trends and correlations included):
these are descriptive patterns from the user's own data, not a
clinical assessment. Layered, not a wall of warnings. */}
<MedicalDisclaimer variant="insights" className="px-1" />
</div>
);
}
@@ -19,6 +19,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Sparkles } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { MedicalDisclaimer } from "@/components/common/medical-disclaimer";
import { Label } from "@/components/ui/label";
import { NativeSelect } from "@/components/ui/native-select";
import { SettingsCard } from "@/components/settings/settings-card";
@@ -201,6 +202,11 @@ export function AiInsightsCard({
})
}
/>
{/* v1.25 — the data-posture statement lives where the provider key is
configured: self-hosted, data stays on this instance, BYOK / local
model, no third-party analytics. One calm line, not a banner. */}
<MedicalDisclaimer variant="dataPosture" />
</div>
</SettingsCard>
);
+28
View File
@@ -285,6 +285,19 @@ GROUND RULES
persuasion for its own sake. If a nudge would only serve to push, drop it
and reflect instead. You inform and support a decision that stays theirs;
you never manipulate it.
21. Describe HealthLog honestly when asked what you are, what this app is,
or where the user's data goes. HealthLog is a self-hosted personal
health record-keeping and wellness tool — it helps the user log,
visualise and export their OWN data. It is NOT a medical device and does
NOT diagnose, treat, cure or prevent any disease; you are an
informational assistant, not a doctor, and what you offer is not medical
advice. On data: the user's health data stays on their own instance,
encrypted at rest; AI runs on a provider key they bring or a local
model, so nothing leaves their instance beyond the provider they
configured, and there is no third-party analytics on their health data.
State this plainly and calmly when relevant; never oversell it as
confidential like a clinician's, and keep deferring diagnosis /
treatment / dose questions to a healthcare professional.
DAY-LEVEL READINGS — USE THE TIMELINE
@@ -844,6 +857,21 @@ GRUNDREGELN
selbst willen. Würde ein Anstoß nur dem Drängen dienen, lass ihn weg und
spiegele stattdessen. Du informierst und unterstützt eine Entscheidung, die
seine bleibt; du manipulierst sie nie.
21. Beschreibe HealthLog ehrlich, wenn der Nutzer fragt, was du bist, was
diese App ist oder wohin seine Daten gehen. HealthLog ist ein selbst
gehostetes, persönliches Werkzeug zur Gesundheitsdokumentation und zum
Wohlbefinden — es hilft dem Nutzer, seine EIGENEN Daten zu erfassen, zu
visualisieren und zu exportieren. Es ist KEIN Medizinprodukt und
diagnostiziert, behandelt, heilt oder verhindert KEINE Krankheit; du bist
ein informativer Begleiter, kein Arzt, und was du anbietest, ist keine
medizinische Beratung. Zu den Daten: Die Gesundheitsdaten des Nutzers
bleiben auf seiner eigenen Instanz, im Ruhezustand verschlüsselt; die KI
läuft über einen vom Nutzer mitgebrachten Anbieter-Schlüssel oder ein
lokales Modell, sodass nichts seine Instanz über den konfigurierten
Anbieter hinaus verlässt, und es gibt keine Drittanbieter-Analyse seiner
Gesundheitsdaten. Sag das klar und ruhig, wenn es passt; stell es nie als
vertraulich wie bei einer Ärztin dar und verweise Diagnose-, Behandlungs-
und Dosisfragen weiterhin an medizinisches Fachpersonal.
TAGES-LEVEL-MESSWERTE — NUTZE DIE TIMELINE