Commit graph

404 Commits

Autor SHA1 Nachricht Datum
a2610d0094 Quellenübersicht: Klick auf Quelle klappt Artikel-Liste auf
Quellen-Boxen waren bisher reine Anzeige. Jetzt sind sie klickbar:
beim Klick erscheint direkt unter der Box (über die volle Grid-Breite)
eine Liste der Artikel-Headlines dieser Quelle, jede mit Link zum
Originalartikel. Mutual-exclusive — Klick auf eine andere Quelle
schließt die vorherige automatisch.

- components.js: Item bekommt data-source, onclick + Tastatur-Support
  (Enter/Space), aria-expanded.
- app.js: toggleSourceOverviewDetail filtert _currentArticles nach
  Quelle, sortiert chronologisch absteigend, fügt das Detail-Element
  via insertAdjacentElement direkt nach dem geklickten Item ein.
- CSS: aktiver Item-Status (Glow + Tint), Detail-Block mit
  grid-column 1/-1 (volle Breite) + max-height 320px scrollbar bei
  vielen Artikeln + dezente Slide-In-Animation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:57:48 +02:00
a08df3d121 RSS-Parser: Match-Schwelle adaptiv (Bug 1 aus Buckelwal-Diagnose)
Bisher musste eine Headline mindestens 2 der dynamisch generierten
Suchworte enthalten, um den Match-Filter zu passieren. Bei thematisch
engen Lagen (Bsp. "Buckelwal timmy") fielen damit echte Treffer wie
"Transport mit Buckelwal erreicht dänische Gewässer..." durch, weil
nur 1 Keyword (buckelwal) gematcht hat.

Neue Heuristik: enthält der Text mindestens ein spezifisches Keyword
(>=7 Zeichen, also keine kurzen Akteursnamen wie "iran" oder "trump"),
reicht 1 Treffer. Bei nur kurzen, generischen Keywords gilt weiter die
alte Schwelle (halb der Wörter, max. 2). Topic-Filter danach (Haiku)
fängt False Positives.

Damit kommen ZDF/tagesschau/n-tv-Headlines mit nur einem starken
Begriff durch — der Hauptgrund, warum Lage 8 Buckelwal mit ZDF-Quelle
am ersten Refresh 0 Artikel hatte.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:55:05 +02:00
0a6208c289 WebSearch: eingetragene Web-Quellen via Haiku vorselektieren
Bisher hatten Quellen vom Typ web_source keine praktische Wirkung auf
die Recherche - sie lagen nur als Marker in der DB. Jetzt werden sie
aktiv in den Recherche-Prompt eingebunden.

Ablauf:
1. Vor dem Hauptaufruf an Opus prüft ein günstiger Haiku-Call alle
   aktiven Web-Quellen des Tenants (plus globale) und wählt die
   thematisch passenden aus. Leere Selektion ist ausdrücklich erlaubt.
2. Die ausgewählten Domains werden dem Recherche-Prompt als
   "EINGETRAGENE WEB-QUELLEN" Block beigegeben mit der Empfehlung,
   gezielt mit "site:domain query" zu suchen, falls thematisch passend.
3. site: ist Empfehlung, kein Zwang - Claude bleibt flexibel und
   ergänzt seine sonstige Recherche.

- source_rules.get_feeds_with_metadata: SELECT um notes-Feld erweitert,
  damit der Selektor besseren Kontext zur Quelle hat.
- ResearcherAgent.select_relevant_web_sources: neuer Helper analog zu
  select_relevant_feeds, mit Skip-Optimierung wenn ≤3 Quellen.
- WEB_SOURCE_SELECTION_PROMPT: explizite Regel "lieber leer als
  pauschal alle", verhindert Token-Verschwendung.
- ResearcherAgent.search: neuer Parameter preferred_sources, beide
  Templates (RESEARCH + DEEP_RESEARCH) bekommen optionalen
  preferred_sources_block.
- Orchestrator._web_search_pipeline: Vorselektion vor researcher.search,
  Token-Usage in usage_acc, Logging der gewählten Domains.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:45:17 +02:00
19038472cf Ereignis-Timeline: ▼-Pfeil unter aktivem Heatmap-Balken entfernt
Der Pfeil überschattete das darunter liegende Stunden-Label. Goldener
Balken mit Glow + scaleY reicht als visuelles Aktiv-Signal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:06:31 +02:00
462127dc52 Ereignis-Timeline: Heatmap-Klick-Bug beheben
Inline-onclick mit JSON.stringify(label) + UI.escape erzeugte bei
Bucket-Labels mit Anführungszeichen oder Sonderzeichen einen kaputten
HTML-Attribut-String. Klicks lösten daher gar keinen Handler aus.

Statt JS-String im onclick werden Bucket-Daten jetzt als
data-start/data-end/data-label-Attribute am Cell-Element gehalten.
Onclick ruft App.handleStripClick(this), das die Werte sauber aus
dataset liest und an openTimelineWindow weiterreicht.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:04:02 +02:00
34aeb04a88 Ereignis-Timeline: Klick auf Heatmap-Balken filtert den Stream
Vorher scrollte ein Klick auf einen Balken nur zur passenden Zeit-
Gruppe — bei langem Stream kaum erkennbar. Jetzt filtert der Klick
den Stream auf das Zeitfenster des Balkens und zeigt nur diese
Einträge.

