Aenderung am Grund-Mechanismus: LLM liefert pro Bullet die Meldungs-IDs
im Format {M<ID>, M<ID>}, das Backend loest die IDs gegen new_articles
zu Quellen-Namen auf und schreibt {Reuters, Rybar} in die DB. Uebernommene
Bullets aus previous_developments behalten ihre bestehende {Name}-Klammer.
Bullets ohne Quellen-Klammer oder mit unaufloesbarer Klammer werden vom
Parser verworfen — dadurch existiert "Keine Quelle" nicht mehr.
Frontend: Bias-Farbcodierung (pro-RU, staatsnah) + zugehoerige Heuristik
_classifyBias/_biasLabel entfernt. Kein Sonderfall-Rendering fuer leere
Pills mehr.
Der LATEST_DEVELOPMENTS-Prompt produzierte Bullets ohne Citations — das
Frontend zeigte daher "Keine Quelle". Prompt ergaenzt: jedes Bullet endet mit
{Quellenname1, Quellenname2} (geschweifte Klammern, exakte Schreibweise aus
Quelle:-Zeile). Frontend-Parser extrahiert diese Klammer, matcht Namen
case-insensitive gegen sources_json und erstellt klickbare Pills.
Fallback fuer Legacy-Bullets: Inline-[N]-Citations werden weiterhin erkannt.
Altbestand-Bullets ohne Marker erhalten beim naechsten Refresh Quellen.
Der Bullet-Render fuer Live-Monitoring (adhoc) zeigt nun pro Eintrag eine
Karte mit klickbaren Quellen-Pills (Quellname statt nur [N]) im Header und
dezentem Zeitstempel rechts oben. Der Ereignistext steht darunter ohne
Inline-Citations. Bias-Markierung (pro-RU, staatsnah) als kleines Suffix.
Recherchen behalten den bisherigen renderZusammenfassung-Render unveraendert.
Drei unabhaengige Schutzschichten gegen falsche Umschreibungen
(ae/oe/ue/ss statt ä/ö/ü/ß) im Lagebild:
1. Prompt-Ergaenzung in INCREMENTAL_ANALYSIS_PROMPT_TEMPLATE und
INCREMENTAL_BRIEFING_PROMPT_TEMPLATE (analyzer.py): explizite
Priorisierung, dass die Regel "echte UTF-8-Umlaute" Vorrang vor
"bestehende Formulierungen beibehalten" hat. Adressiert den Fall,
dass Claude beim inkrementellen Update Altlasten weitertraegt.
2. Deterministische Normalisierung in post_refresh_qc.py:
- normalize_german_umlauts(text) - Regex mit Wortgrenzen, case-
preserving, Whitelist-tauglich, ~140 Eintraege im Woerterbuch
abgeleitet aus den 140 Hard-Hits in Lage #6
- normalize_umlaut_fields(db, incident_id) - laedt summary und
latest_developments, normalisiert, schreibt nur bei Aenderungen
zurueck (idempotent)
- Eingehaengt in run_post_refresh_qc() nach dem Location-Check,
Fehler stoppen die Pipeline nicht (identisches Muster wie
bestehende Checks)
3. scripts/bootstrap_umlaut_repair.py - Einmal-Skript zur
Bestandsbereinigung der bereits gespeicherten summary-Felder.
Idempotent. Beim initialen Lauf auf Produktiv-DB: 14 Lagen
aktualisiert, 431 Ersetzungen insgesamt, Lage #6 von 140 auf
15 Rest-Treffer reduziert.
Whitelist (leer): aktuell kein Konflikt zwischen deutschen Ziel-
Woertern und englischen Fremdwoertern. Kann bei Bedarf erweitert
werden ohne Schema-Aenderung.
Verifikation:
- py_compile OK fuer alle drei Dateien
- Service-Restart ohne Errors
- Unit-Tests: positive Faelle ("Oeffnung der Strasse" -> 4 Ersetzungen),
Whitelist ("Boeing liefert Business-Access" -> 0 Ersetzungen),
Komposita ("Wasserstrasse", "Parlamentspraesident") korrekt
- Bootstrap 2x ausgefuehrt (erster Lauf 288 Ersetzungen, zweiter 143
nach Dict-Erweiterung), kumulativ 431
Architektur bleibt dormant ohne Daten-Altlasten: wenn keine Lage
Umschreibungen enthaelt, arbeitet normalize_umlaut_fields in <1ms
und schreibt nichts. Kein Overhead im Refresh-Pfad.
Der geplante YouTube-Captions-Fallback (Phase 2 via yt-dlp) wird nicht
umgesetzt. Begruendung: strategische Entscheidung, keinen YouTube-Scrape
als Quelle zu nutzen.
Geaendert:
- src/feeds/transcript_extractors/__init__.py:
- try/except-Import fuer youtube-Modul entfernt (nie existiert)
- Modul-Docstring aktualisiert (Stufen 1+2, kein 3)
- source-Enum-Kommentar: nur noch rss_native / website_scrape
Konsequenz: Episoden, die weder Podcasting-2.0-Tag noch Sender-Manuskript
haben (z. B. Paywall-Inhalte bei FAZ/Handelsblatt), werden dauerhaft
verworfen. Fuer deutsche Qualitaetsmedien-Podcasts (Dlf, NDR, SZ, Spiegel,
ZEIT wo frei) reichen die zwei aktiven Stufen.
- DB-Migration: Spalte latest_developments (TEXT) in incidents
- Analyzer: neuer Prompt LATEST_DEVELOPMENTS_PROMPT_TEMPLATE und
Methode generate_latest_developments() liefert chronologische
Bullet-Liste (max. 8, neueste oben, Zeitstempel DD.MM. HH:MM)
- Orchestrator: nach Analyse+Faktencheck ein Extra-Schritt nur fuer
incident_type=adhoc, der die neue Kachel fortschreibt
- Analyzer-Prompts (Erst- und inkrementell): erzeugen KEINE
Zusammenfassung-Sektion mehr im Lagebild (vermeidet Duplikat mit
der neuen Kachel)
- models.IncidentResponse um latest_developments erweitert
- Frontend: Rendering der Kachel in app.js
Zusammenfassung-Content-Container bekommt overflow-y: auto und
Scrollbar-Styling analog zu Lagebild, Faktencheck und Timeline.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Seite 2: Dynamisches Inhaltsverzeichnis mit klickbaren Anker-Links
- Nur ausgewählte Bereiche erscheinen im Verzeichnis (CSS Counter)
- Jeder Abschnitt beginnt auf neuer Seite (page-break-before)
- Redundante Inline-Styles für Seitenumbrüche entfernt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Alle sichtbaren "Executive Summary"-Bezeichnungen durch "Zusammenfassung" ersetzt
(PDF/DOCX-Überschrift, Dateiname, Fallback-Texte)
- Deckblatt-Farben von #888/#aaa auf Navy #0a1832 geändert für
bessere Lesbarkeit beim Druck (PDF-Template + DOCX)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Frontend + Backend erkennen jetzt sowohl ## ZUSAMMENFASSUNG als
auch ## ÜBERBLICK als Zusammenfassungs-Sektion
- Inkrementelles Prompt weist Modell an, ÜBERBLICK in
ZUSAMMENFASSUNG umzubenennen und als Bullet-Points zu formatieren
- Bestehende Lagen zeigen Zusammenfassung sofort in der Kachel
Research-Lagen: ZUSAMMENFASSUNG-Sektion wird aus dem Bericht
extrahiert und in eigener Kachel oberhalb des Recherchberichts
angezeigt. Der Recherchebericht zeigt den Rest ohne Dopplung.
- Neue Kachel mit gs-id="zusammenfassung" im GridStack
- Toggle-Button in der Layout-Leiste
- extractZusammenfassung() und renderZusammenfassung() in UI
- Adhoc/Live-Lagen: Kachel wird automatisch ausgeblendet
- Export nutzt weiterhin _extract_zusammenfassung() aus dem Backend
Wenn ein Fall aus der Queue entfernt wird (Cancel, Fehler, Abschluss),
bleiben die #-Nummern der verbleibenden Eintraege jetzt nicht mehr
stecken. _reindexQueuePositions() sortiert nach alter Position und
nummeriert sequentiell neu (#1, #2, ...).
Aufgerufen in: handleRefreshCancelled, handleRefreshError,
handleRefreshComplete.
Bisher war Cancel kooperativ (Flag-basiert) -- der Code pruefte das Flag
nur an wenigen Checkpoints. Laufende Claude CLI Subprozesse (WebSearch,
Analyse, Faktencheck) liefen bis zum Ende weiter, was minutenlanges
Warten beim Abbrechen verursachte.
Neuer Ansatz:
- ContextVar _cancel_event_var in claude_client.py
- Orchestrator setzt asyncio.Event vor jedem Refresh
- call_claude wartet parallel auf Prozess UND cancel_event
- Bei Cancel: process.kill() + CancelledError sofort
- Kein Durchreichen durch Agent-Methoden noetig (contextvars)
Beide Prompts (Research + Adhoc) definieren jetzt explizit:
- Modell ist Recherche-Planer, nicht Faktenbehaupter
- Thema muss nicht bekannt oder verifiziert werden
- Briefing IMMER erstellen, keine Rueckfragen/Disclaimer
- Recherche-Schwerpunkte praxisnaeher formuliert
Behebt sporadische Verweigerungen bei unbekannten Faellen.
Claude vergibt manchmal Buchstaben-Suffixe an Quellennummern (z.B. [22b] statt [22]). Bisher wurden dafür leere Platzhalter-Quellen erstellt. Jetzt wird geprüft ob die Basisnummer existiert und die Referenz im Text korrigiert.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_build_lagebild_response() liefert jetzt Top-3-Artikel (neueste)
pro Location für Karten-Popups mit klickbaren Quellen-Links.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shared-Logik extrahiert (_build_lagebild_response, _get_snapshot_response).
Neue Endpunkte:
- GET /api/public/lagebild/{incident_id} für beliebige öffentliche Lagen
- GET /api/public/lagebild/{incident_id}/snapshot/{snapshot_id}
Bestehende Iran-Endpunkte bleiben abwärtskompatibel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nutzer koennen per Klick auf Chips Anweisungen zur Beschreibung
hinzufuegen: Zusammenfassung, Vergleichstabelle, Zeitverlauf,
Pro/Contra oder eigene Tabellen. Format: [TABELLE: ...] und
[ZUSAMMENFASSUNG]. Mehrere Anweisungen moeglich. Analyzer-Prompts
beachten diese Anweisungen verbindlich. Beschreibung-generieren
bewahrt bestehende Direktiven.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Analyzer-Prompts erlauben jetzt Tabellen wenn Daten sich strukturiert
vergleichen lassen (Produkte, Modelle, Kennzahlen etc.).
Frontend parst Markdown-Tabellensyntax und rendert sie als HTML-Tabellen
mit passendem Styling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Statt beim PDF-Export 30+ Sekunden auf die KI-Zusammenfassung zu
warten, wird sie jetzt automatisch nach jedem Refresh generiert.
Beim Export ist sie dann sofort verfuegbar (gecacht in DB).
Summary-Aenderungen invalidieren den Cache automatisch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sidebar bekommt z-index 9500 (über dem Progress-Overlay mit 9000),
sodass man während der ersten Recherche einer neuen Lage andere
Fälle in der Sidebar anklicken und damit weiterarbeiten kann.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HTTPBearer(auto_error=True) gab 403 zurueck wenn kein Authorization-
Header gesendet wurde. Das Frontend erkennt nur 401 als Session-Ablauf
und leitet zum Login weiter. 403 wurde als generischer Fehler behandelt,
wodurch abgelaufene Sessions still fehlschlugen (kein Redirect zum Login).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wenn ein vorheriger Refresh die Summary gespeichert hat aber die
Faktenchecks durch einen Crash verloren gingen, wurden bei allen
Folge-Refreshes keine Artikel an den Factchecker uebergeben
(all_articles_preloaded blieb None), was zu leeren Ergebnissen fuehrte.
Betroffen: Incidents 56, 57, 58 (alle mit 0 Faktenchecks trotz Artikeln).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Der lokale Import in der Massen-Downgrade-Pruefung ueberschattete den
Top-Level-Import und verursachte: cannot access local variable
find_matching_claim where it is not associated with a value.
Dadurch scheiterte der Faktencheck komplett (0 Faktenchecks).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Beim Zurueckwechseln auf eine laufende Lage wird der gespeicherte
State (minimized/offen) direkt aus _progressState wiederhergestellt.
War das Popup offen -> offen. War es minimiert -> Mini-Bar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Confirm-Dialog z-index ueber Progress-Popup (10000 > 9000)
2. Progress-Popup wird ausgeblendet waehrend Confirm-Dialog offen
3. Kein dunkler-werdendes Overlay bei mehrfachem Abbrechen-Klick
4. Abbrechen funktioniert jetzt auch fuer Lagen in der Warteschlange
(werden direkt aus der Queue entfernt statt auf Start zu warten)
5. Cancel-Status wird im Popup-Titel angezeigt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
handleRefreshError und handleRefreshCancelled haben den Sidebar-
Refresh-Status (Gold-Rand, Spinner-Text) nicht entfernt. Dadurch
blieben Lagen faeelschlicherweise als laufend markiert.
Jetzt wird bei Error/Cancel: _removeSidebarRefreshStatus() aufgerufen,
_progressState geloescht, und Sidebar neu gerendert.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
API /incidents/refreshing gibt jetzt auch queued IDs mit Position
und den aktuell laufenden Task zurueck.
Frontend nutzt started_at aus der API fuer Timer-Wiederherstellung.
Queued Lagen werden mit korrekter Position wiederhergestellt.
Aktiv laufender Task wird als researching angezeigt statt queued.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Position aus _progressState lesen als Fallback wenn extra.queue_position
nicht verfuegbar ist (z.B. bei Lagenwechsel-Restore).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Progress-State wird jetzt fuer alle refreshenden Lagen angelegt,
nicht nur fuer die aktuell ausgewaehlte. Sidebar-Update passiert
vor dem Early-Return fuer nicht-aktuelle Lagen.
Bei WebSocket-Reconnect (auch nach Ctrl+Shift+R) wird der State
fuer bereits laufende Refreshes korrekt wiederhergestellt.
Sidebar-Cleanup bei Refresh-Abschluss fuer alle Lagen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
renderIncidentItem() baut den Refresh-Status (Gold-Rand, Spinner,
Statustext, Warteschlange-Position) direkt ins HTML ein. Ueberlebt
jetzt renderSidebar()-Aufrufe bei Lagenwechsel und Aktualisierungen.
Sidebar wird nach jedem WebSocket-Status-Update neu gerendert,
damit der Status fuer ALLE Lagen immer sichtbar bleibt.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Klick irgendwo ausserhalb des Popups minimiert es zur Mini-Bar.
Beim ersten Durchlauf (blocking) bleibt das Popup offen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-Minimize entfernt. Popup erscheint sowohl bei erstem Durchlauf
als auch bei Aktualisierung sofort. Kann dann manuell minimiert werden.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Aktualisierungen starten minimiert (Mini-Bar), Popup nur per Klick.
Verhindert Ueberlagerung von Bearbeiten/Export-Buttons.
2. Erster Durchlauf: Bearbeiten/Export/Archivieren/Loeschen gesperrt,
nur Abbrechen moeglich.
3. Sidebar: Warteschlange-Lagen zeigen Position (#1, #2...) mit
eigenem visuellen Stil (gedimmt, pulsierender Dot).
4. Sidebar-Status (Recherchiert/Analysiert/Faktencheck) wird fuer
ALLE laufenden Lagen angezeigt, nicht nur die aktuelle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ladebalken ersetzt durch zentriertes Popup-Fenster mit Checkbox-Checkliste
(Warteschlange, Recherche, Analyse, Faktencheck) und Echtzeit-Timer.
Erster Durchlauf: Popup nicht wegklickbar, Blur-Effekt auf Kacheln.
Aktualisierung: Popup minimierbar zu kompakter Status-Leiste.
Timer laeuft pro Lage im Hintergrund weiter bei Lagenwechsel.
Gesamtzeit wird am Ende im Abschluss-Popup angezeigt.
Sidebar: Animierter Gold-Rand und Fortschrittstext (Recherchiert/
Analysiert/Faktencheck) unter dem Lage-Namen bei laufendem Refresh.
Zusaetzlicher Cancel-Checkpoint im Orchestrator nach Uebersetzung.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Template passt Text je nach incident_type an:
- adhoc: "Neues Lagebild - Benachrichtigung" / "Neuigkeiten zur Lage"
- research: "Recherche - Benachrichtigung" / "Neuigkeiten zur Recherche"
incident_type wird durch die gesamte Notification-Kette durchgereicht.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Zwei Bugs behoben die dazu fuehrten, dass alle established Faktenchecks
bei einem inkrementellen Refresh auf unverified zurueckgesetzt wurden:
1. _format_existing_facts() uebergibt jetzt Evidence-Kontext an den LLM,
damit bestehende Claims im inkrementellen Modus verifiziert bleiben.
2. Neuer Schutz im Orchestrator: Wenn >50% der established Fakten
herabgestuft wuerden, werden die FC-Ergebnisse komplett verworfen.
Root Cause: Inkrementeller Faktencheck hatte nur Claims+Status aber
keine Evidence. Der LLM konnte bestehende Fakten nicht verifizieren
und gab unverified fuer alles zurueck.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stopPropagation auf dem Dropdown-Container verhindert, dass der
globale Click-Listener das Menue schliesst wenn man den Org-Switcher
bedient.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ermoeglicht dem Global Admin (is_global_admin Flag) zwischen
Organisationen zu wechseln. Neue Endpoints: GET /api/auth/organizations,
POST /api/auth/switch-org. Org-Dropdown im Header-Menue, nur fuer
Global Admin sichtbar. Komplett herausnehmbar (Flag + Code-Bloecke).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>