feat(public-mood): Stimmungs-Kachel aus Foren-Quellen

Eigene Pipeline-Stufe nach factcheck, vor summary, die Foren-Artikel
(media_type='forum') zu einer Themen-Zusammenfassung verarbeitet. Wird als
separate Dashboard-Kachel "Öffentliche Stimmung" angezeigt — getrennt von
Lagebild und Faktencheck, damit anonyme Forenposts nicht mit belegter
Faktenlage verwechselt werden.

- DB-Migration: incidents.public_mood (TEXT) + public_mood_updated_at (TS).
- pipeline_tracker: neuer Pipeline-Step "public_mood" (DE/EN-Labels).
- analyzer.generate_public_mood: Haiku-Call der Foren-Beitraege pro Quelle
  gruppiert und 3-6 thematische Bullets erzeugt, mit expliziter Quellen-
  Herkunft pro Bullet. Bei zu duennem Material gibt's keinen Output.
- orchestrator: neuer Schritt zwischen Factcheck und Summary. Laedt alle
  Foren-Artikel der Lage (via JOIN auf sources), uebergibt sie an den
  Stimmungs-Agent, speichert den Markdown-Text in incidents.public_mood.
- Topic-Filter (analyzer.filter_relevant_articles) markiert Foren-Quellen
  mit [FORUM]-Tag und bekommt im Prompt die Regel, Foren-Artikel weicher
  zu bewerten (Lage-Keyword im Titel reicht). Sie sollen in der Stimmungs-
  Kachel landen, nicht voreilig verworfen werden.
- IncidentResponse-Modell: public_mood/public_mood_updated_at ergaenzt.
- Frontend: neuer Tab "Öffentliche Stimmung" (nur sichtbar wenn Inhalt da),
  eigene Kachel mit Warn-Hinweis "keine Faktenlage". UI.renderPublicMood
  als einfacher Bullet-Renderer.
- dashboard.html Cache-Buster fuer components.js + app.js gebumpt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
2026-05-22 00:20:17 +02:00
Ursprung 379d14518c
Commit d65f0180d9
8 geänderte Dateien mit 243 neuen und 3 gelöschten Zeilen

Datei anzeigen

@@ -209,6 +209,7 @@
<button class="tab-btn" data-tab="timeline" data-i18n="tab.timeline">Ereignis-Timeline</button>
<button class="tab-btn" data-tab="karte" data-i18n="tab.map">Geografische Verteilung</button>
<button class="tab-btn" data-tab="faktencheck" data-i18n="tab.factcheck">Faktencheck</button>
<button class="tab-btn" data-tab="stimmung" data-i18n="tab.public_mood" id="tab-btn-stimmung" style="display:none;">Öffentliche Stimmung</button>
<button class="tab-btn" data-tab="pipeline" data-i18n="tab.pipeline">Analysepipeline</button>
<button class="tab-btn" data-tab="quellen" data-i18n="tab.sources_overview">Quellenübersicht</button>
</div>
@@ -293,6 +294,24 @@
</div>
</div>
<div class="tab-panel" id="panel-stimmung">
<div class="card incident-analysis-stimmung" id="stimmung-card">
<div class="card-header">
<div class="card-title">
<span data-i18n="card.public_mood">Öffentliche Stimmung</span>
<span class="info-icon" data-tooltip="Themen und Bruchlinien aus Foren-Quellen (z.B. 5ch, Hatena, Note).&#10;&#10;KEINE Faktenlage - reines Stimmungsmaterial.&#10;Beitraege sind anonym und koennen Trolling enthalten."><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></span>
</div>
<span class="stimmung-timestamp" id="stimmung-timestamp"></span>
</div>
<div id="stimmung-content">
<div id="stimmung-text" class="summary-text" style="padding:8px 16px;"></div>
<div style="padding:0 16px 16px; font-size:11px; color:var(--text-disabled); border-top:1px solid var(--border); margin-top:8px; padding-top:8px;">
Hinweis: Forenbeiträge sind anonyme Online-Stimmungen, keine Faktenlage. Sie fließen nicht in den Faktencheck ein.
</div>
</div>
</div>
</div>
<div class="tab-panel" id="panel-pipeline">
<div class="card pipeline-card" id="pipeline-card">
<div class="card-header">
@@ -778,10 +797,10 @@
<script src="/static/js/i18n.js?v=20260513a"></script>
<script src="/static/js/api.js?v=20260423a"></script>
<script src="/static/js/ws.js?v=20260316b"></script>
<script src="/static/js/components.js?v=20260514e"></script>
<script src="/static/js/components.js?v=20260522a"></script>
<script src="/static/js/layout.js?v=20260513f"></script>
<script src="/static/js/pipeline.js?v=20260513d"></script>
<script src="/static/js/app.js?v=20260514e"></script>
<script src="/static/js/app.js?v=20260522a"></script>
<script src="/static/js/cluster-data.js?v=20260322f"></script>
<script src="/static/js/tutorial.js?v=20260316z"></script>
<script src="/static/js/chat.js?v=20260514e"></script>