- Aktiver Balken: vergrößert (scaleY 1.6) + goldener Hintergrund +
  starker Glow + kleiner ▼-Pfeil darunter; alle anderen Balken auf
  40% Opacity gedimmt.
- Banner zwischen Strip und Stream zeigt "Gefiltert auf [Label] ·
  X Einträge" mit "Filter aufheben"-Button.
- Zweiter Klick auf denselben Balken oder Banner-Button hebt den
  Filter auf.
- Filter/Range-Buttons setzen den Strip-Window-Filter zurück (sonst
  inkonsistente Doppel-Filterung).
- Lagen-Wechsel räumt _activeStripWindow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:59:32 +02:00
b14fe31f42 Ereignis-Timeline: Newsfeed mit Lagebericht-Sektionen + Heatmap-Strip
Komplett neu gedacht: nicht mehr horizontale Karten-Kette, sondern
vertikaler Newsfeed mit den vorhandenen vt-Klassen, plus dezenter
Heatmap-Strip oben für die Quantitäts-Übersicht.

- Heatmap-Strip oben (14 px hoch): ein Quadrat pro Tag/Stunde/Woche/
  Monat je nach Spannweite, Farbintensität = Aktivität, goldener
  Boden-Strich bei Lagebericht.
- Klick auf Heatmap-Quadrat: Stream scrollt zur passenden Zeit-Gruppe,
  diese flasht kurz auf.
- Newsfeed darunter: vt-time-group mit Datums-Trennzeilen
  (Heute/Gestern/...), Lagebericht-Einträge sind durch ihre vt-snapshot
  Klasse prominent gegenüber Meldungs-Einträgen.
- Klick auf Lagebericht: Volltext klappt inline auf (vorhandener
  lazyLoadSnapshotDetail-Mechanismus, kein separates Detail-Panel mehr).
- Klick auf Meldung: Detail klappt inline auf.

Karten-Kette, Verbindungs-Stränge, "Aktuell"-Marker, Snapshot-Detail-
Panel, Window-Detail-Panel und alle zugehörigen CSS-Klassen
(ht-card/ht-link/ht-now/ht-chain/ht-detail) komplett entfernt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:51:41 +02:00
ffb8dddc4f Ereignis-Timeline: Snapshot-zentriertes Konzept
Komplette Neufassung der horizontalen Timeline. Lageberichte sind die
natürlichen Anker einer OSINT-Lage; Artikel werden um sie herum
gruppiert.

Aufbau:
- Quanti-Strip oben: schmale Heatmap-Reihe (ein Quadrat pro Stunde/Tag/
  Woche/Monat je nach Spannweite), Farbintensität = Aktivität. Quadrate
  mit Lagebericht haben goldene Unterkante. Klick auf Quadrat öffnet
  Detail-Panel mit allen Meldungen des Zeitfensters.
- Lagebericht-Kette darunter: jede Karte zeigt Datum, Vorschau-Text aus
  dem Snapshot, Anzahl Meldungen + Fakten. Karten sind durch Stränge
  verbunden, die "X Meldungen"-Pille tragen — Klick auf Strang öffnet
  Liste der Meldungen zwischen den beiden Lageberichten.
- "Aktuell"-Marker am rechten Ende mit pulsierendem Pin.

Filter:
- Alle: Strip + Kette
- Meldungen: Strip + vertikaler Stream
- Lageberichte: nur Karten ohne Strip/Stränge

Edge-Case: Lagen ohne Lagebericht zeigen Strip + Stream als Fallback.

Mobile (<900px): Kette stapelt vertikal, Strip bleibt horizontal.

Alte Bar-Achse, Punkte, Bucket-Merge, Day-Markers etc. komplett
entfernt — die alte Achse war für sporadische OSINT-Aktivität das
falsche Pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:38:09 +02:00
AegisSight Promote-UI
0edbf7e3b8 Revert "Ereignis-Timeline: Säulen, Lagebericht-Linien, Themen-Labels"
This reverts commit 370bb94b26.
2026-05-01 15:22:13 +02:00
AegisSight Promote-UI
de01ab71fc Revert "Ereignis-Timeline: Überlappungen oben auflösen"
This reverts commit 58eb1298ca.
2026-05-01 15:22:06 +02:00
AegisSight Promote-UI
86a49e082c Revert "Ereignis-Timeline: Lagebericht-Stempel zusammenfassen, Bar-Cap entfernen"
This reverts commit cae9c5467a.
2026-05-01 15:21:53 +02:00
AegisSight Promote-UI
221b21cb4e Revert "Cache-Bust: style.css und app.js Versionen erhöht"
This reverts commit 30cb276ec6.
2026-05-01 15:21:52 +02:00
30cb276ec6 Cache-Bust: style.css und app.js Versionen erhöht
Browser hatten die alten Timeline-Stile gecached und Änderungen waren
nicht sichtbar. Versions-Suffixe auf 20260501a aktualisiert, damit der
Cache zwingend invalidiert wird.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:15:55 +02:00
cae9c5467a Ereignis-Timeline: Lagebericht-Stempel zusammenfassen, Bar-Cap entfernen
Mehrere Snapshots in derselben Achsen-Position erzeugten verschmierte,
übereinanderliegende Pin-Symbole. Zusätzlich war der goldene Streifen
auf der Bar (Bar-Cap) redundant zur vertikalen Snapshot-Linie.

