feat(mood): revise factor + tag catalog (migration 0126)

Tighten the operator-finalised mood catalog to three RATED factors and a
trimmed default BINARY tag set.

- Seed `factor_sadness` (RATED, 1..5, inverse — higher = worse) under the
  `factors` category, with its label in all six locales and a CloudRain
  glyph. The RATED set is now work / sleep quality / sadness.
- Retire `factor_conflict`, `factor_family`, `factor_social`,
  `factor_stress`: flip each to BINARY and deactivate. The catalog rows
  stay so historical entries still resolve their labels (the insights read
  joins by id without an active filter); they just leave the RATED set and
  the default picker.
- Trim the default BINARY tag set roughly in half: deactivate the
  sleep-tag category (now covered by the sleep-quality slider), the mood
  level restatements, the nutrition long-tail, and low-signal day-type
  tags. Kept rows are the high-signal, correlatable tags. Nothing is
  deleted; inactive rows still resolve labels for existing entries.

Reference-data only — no schema change, idempotent upsert + closed-key
updates, no per-user data touched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.8 committed 2026-06-05 15:28:47 +02:00
1 parent fac2ebeca1
commit a3e741c23c
8 files changed
+86 -6

No files matched your search

+2 -1
View File
@@ -577,7 +577,8 @@
"factorSleepQuality": "Schlafqualität",
"factorStress": "Stress",
"factorConflict": "Konflikt",
"factorFamily": "Familie"
"factorFamily": "Familie",
"factorSadness": "Traurigkeit"
}
},
"medications": {
+2 -1
View File
@@ -577,7 +577,8 @@
"factorSleepQuality": "sleep quality",
"factorStress": "stress",
"factorConflict": "conflict",
"factorFamily": "family"
"factorFamily": "family",
"factorSadness": "sadness"
}
},
"medications": {
+2 -1
View File
@@ -577,7 +577,8 @@
"factorSleepQuality": "calidad del sueño",
"factorStress": "estrés",
"factorConflict": "conflicto",
"factorFamily": "familia"
"factorFamily": "familia",
"factorSadness": "tristeza"
}
},
"medications": {
+2 -1
View File
@@ -577,7 +577,8 @@
"factorSleepQuality": "qualité du sommeil",
"factorStress": "stress",
"factorConflict": "conflit",
"factorFamily": "famille"
"factorFamily": "famille",
"factorSadness": "tristesse"
}
},
"medications": {
+2 -1
View File
@@ -577,7 +577,8 @@
"factorSleepQuality": "qualità del sonno",
"factorStress": "stress",
"factorConflict": "conflitto",
"factorFamily": "famiglia"
"factorFamily": "famiglia",
"factorSadness": "tristezza"
}
},
"medications": {
+2 -1
View File
@@ -577,7 +577,8 @@
"factorSleepQuality": "jakość snu",
"factorStress": "stres",
"factorConflict": "konflikt",
"factorFamily": "rodzina"
"factorFamily": "rodzina",
"factorSadness": "smutek"
}
},
"medications": {
@@ -0,0 +1,72 @@
-- v1.12.9 — mood-catalog revision.
--
-- Tightens the operator-finalised mood factor + tag catalog seeded by
-- 0118 / 0119 / 0120. Three changes, all reference-data only — no schema
-- change, no per-user data touched, no destructive delete:
--
-- 1. Seed a new RATED factor `factor_sadness` (scale 1..5, inverse —
-- higher = a worse day), under the existing `factors` category.
-- 2. Retire the four RATED factors the operator dropped
-- (`factor_conflict`, `factor_family`, `factor_social`,
-- `factor_stress`): flip each to `kind = 'BINARY'` AND set
-- `is_active = false`. They leave the RATED set and the default
-- picker, but the catalog rows stay so any historical
-- `mood_entry_tag_links` row still resolves its label (the insights
-- read joins the catalog row by id and does NOT filter `is_active`).
-- The result: exactly THREE RATED factors remain
-- (`factor_work`, `factor_sleep_quality`, `factor_sadness`).
-- 3. Trim the default BINARY tag set roughly in half: mark the
-- long-tail / slider-redundant tags `is_active = false`. The picker
-- (`GET /api/mood/tags` filters `is_active = true`) stops offering
-- them; existing entries that reference a now-inactive tag still
-- resolve its label because the read joins by id without the active
-- filter. Nothing is deleted.
--
-- Idempotent: the seed uses a deterministic id + ON CONFLICT upsert by
-- `key` (mirrors 0118 / 0119 / 0120); the retire / trim updates key off a
-- closed `key` list, so a re-run is a no-op.
-- ─── 1. New RATED factor: sadness (inverse, 1..5) ────────────────────
INSERT INTO "mood_tags"
("id", "category_id", "key", "label_key", "icon", "sort_order", "is_active", "kind", "scale_min", "scale_max", "inverse")
VALUES ('mt_factor_sadness', 'mtc_factors', 'factor_sadness', 'mood.tag.factorSadness', 'CloudRain', 2, true, 'RATED', 1, 5, true)
ON CONFLICT ("key") DO UPDATE SET
"category_id" = EXCLUDED."category_id", "label_key" = EXCLUDED."label_key", "icon" = EXCLUDED."icon",
"sort_order" = EXCLUDED."sort_order", "is_active" = EXCLUDED."is_active", "kind" = EXCLUDED."kind",
"scale_min" = EXCLUDED."scale_min", "scale_max" = EXCLUDED."scale_max", "inverse" = EXCLUDED."inverse";
-- Re-seat the two surviving RATED factors at the top of the `factors`
-- category so the final order reads work → sleep quality → sadness.
UPDATE "mood_tags" SET "sort_order" = 0 WHERE "key" = 'factor_work';
UPDATE "mood_tags" SET "sort_order" = 1 WHERE "key" = 'factor_sleep_quality';
-- ─── 2. Retire the four dropped RATED factors (BINARY + inactive) ─────
-- Flip kind so they leave the RATED set; deactivate so they leave the
-- default picker. Historical RATED links keep their `rating` value and
-- still resolve their label (read joins the catalog row by id, no active
-- filter). Not deleted — references stay valid.
UPDATE "mood_tags"
SET "kind" = 'BINARY', "is_active" = false
WHERE "key" IN ('factor_conflict', 'factor_family', 'factor_social', 'factor_stress');
-- ─── 3. Trim the default BINARY tag set (~half) ──────────────────────
-- Deactivate the long-tail / slider-redundant binary tags. Kept set is
-- the high-signal, plausibly-correlatable tags; dropped set is sleep
-- tags now covered by the Schlafqualität slider, mood-level restatements
-- (the 1..5 face already carries them), and low-signal duplicates. Rows
-- stay so historical entries still resolve their labels.
UPDATE "mood_tags"
SET "is_active" = false
WHERE "key" IN (
-- feelings: drop tags that just restate the 5-face mood level / overlap
-- the surviving factor_sadness; keep a tight high-signal subset.
'content', 'unsure', 'bored', 'tense', 'stressed', 'sad',
-- sleep: the Schlafqualität (factor_sleep_quality) slider supersedes
-- the whole sleep-tag category.
'slept_well', 'slept_ok', 'slept_poorly', 'early_night',
-- health: drop the nutrition long-tail (kept: ate_well, alcohol,
-- worked_out, walked, hydrated).
'fast_food', 'no_sweets', 'big_meal',
-- work: drop low-signal day-type tags (kept: productive, day_off).
'overtime', 'travel', 'sick_day'
);
+2
View File
@@ -10,6 +10,7 @@ import {
CheckCircle,
Clock,
CloudMoon,
CloudRain,
CloudSun,
Dumbbell,
Film,
@@ -66,6 +67,7 @@ const MOOD_TAG_ICONS: Record<string, LucideIcon> = {
CheckCircle,
Clock,
CloudMoon,
CloudRain,
CloudSun,
Dumbbell,
Film,