feat(insights): fold structured mood tags into the tag takeaways

The structured taxonomy tags carry a separate, richer signal than the
free-text tag list: a curated catalog pick with a label key and category,
attached to a mood entry through the tag-link join. The tag-mood takeaway
builder only ever read the flat tags, so the structured tags never reached
the ranked lift/drop pool despite both call sites already passing them.

Merge both axes into one ranked pool. A structured candidate emits its
catalog label key as tagKey, which the feed resolves to the active locale
the same way the weekday key already resolves; flat tags keep their
verbatim label. The lean status snapshot now loads the structured-tag
links too, so the takeaways the model sees match the ones on screen.
This commit is contained in:
Marc-André Bombeck committed 2026-06-01 23:43:39 +02:00
1 parent d1e6befa5c
commit 38867439f3
5 files changed
+161 -12

No files matched your search

@@ -34,6 +34,22 @@ describe("<MoodNarrativeFeed>", () => {
expect(html).toContain('data-slot="mood-narrative-feed"');
});
it("resolves a structured tag key into the sentence", () => {
const items: MoodNarrativeItem[] = [
{
kind: "tag-lift",
messageKey: "insights.mood.narrative.tagLift",
vars: { tagKey: "charts.weekdaysFull.fri", delta: "0.6" },
},
];
// `charts.weekdaysFull.fri` stands in for a catalog label key — the
// renderer must resolve `tagKey` the same way it resolves `weekdayKey`.
const html = render(<MoodNarrativeFeed items={items} />);
expect(html).toContain("Friday");
expect(html).not.toContain("charts.weekdaysFull.fri");
expect(html).toContain("0.6");
});
it("renders one row per takeaway in order", () => {
const items: MoodNarrativeItem[] = [
{
@@ -70,9 +70,12 @@ export function MoodNarrativeFeed({ items }: { items: MoodNarrativeItem[] }) {
{items.map((item, index) => {
const Icon = pickIcon(item);
// The weekday key is itself an i18n key — resolve it before
// interpolating so the sentence reads in the active locale.
// interpolating so the sentence reads in the active locale. A
// structured tag→mood takeaway carries its catalog `tagKey`
// (an i18n key) the same way; flat tags supply `tag` verbatim.
const vars: Record<string, string> = { ...item.vars };
if (vars.weekdayKey) vars.weekday = t(vars.weekdayKey);
if (vars.tagKey) vars.tag = t(vars.tagKey);
return (
<li
key={`${item.kind}-${index}`}