- Snapshots werden pro Achsen-Position (auf 0,5%-Genauigkeit) gruppiert.
  Eine einzige Linie + ein einziger Pin pro Gruppe; bei mehreren
  Lageberichten zeigt der Pin die Anzahl als Zahl statt das Stempel-
  Symbol.
- Bar-Cap (separates Element über der Bar) entfernt. Stattdessen
  bekommt die Bar-Füllung bei has-snapshot eine dezente goldene
  Top-Linie via ::before — keine Doppel-Markierung mehr.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:13:17 +02:00
58eb1298ca Ereignis-Timeline: Überlappungen oben auflösen
Im Top-Bereich der Achse kollidierten Tagesmarker, Themen-Labels und
Lagebericht-Stempel auf der gleichen Höhe. Jetzt klare Schichten:

- Tagesmarker (Heute/Gestern/Datum): top 0
- Themen-Labels: eigene Schiene direkt darunter (top 22 / 42 hourly),
  nicht mehr Kind der Bar — verhindert Wandern bei verschieden hohen
  Bars
- Bars: nach unten verschoben (top 44 / 64 hourly)
- Lagebericht-Linien: gehen jetzt nur durch den Bar-Bereich,
  Pin-Symbol (Cap) hängt UNTEN an der Achsenlinie statt oben in den
  Tagesmarkern
- Heute-Linie: bei stündlicher Granularität ausgeblendet (Tagesmarker
  zeigt eh "Heute, ..."), bei Tag/Woche/Monat weiterhin aktiv

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:10:16 +02:00
370bb94b26 Ereignis-Timeline: Säulen, Lagebericht-Linien, Themen-Labels
Punkte ersetzt durch schmale Säulen (Bar-Chart), Höhe = Anzahl Artikel
im Bucket relativ zum Maximum. Aktivität ist sofort als Verlauf lesbar.

- Granularität: hour < 48h, day < 30T, week < 180T, sonst month.
  Bucket-Merge (verfälscht das Datum) entfernt, stattdessen sauberer
  Granularitätswechsel.
- Lagebericht-Linien quer durch die Achse als dezente goldene Vertikalen
  mit kleinem Stempel-Symbol oben. Klick öffnet das Bucket-Detail mit
  dem zugehörigen Snapshot.
- Heute-Linie mit Label, wenn der heutige Zeitpunkt im sichtbaren
  Bereich liegt.
- Themen-Label über den Top-3 aktivsten Buckets: clientseitig per
  Wort-Häufigkeit aus Headlines, mit deutscher Stopwortliste. Zeigt
  nur, wenn ein Wort mindestens zweimal vorkommt.
- Hover über eine Säule: Mini-Karte mit den 3 relevantesten Headlines
  des Buckets (sortiert nach relevance_score), plus "+N weitere" und
  Lagebericht-Hinweis bei gemischten Buckets.
- Snapshot-Bars bekommen oben einen goldenen Cap als Marker.
- Reduced-motion respektiert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:04:43 +02:00
c9bd6310ae Analysepipeline: Snake-Track mittig zentrieren statt 100% breit
Track war 100% breit, dadurch saß die ltr-Reihe links und die rtl-Reihe
rechts in der Karte. Block 3 (Ende Reihe 1) und Block 4 (Anfang Reihe 2)
lagen weit auseinander, der Reihenwechsel-Pfeil wirkte zusammenhanglos.

Track ist jetzt inline-flex (schrumpft auf Inhaltsbreite, ca. 3 Blöcke
plus Lücken) und wird in der Karte zentriert. Alle drei Reihen sind
gleich breit, Block 3 sitzt direkt über Block 4, die Snake-Form wird
optisch nachvollziehbar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:38:19 +02:00
392028a9aa Analysepipeline: kompakter Reihenwechsel-Pfeil statt langem Bogen
Der U-Turn-Bogen, der quer ueber die ganze Box-Breite lief, wirkte bei
nur drei Bloecken pro Reihe optisch ueberladen. Jetzt sitzt unter dem
letzten Block der oberen Reihe ein schlichter, kurzer Pfeil nach unten,
der direkt zum ersten Block der naechsten Reihe zeigt.

- pipeline.js: Neue _renderUturn-Variante, die Spacer (Block-Breite)
  vor oder hinter den Pfeil setzt, sodass er passgenau unter dem letzten
  Block sitzt (rechts nach ltr-Reihe, links nach rtl-Reihe).
- style.css: Pfeil-Container nutzt Flex mit Block-Breite-Spacern statt
  100%-breitem SVG. Kurzer ↓ als gerader Pfad mit Pfeilkopf,
  is-flowing-Animation bleibt erhalten.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:35:13 +02:00
7b5adccf2b Analysepipeline: echte Umlaute und ASCII-Bindestriche
Em-dashes und Umlaut-Umschreibungen aus den Pipeline-Aenderungen
entfernt: Tooltip-Texte, HTML-Empty-State, JS-Kommentare,
Count-Status-Platzhalter, Orchestrator-Kommentare und CSS-Kommentare.

Anstelle von typografischen Gedankenstrichen werden jetzt Kommas oder
Punkte gesetzt, "uebersprungen" -> "uebersprungen" mit echtem Umlaut,
"laeuft" usw. analog. UI-Text "&mdash; Refresh starten" wird zu zwei
Saetzen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:16:28 +02:00
059a9a2dc7 Analysepipeline: Snake-Layout (3x3) statt linearer Reihe
Pipeline laeuft jetzt zickzack: Reihe 1 von links nach rechts, U-Turn
nach unten, Reihe 2 von rechts nach links, U-Turn nach unten,
Reihe 3 wieder von links nach rechts. Karte waechst auf benoetigte
Hoehe statt horizontalem Scrollen.

