fix(glp1): match side-effect tags by key, not by an English/German word list

The GLP-1 side-effect chips in the mood form write the localised label into
the flat `MoodEntry.tags` column, and three readers matched that label against
a hand-written list of English and German words: the therapy timeline, the
Coach snapshot, and the doctor report's GLP-1 block. The app ships six locales.
Tap the nausea chip with the UI in French, Spanish, Italian or Polish and the
day never reached the timeline, the Coach grounded its answers on an empty
side-effect list, and a clinician read an empty table over a record that had
three months of symptoms. Nothing on any of those surfaces said the table was a
filter rather than the truth. The three lists had also drifted apart from each
other.

The chips are a closed set with stable keys, so matching the label was the
defect and the key is the fix. `glp1-side-effect-tags.ts` holds the catalogue —
seven keys, each with the i18n key its chip renders — and the mood form now
draws its strip from it, so the write side and the read side cannot diverge
again. `glp1-side-effect-tag-match.ts` builds the label-to-key index by reading
`messages/<locale>.json` for every entry in `locales`, rather than transcribing
the labels into a second list. Transcribing would have fixed today's four
locales and reopened the same hole on the day a seventh is added; deriving
means a new bundle is matchable as soon as it lands, with no edit in the
matcher. Comparison folds case, accents and separators, so the English and
German tags already in the database keep resolving and a straight apostrophe
matches the typographic one the French bundle uses.

All three readers now report the catalogue key instead of the stored string.
The same recorded symptom yields one key whatever language it was tagged in,
which also fixes the mixed-language output an account gets after switching UI
locale; the doctor-report PDF resolves the key through the label map so the
clinician reads it in the report's language.

A tag outside the catalogue is still never counted — a free-text mood tag must
not reach a doctor as an invented symptom, and the rest of somebody's day does
not belong on a therapy timeline. It is no longer dropped without trace: the
timeline route annotates the unresolved count on its wide event, so the
classification rate stays answerable while the tag text stays in the database.

The Coach's copy of the list also carried `vomiting`, `reflux` and `erbrechen`.
Those are removed. No chip writes them, so they only ever matched a hand-typed
tag in two of six languages; keeping them would have rebuilt the defect inside
its own fix. Restoring either symptom means giving it a chip and a label in all
six bundles.

The response schema for `GET /api/insights/glp1-timeline` now enumerates the
keys, and its description no longer documents the bilingual match as if it were
the contract.
This commit is contained in:
Marc-André Bombeck committed 2026-08-23 13:56:32 +02:00
1 parent b76468f455
commit 97d0c26ceb
14 files changed
+833 -167

No files matched your search

+15 -3
View File
@@ -50680,12 +50680,24 @@ components:
reason:
type: string
tags:
description: The recognised side-effect tags picked up that day. Matched against a FIXED bilingual list of English and
German tag strings, so a tag written in any other language — or spelled differently — does not appear
here at all.
description: "Catalogue keys of the side effects tagged that day, deduplicated. These are STABLE KEYS, not the strings
the entries were written with: the capture chip writes its label in the writer's language, and the
matcher indexes every shipped locale's label back to the key, so the same recorded symptom yields the
same key whether it was tagged in German, English, Spanish, French, Italian or Polish. Render the key
in the reader's own language rather than echoing it. A mood tag that is not a catalogue side effect —
anything the user typed freehand — is not reported here; it is not a side effect, and a therapy
timeline is not the place for the rest of somebody's day."
type: array
items:
type: string
enum:
- nausea
- constipation
- diarrhea
- fatigue
- appetite-loss
- heartburn
- headache
required:
- date
- kind