- pipeline.js: Bloecke werden in Dreier-Gruppen aufgeteilt, Direction
  ltr/rtl wechselt pro Reihe. Zwischen Reihen rendert ein SVG-U-Turn-Pfeil
  (Bogen mit Pfeilkopf) die Verbindung. Daten-Fluss-Animation (is-flowing)
  funktioniert sowohl auf Inner-Pfeilen als auch auf U-Turns.
- CSS: .pipeline-row mit flex-direction abhaengig von data-direction.
  rtl-Reihen kippen Pfeilkopf und Animation in entgegengesetzte Richtung.
  U-Turn-Pfad als SVG mit stroke-dasharray-Animation bei aktivem Fluss.
- Mobile (<900px): Snake aufgeloest, alle Reihen werden vertikal
  gestapelt, U-Turns ausgeblendet — bestehende Vertikal-Stilistik bleibt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:07:58 +02:00
3a346ba2ec Analysepipeline: Visualisierung der Refresh-Schritte
Neuer Tab "Analysepipeline" zwischen Faktencheck und Quellenuebersicht.
Zeigt 9 Verarbeitungsschritte als n8n-artige Blockkette: Quellen sichten,
Nachrichten sammeln, Doppeltes filtern, Relevanz bewerten, Orte erkennen,
Lagebild verfassen, Fakten pruefen, Qualitaetscheck, Benachrichtigen.

- Backend: refresh_pipeline_steps-Tabelle persistiert pro Refresh+Pass die
  Status- und Zahlen-Werte. pipeline_tracker.py kapselt Start/Done/Skip/Error
  inkl. WebSocket-Broadcast (Event-Typ pipeline_step). 9 Hooks im Orchestrator
  speisen die Anzeige.
- API: GET /api/incidents/{id}/pipeline liefert Definition + letzten Stand
  (Zahlen aus letztem Refresh, Multi-Pass-Konsolidierung).
- Frontend: pipeline.js rendert Vollbild-Blockkette mit pulsierendem Glow am
  aktiven Block, animierten Pfeilen bei Datenfluss, Haekchen am fertigen Block.
  Hover-Tooltip mit Erklaerung in Nutzersprache, Klick oeffnet Detail-Popup.
  Bei Research-Lagen leuchtet ein Schleifen-Pfeil pro Mehrfach-Durchlauf auf.
  Mini-Variante (nur Icons) im Refresh-Progress-Popup.
- CSS: Light/Dark-Theme-fest, dezenter Circuit-Hintergrund (5% Opacity),
  Mobile-vertikale Stapelung unter 900px, prefers-reduced-motion respektiert.
- Uebersprungene Schritte (z.B. Geoparsing ohne neue Artikel) werden
  ausgeblendet, brandneue Lagen ohne Refresh zeigen Hinweis.

Tooltips bewusst in normaler Sprache ohne Internas (keine Modellnamen,
keine Toolnamen, keine Phasen-Labels).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 13:53:44 +02:00
2b51e49d0d Release-Notes: Hintergrundbild-Unschärfe zuverlässiger und vollständiger 2026-05-01 01:12:45 +02:00
Claude Code
e3fe7fac85 fix(blur): Refresh-Blur stabilisieren und Header mit-blurren
Problem: Beim Anlegen einer neuen Lage verschwand der Blur-Effekt auf dem
Hintergrund-Inhalt sobald das Browserfenster in der Groesse veraendert wurde.
Zudem blieb der Lagen-Titel im Header offen sichtbar, waehrend der Inhalt
darunter geblurrt war — der wechselnde Titel war also klar lesbar.

Ursache:
- Blur lag auf .tab-panels und parallel auf .tab-panel — zwei verschachtelte
  Composite-Layer, die bei jedem Reflow neu berechnet werden.
- transition: filter 0.4s ease auf .tab-panel — bei Resize lief die Transition
  oft rueckwaerts oder pausierte, was den Blur visuell verschwinden liess.
- .incident-header-strip lag ausserhalb von .tab-panels und war dadurch nie
  geblurrt (Titel/Aktionen/Beschreibung blieben offen sichtbar).

Aenderungen:
- Blur-Anker hochgezogen auf #incident-view (Klasse refresh-blurred), so dass
  Header und Tab-Panels gemeinsam unscharf werden.
- Nur noch eine Filter-Ebene (filter: blur(8px)).
- Transition entfernt — Blur soll schlagartig kommen und gehen, kein
  lesbarer Zwischenzustand beim Reflow.
- will-change: filter; transform: translateZ(0); — erzwingt einen persistenten
  GPU-Composite-Layer, der bei Window-Resize stabil bleibt.

Headless-Tests bestaetigen: alte Variante 89.8% Pixel-Stabilitaet ueber 6
Resize-Zyklen mit Content-Mutation, neue Variante 97.0% (Rest = Content-Diff).
2026-04-30 22:40:51 +00:00
Claude Code
88b18d0775 fix(researcher): Robusteres JSON-Parsing der Claude-Antworten
Behebt das Symptom, dass Recherche-Lagen wie staging Lage 6 "Friedrich Merz"
trotz erfolgreichem Refresh leer blieben. Claude lieferte nicht-leere Antworten
(1226-2125 Zeichen), die der bisherige Regex-Parser nicht extrahieren konnte —
die Recherche meldete "0 Artikel" und der Refresh wurde stumm als Erfolg
verbucht.

Aenderungen:
- _parse_response, select_relevant_feeds, extract_dynamic_keywords und
  select_relevant_telegram_channels nutzen jetzt json.JSONDecoder.raw_decode
  ueber Modul-Helper _extract_json_array/_extract_json_object. Damit werden
  auch JSON-Bloecke mit Vor-/Nachtext, Markdown-Fences oder verschachtelten
  Objekten zuverlaessig erkannt.
- Bei Parse-Fehlschlag wird jetzt ein gekuerztes Sample der Claude-Antwort
  geloggt, damit kuenftige Faelle direkt debuggbar sind.
- Neue ResearcherParseError-Exception unterscheidet "echt 0 Treffer" von
  "Antwort kaputt". search() gibt zusaetzlich ein parse_failed-Flag zurueck.
- Orchestrator-Multi-Pass: wenn alle 3 research-Durchlaeufe 0 neue Artikel
  ergeben UND mindestens einer am Parser scheiterte, wird der Refresh als
  Fehler markiert (statt als stiller Erfolg). Der WebSocket-refresh_error
  loest dann die sichtbare UI-Meldung aus.

Adhoc-Lagen sind unveraendert: dort fangen RSS und Telegram die kaputte
Claude-Antwort auf, dafuer ist nur die Diagnose im Log neu.
2026-04-30 20:45:41 +00:00
AegisSight Dev
682828ea58 Update-Meldungen folgen Theme, korrekte Umlaute
Banner und Was-ist-neu-Modal nutzen jetzt CSS-Variablen
(--bg-card, --text-primary, --accent etc.) statt fester
Dark-Mode-Farben, damit sie sich automatisch dem Hell-/Dunkelmodus
anpassen. RELEASES.json: alte ae/oe/ue-Schreibweisen auf echte
Umlaute umgestellt + neuer Eintrag fuer diesen Fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:19:02 +00:00
ac5160010d Release-Notes: Blur versucht zu fixen 2026-04-29 22:10:55 +02:00
Claude Code
059395393c Fix: rAF-Sicherheitsnetz fuer Blur zentral in _showPopupProgress
Vorheriger Fix in selectIncident griff nicht beim handleRefresh-Pfad
(manueller Aktualisieren-Klick), weil dieser direkt UI.showProgress aufruft
ohne selectIncident zu durchlaufen. Damit blieb eine Lage, deren erster
Refresh per Klick angestossen wurde, unblurred.

rAF mit add("blurred") jetzt direkt in _showPopupProgress (components.js),
sobald state.isFirst gesetzt ist. Damit greift der Blur in jedem Pfad, der
durch _showPopupProgress laeuft — selectIncident, handleRefresh,
handleStatusUpdate (WebSocket), Initial-Restore.

Der zentrale rAF in selectIncident ist redundant und wieder entfernt.
Der _willReBlur-Skip von remove("blurred") in selectIncident bleibt
erhalten — verhindert ueberfluessiges remove+add im selben Tick.

cache-bust components.js auf v=20260427a, app.js auf v=20260427c.
2026-04-27 20:52:39 +00:00
Claude Code
14d1062583 Fix: Blur greift bei jedem ersten Durchlauf der ausgewaehlten Lage
Vorheriger Patch hatte den rAF nur im Create-Flow gesetzt. Damit funktionierte
zwar das Anlegen, aber das Auswaehlen einer existierenden Lage, deren erste
Recherche gerade laeuft (oder nach einem manuellen ersten Refresh), blieb
unblurred.

Loesung: rAF mit add("blurred") jetzt zentral in selectIncident, sobald der
Progress-State isFirst=true und nicht minimiert ist. Damit greift der Blur
in allen Pfaden (Anlegen, manueller Refresh, Auswahl einer laufenden Lage,
Wechsel zwischen Faellen, Initial-Load via savedId).

Der zusaetzliche rAF in createIncident von 2ee90a4 ist damit redundant und
wieder entfernt — der zentrale Hook in selectIncident deckt den Fall mit ab.

cache-bust app.js auf v=20260427b.
2026-04-27 19:43:18 +00:00
Claude Code
2ee90a4b3b Fix: Blur greift sofort beim Anlegen einer neuen Lage
Im Create-Flow wurde .blurred in selectIncident() erst entfernt und gleich
darauf via _showPopupProgress wieder gesetzt. CSS filter:blur greift in der
Kombination (frischer isFirst-State + selectIncident im selben Tick + viel
vorausgehende DOM-Manipulation durch Modal-Close + renderSidebar) nicht
zuverlaessig im selben Frame — der Fall war bis zum Wegklicken/Zurueckklicken
unblurred.

selectIncident: remove("blurred") wird uebersprungen, wenn der zustaendige
Progress-State isFirst=true und nicht minimiert ist (Blur soll bestehen
bleiben statt remove+add im selben Tick).

createIncident: zusaetzlich requestAnimationFrame mit grid.classList.add im
naechsten Frame, falls der Browser den ersten add-Aufruf in selectIncident
nicht visuell uebernommen hat. Doppeltes Setzen ist idempotent.

dashboard.html: cache-bust app.js auf v=20260427a.
2026-04-27 19:29:00 +00:00
d1f88c9e9f Release-Notes: Update-Modal beim ersten Besuch 2026-04-26 23:08:07 +02:00
Claude Code
ad53786a24 Update-System: Modal auch beim ersten Besuch zeigen
Frueher wurde beim allerersten Treffen mit dem Update-System der lastSeen-
Marker stillschweigend gesetzt, ohne Modal — User sah erst beim ZWEITEN
Update was. Fuer Kunden-Onboarding ist das suboptimal: sie sollen direkt
sehen, dass das System existiert und welche Updates aktuell sind.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 20:43:24 +00:00
a9806a586b Release-Notes: Updatenachricht bei Deployment 2026-04-26 22:40:34 +02:00
Claude Code
2aaa51e2a8 Update-System Frontend: Banner + Was-ist-neu-Modal
Beim ersten Login nach einem Update zeigt der Monitor nun ein Modal mit den
Release-Notes des Updates (aus RELEASES.json). Wenn waehrend einer laufenden
Sitzung ein neues Update live geht, erscheint unten rechts ein Banner mit
einem Aktualisieren-Knopf.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 20:32:50 +00:00
Claude Code
2df37cb617 Update-System: /api/version + /api/release-notes + RELEASES.json
Frontend kann jetzt erkennen, wann eine neue Version live ist, und dem Nutzer
einen passenden Hinweis sowie die Release-Notes anzeigen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 20:28:10 +00:00
Claude Code
5473ba3ed7 WICHTIG: DB_PATH per ENV ueberschreibbar; data-Symlink aus Repo entfernt
Verhindert dass Staging und Live versehentlich dieselbe DB nutzen
(Symlink data wurde frueher beim git clone mitgeklont und zeigte
auf das gleiche physische Verzeichnis /home/claude-dev/osint-data).
Staging muss jetzt DB_PATH in der .env explizit setzen.
2026-04-26 19:42:33 +00:00
Claude Code
8042639d20 CLAUDE.md: Auto-Deploy + Promote-UI + Live-systemd dokumentiert
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:14:35 +00:00
claude-dev
ec53ab27cd CLAUDE.md: Staging-Umgebung dokumentiert (Service, DB, .env, Workflow)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 17:56:29 +00:00
claude-dev
c73541cdbe Block C: Prompt-Umlaute korrigiert + Timeout parametrisiert
- ENHANCE_PROMPT_ADHOC und ENHANCE_PROMPT_RESEARCH: Umschreibungen durch
  echte Umlaute ersetzt (fuer -> fuer, praezises -> praezises, ...). Behebt
  den Widerspruch, dass der Prompt "echte Umlaute verwenden" forderte,
  die Anweisung selbst aber ae/oe/ue/ss nutzte.
- call_claude() bekommt neuen timeout-Parameter. None = Fallback auf
  CLAUDE_TIMEOUT (1800s), sonst Override in Sekunden. asyncio.wait_for
  und die cancel-aware Variante nutzen durchgaengig den effective_timeout.
- Enhance-Endpoint ruft call_claude mit timeout=60 auf (Haiku-Single-Shot,
  vorher global 1800s).
- chat.py _call_claude_chat: Timeout von 60s auf 120s erhoeht (Chat-Antworten
  koennen etwas laenger dauern, haben aber keinen Anspruch auf 30 Min).
2026-04-23 17:56:28 +00:00
claude-dev
5d5ec7c924 Block B: ClaudeCliError + differenzierte HTTP-Status + Rate-Limit-Retry
- Neue Exception-Klasse ClaudeCliError(error_type, message) in claude_client.py
  mit Kategorien rate_limit / auth_error / timeout / cli_error.
- _classify_cli_error() als geteilter Klassifikator (Keywords fuer Rate-Limit
  und Auth-Fehler wie "does not have access", "login again").
- call_claude() erkennt jetzt auch is_error=true im JSON bei returncode=0
  (Hauptursache des Ausfalls vom 22.04.: CLI liefert "Your organization
  does not have access" mit is_error=true statt Exit-Code).
- Orchestrator: ClaudeCliError mit rate_limit/timeout als transient behandelt
  (3 Retries mit Backoff 0s/120s/300s). auth_error/cli_error brechen sofort
  ab ohne Retry. Behebt den bestehenden Bug, dass Rate-Limit-Fehler gar nicht
  retried wurden.
- routers/incidents.py Enhance-Endpoint: ClaudeCliError wird auf
  503 (auth_error) / 429 (rate_limit) gemappt, TimeoutError auf 504.
- routers/chat.py _call_claude_chat(): wirft jetzt ClaudeCliError statt
  generischem RuntimeError. Chat-Endpoint mappt auth_error auf 503.
- Frontend: neue ApiError-Klasse in api.js mit status+detail.
  generateDescription() in app.js zeigt differenzierte Toasts nach
  HTTP-Status (503/429/504/403).
- dashboard.html: Cache-Bust api.js + app.js auf v=20260423a
2026-04-23 17:54:13 +00:00
claude-dev
e8ac0d0c50 Block A: License-Check + Credits-Tracking fuer Enhance und Chat
- Neuer Helper charge_usage_to_tenant() in services/license_service.py:
  UPSERT in token_usage_monthly und Credits-Abzug aus licenses.credits_used.
  Wiederverwendbar fuer alle Claude-Call-Verursacher.
- Orchestrator: Inline-Buchungslogik (35 Zeilen) durch Helper-Aufruf ersetzt.
- routers/incidents.py POST /enhance-description: require_writable_license
  statt get_current_user, db_dependency hinzugefuegt, Credits-Buchung mit
  source="enhance" nach jedem Claude-Call.
- routers/chat.py POST /: analog require_writable_license + Credits-Buchung
  mit source="chat". _call_claude_chat() gibt jetzt zusaetzlich ClaudeUsage
  zurueck.

Abgelaufene/gesperrte Lizenzen koennen damit keine Haiku-Calls mehr ausloesen,
und alle Kosten werden konsistent auf Tenant-Ebene verbucht.
2026-04-23 17:49:32 +00:00
claude-dev
c8a8e10020 Chat-Doku aktualisiert + Tutorial-Einstieg temporaer deaktiviert
- Chat-System-Prompt: Aktualisierungs-Modi (Minuten/Stunden/Tage/Wochen, 10-Min-Minimum, Startzeit), 5 Faktencheck-Status (Bestaetigt, Gesichert, Unbestaetigt, Umstritten, Widerlegt), Export mit PDF/DOCX und Bereichsauswahl
- Tutorial-Button in Sidebar auskommentiert (Ueberarbeitung)
- Tutorial-Trigger im Chat auskommentiert (Opener-Hinweis und Keyword-Erkennung)
2026-04-23 17:43:27 +00:00
claude-dev
a579e2c275 Neueste Entwicklungen aus Lagebild statt aus Artikel-Strom
Bisher extrahierte der Generator Bullets direkt aus den neu eingesammelten
Artikeln und mergte sie mit den bestehenden Developments. Das fuehrte zu
zwei wiederkehrenden Problemen:

1. Off-topic Artikel, die den Keyword-Prefilter aber nicht den Topic-Filter
   passiert hatten, konnten als Bullet landen (die Kachel bildete dann
   Nebenschauplaetze des Weltgeschehens ab statt der Lage).
2. Alte Bullets blieben stehen, auch wenn sie laengst nicht mehr die
   'neuesten' Entwicklungen waren — nur sehr ueberholte Eintraege fielen
   durch das 8-Bullet-Cap raus.

Neue Logik: Der Generator nimmt das frisch erzeugte Lagebild als autoritative
inhaltliche Grundlage und waehlt daraus Bullets aus, die durch eine aktuelle
belegende Meldung (<~7 Tage) gestuetzt sind. Dadurch:

- Thematisch sauber: Lagebild enthaelt bereits nur relevante Inhalte.
- Echt 'neueste': Alte Hintergrund-Erwaehnungen im Lagebild fallen raus,
  weil kein aktueller Artikel sie belegt.
- Klar datiert: Zeitstempel zwingend aus article.published_at der
  belegenden Meldung.
- Kompakt: 4-6 Bullets (vorher 8), nach Zeitstempel absteigend.

Kein Merge mit previous_developments mehr — bei jedem Refresh neu generiert
(behebt das Drift-Problem). previous_developments bleibt nur als Fallback,
falls der Generator im Einzelfall 0 Bullets parst.
2026-04-21 14:23:18 +00:00
claude-dev
efae707fa9 Fix: Blur + Aktions-Lock beim Anlegen eines Falls sofort aktiv
Beim Create-Flow wurde selectIncident() aufgerufen, BEVOR der Fall
als refreshend markiert wurde. Dadurch entfernte selectIncident den
'.blurred'-Zustand des Tab-Containers und rief _lockActionsIfFirst(false)
auf — der Fallinhalt war zwischen Oeffnen und Eintreffen der ersten
WebSocket-Statusnachricht kurzzeitig klickbar und unblurred.

Jetzt wird der Refresh-Status und ein Initial-State mit isFirst=true
schon VOR selectIncident gesetzt. selectIncident erkennt isRefreshing
und ruft _showPopupProgress + _lockActionsIfFirst(true) mit dem
bestehenden State auf — Blur und Lock greifen sofort.
2026-04-21 14:02:52 +00:00
claude-dev
05b60ffb35 Fix: Timer springt beim Seiten-Reload nicht mehr zurueck
Bei Research-Multi-Pass (3 Durchlaeufe) und bei Retry-Versuchen wird
pro Pass/Retry ein neuer refresh_log-Eintrag mit frischem started_at
angelegt. /incidents/refreshing gab dadurch beim Reload den spaeteren
started_at zurueck statt des urspruenglichen Session-Starts — der
Frontend-Timer sprang auf 0:00 zurueck.

Orchestrator traegt jetzt _current_task_started_at in-memory, gesetzt
beim Queue-Pickup und geraeumt im finally. /incidents/refreshing liefert
diesen Session-Start fuer den aktuell laufenden Task (Fallback: letzter
refresh_log-Eintrag, falls der Server zwischenzeitlich neu gestartet
wurde).
2026-04-21 13:42:51 +00:00
claude-dev
60b8646fe4 Semantischer Topic-Filter gegen off-topic Keyword-Zufallstreffer
Neue Artikel passieren jetzt vor DB-Speicherung einen Haiku-Relevanzfilter
(AnalyzerAgent.filter_relevant_articles), der Artikel verwirft, die nur
auf generische Keywords matchen, aber das Kernthema der Lage nicht
inhaltlich behandeln. Bei Parsing-/API-Fehler oder 100%-Rejection: Fallback
auf unveraenderte Kandidatenliste.

Orchestrator trennt DB-Dedup und INSERT, damit der Filter nur auf neue
Kandidaten laeuft (Kostenoptimierung). LATEST_DEVELOPMENTS-Prompt erhaelt
zusaetzliche Relevanz-Gate-Regel als zweite Sicherung.

Hintergrund: Incident 'Russische Militaerblogger' sammelte bisher Iran-,
Nahost- und allgemeine Ukraine-Artikel ein, weil Keyword-Match ab 2 von 8
Begriffen ('iran', 'russland', 'drohne', ...) genuegt. Der semantische
Filter verwirft solche Zufallstreffer.
2026-04-21 12:01:56 +00:00
claude-dev
285df86c7b Export-Metadaten: Umlaut-Fix, xmpMM:VersionID + History
- dc:rights und xmpRights:UsageTerms: Empfaenger -> Empfänger (echte Umlaute)
- Scope-Labels: Vollstaendiger Bericht -> Vollständiger Bericht (zwei Stellen)
- DOCX-Fallback-Text: verfuegbar -> verfügbar
- xmpMM:VersionID: Snapshot-Count der Lage (Proxy fuer Berichts-Revision).
  Router laedt COUNT(*) FROM incident_snapshots und reicht es durch.
- xmpMM:History: Audit-Event pro Export als rdf:Seq-Eintrag mit Timestamp,
  softwareAgent, InstanceID, Scope und Version. Single-Event-Format aus
  pragmatischem Grund (pikepdf-API unterstuetzt keine nativen stEvt-
  Strukturen; Raw-XML-Injection waere dafuer noetig).
2026-04-20 19:33:18 +00:00
claude-dev
5add8d9d59 Export-Metadaten: Dublin Core, xmpRights und xmpMM nachruesten
Zusaetzliche XMP-Felder im PDF:
- dc:publisher (Organisation, Fallback AegisSight)
- dc:identifier (urn:aegissight:incident:<id>:<timestamp>)
- dc:date (Dokumentendatum, ergaenzend zu xmp:CreateDate)
- dc:format (application/pdf)
- dc:type (Report)
- dc:rights (Vertraulichkeitshinweis)
- pdf:Producer im XMP gespiegelt
- xmpRights:Marked (True) und xmpRights:UsageTerms (= dc:rights)
- xmpMM:DocumentID + xmpMM:InstanceID (UUIDs, frisch pro Export)

Damit koennen DMS-Systeme die Berichte versionieren, eindeutig
identifizieren und Vertraulichkeitshinweise anzeigen.
2026-04-20 19:23:54 +00:00
claude-dev
949df868ff Export: XMP-Metadatenblock und CreationDate/ModDate via pikepdf nachziehen
WeasyPrint 68.1 schreibt weder XMP noch Create-/ModDate ins PDF. Das Post-
Processing via pikepdf ergaenzt beide:

- Info-Dict: /CreationDate + /ModDate im PDF-Standardformat
  (D:YYYYMMDDHHmmSS+HHmm) aus Incident.created_at / updated_at
- XMP-Block mit Dublin Core (dc:title, dc:creator, dc:description,
  dc:subject, dc:language), PDF (pdf:Keywords) und XMP (CreatorTool,
  CreateDate, ModifyDate, MetadataDate) Namespaces

Damit werden die Exporte sowohl von klassischen Tools (Explorer, Finder)
als auch von DMS-Systemen (SharePoint, Bridge, Acrobat) vollstaendig
indexiert. Fallback: Bei Fehler im Post-Processing wird das Original-PDF
zurueckgegeben, Export schlaegt nie fehl.
2026-04-20 19:15:14 +00:00
claude-dev
9293e66d01 Export-Metadaten: category_labels JSON-robust parsen, Keyword-Sanitizer
- category_labels ist in der DB ein JSON-Dict (primary/secondary/tertiary/
  mentioned), nicht ein Komma-String. Der bisherige split(",") fuehrte dazu,
  dass ein nacktes { als Keyword durchrutschte. WeasyPrint bricht den
  PDF-Keywords-Stream an dieser Stelle ab, weil { in PDF-Syntax eine
  Sonderbedeutung hat — Ergebnis war "OSINT, Live-Monitoring, AegisSight, {".
- Neuer Parser: erst JSON (Dict oder Liste), Fallback auf Komma-String.
- _sanitize_keyword(): filtert {, }, [, ], Backslash und normalisiert
  Whitespace in allen Keywords (Defense in Depth).
2026-04-20 19:09:38 +00:00
claude-dev
c0f68e40a5 Export: PDF/DOCX-Dateimetadaten (Title, Author, Subject, Keywords, Category, Comments)
- Neue Helper-Funktion _build_export_metadata baut einheitliches Metadaten-Dict
- PDF via HTML-Meta-Tags (title, author, description, keywords, generator, lang)
- DOCX via doc.core_properties (title, author, subject, keywords, comments,
  category, last_modified_by, language, content_status, created, modified)
- Keywords aus OSINT + Typ + Organisation + category_labels + Top-5-Orten
- Comments-Feld mit strukturiertem Block (Incident-ID, Typ, Scope, Umfang, Orte)
- Router laedt Organisation + Top-Orte aus article_locations und reicht sie durch
2026-04-20 18:58:34 +00:00