Commit graph

136 Commits

Autor SHA1 Nachricht Datum
Claude Dev
b124208fb9 fix: Rate-Limit-Fehler der Claude CLI korrekt erkennen und loggen
Bisher wurde bei Exit Code 1 nur stderr gelesen, wodurch Rate-Limit-Meldungen
auf stdout (als JSON) verloren gingen. Jetzt wird stdout zusätzlich geprüft und
Rate-Limit-Fehler als [rate_limit] geloggt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 18:44:59 +01:00
claude-dev
91d412a797 fix: Robuster JSON-Parser fuer Analyse-Antworten
Analyse-Antworten von Claude wurden bei langen Outputs nicht korrekt
geparst, wodurch das Lagebild-Briefing eingefroren blieb. Neuer Parser
mit 4-stufigem Fallback: direktes Parsen, Regex-Extraktion,
Reparatur abgeschnittenes JSON, Regex-Fallback fuer Summary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:34:30 +01:00
claude-dev
445f645936 feat: Post-Refresh QC auf Haiku umgestellt
Faktencheck-Duplikate: Fuzzy-Vorfilter (Threshold 0.60) reduziert
Kandidaten, Haiku clustert semantische Duplikate kontextbezogen.
Karten-Locations: Haiku bewertet target-Kategorien anhand des
Lage-Kontexts statt statischer Wortlisten.
Kosten ca. 0.005-0.008 USD pro Check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 21:49:50 +01:00
claude-dev
81a393fd4a feat: Post-Refresh Quality Check fuer Faktenchecks und Karten-Locations
Automatischer QC-Schritt nach jedem Refresh:
- Erkennt inhaltliche Faktencheck-Duplikate via Fuzzy-Matching (Threshold 0.80)
- Korrigiert falsch kategorisierte Karten-Locations (z.B. entfernte Laender als 'target')
- Laeuft nach dem Faktencheck-Commit, vor den Notifications
- Fehler im QC blockieren nicht den Refresh-Ablauf

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 21:41:45 +01:00
claude-dev
5e5267572b Fix feedback attachments: proper MIME mixed/alternative structure
Rewrite feedback.py with correct MIMEMultipart(mixed) containing
alternative text part + base64-encoded image attachments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:42:10 +01:00
claude-dev
11db52d28b Fix feedback NameError (data.message -> message) and suppress aiosqlite DEBUG
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:38:22 +01:00
claude-dev
c3680c3673 Add image attachments to feedback form (JPEG/PNG)
- File input in feedback modal (max 3 images, 5 MB each)
- Frontend validation for file count and size
- Backend: multipart/form-data with UploadFile, MIME attachments
- Images attached to feedback email as base64-encoded attachments
- Only JPEG and PNG allowed (validated server-side)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:35:47 +01:00
claude-dev
8a84b7c306 Add DEV_MODE with comprehensive logging for error diagnosis
- DEV_MODE flag in config.py (env var, default true for dev server)
- Log rotation: 5 MB max, 5 backups (RotatingFileHandler)
- DEBUG level in dev mode, INFO in production
- HTTP request logging middleware (dev mode only, skips static files)
- External library log levels suppressed (httpx, httpcore, uvicorn)
- Customer version: set DEV_MODE=false to disable verbose logging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:33:31 +01:00
claude-dev
22f817c42c Reset tile scroll positions when switching incidents
Reset scrollTop on summary-content and factcheck-list containers
so scroll position from previous incident is not preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:19:53 +01:00
claude-dev
ca3ff1520d Clear old content immediately when switching incidents
Prevents brief flash of previous incident content (Lagebild,
Faktencheck, Quellen, Timeline, title) while new data loads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:18:55 +01:00
claude-dev
25029c2b6a Fix missing quotes in getElementById call
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:14:10 +01:00
claude-dev
1172899dbc Fix scroll-to-factcheck: use scroll event lock during rendering
The double-rAF callbacks from resizeTileToContent fire after the
initial scrollTop=0 reset. Add a scroll event listener that forces
scrollTop=0 during the entire rendering phase, then remove it after
3 animation frames (covering all double-rAF resize callbacks).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:12:50 +01:00
claude-dev
ee6cbee6f2 Fix scroll-to-factcheck: disable GridStack animation during incident switch
Remove grid-stack-animate class before rendering to prevent CSS
transitions from causing browser scroll-follow to repositioned tiles.
Re-enable animation after content is loaded and scroll is reset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:10:28 +01:00
claude-dev
d31ec2a9a8 fix: overflow:hidden VOR Display-Wechsel und GridStack-Init setzen
Der Scroll zum Faktencheck wurde durch den display:none->flex Wechsel
und/oder GridStack-Init ausgeloest. overflow:hidden wird jetzt ganz
am Anfang von selectIncident gesetzt, BEVOR das incident-view
sichtbar wird. Erst 500ms nach Abschluss aller Render-Operationen
wird overflow wiederhergestellt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:02:56 +01:00
claude-dev
163c0c1e8b fix: Scroll-Lock per Event-Listener statt overflow:hidden
Scroll-Event-Listener setzt scrollTop=0 bei jedem Scroll-Versuch
waehrend 500ms nach dem Laden. Verhindert zuverlaessig, dass
GridStack, Leaflet oder Fokus-Aenderungen den View verschieben.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:58:54 +01:00
claude-dev
e745814230 fix: Scroll-Block bis 300ms nach Rendering (GridStack-Resizes abwarten)
overflow:hidden bleibt 300ms nach loadIncidentDetail aktiv, damit
die verzögerten GridStack resizeTileToContent-Operationen keinen
Scroll zum Faktencheck ausloesen koennen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:56:20 +01:00
claude-dev
d983042503 fix: Scroll-Ruckeln beseitigt - overflow:hidden waehrend Rendering
Statt den Scroll nach unten mit Timeouts zurueckzukaempfen, wird
overflow:hidden auf main-content gesetzt bevor das Rendering startet.
Nach Abschluss wird scrollTop=0 gesetzt und overflow wiederhergestellt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:54:45 +01:00
claude-dev
376f0d093b fix: Scroll-to-Top bei Lagewechsel verstaerkt (sofort + rAF + setTimeout)
Scroll-Reset erfolgt nun an 4 Zeitpunkten um sicherzustellen, dass
der Hauptbereich nach oben scrollt, auch wenn GridStack oder andere
asynchrone Rendering-Operationen den Scroll verschieben.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:38:05 +01:00
claude-dev
144787f1e7 fix: Modal nicht mehr per Backdrop-Klick schliessbar + Scroll-to-Top bei Lagewechsel
1. Modal (Anlegen/Bearbeiten) schliesst nur noch ueber den X-Button,
   nicht mehr durch Klick auf den dunklen Hintergrund
2. Beim Anklicken einer Lage in der Sidebar scrollt der Hauptbereich
   nach oben statt zum Faktencheck-Bereich

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:33:15 +01:00
claude-dev
32dbfe99ac fix: sources_json Fallback auf vorherige Quellen wenn Analyse leer
Gleicher Bug wie bei der Summary: Wenn die Claude-API-Analyse keine Quellen
zurueckgibt, wurde sources_json mit NULL ueberschrieben. Dadurch konnten die
Quellenverweise [1], [2] etc. im Dashboard nicht mehr als Links gerendert werden.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:42:37 +01:00
claude-dev
61ca1f54f9 fix: Keine Gedankenstriche (em/en-dash) in Lageberichten
Regel in alle 4 Analyse-Prompt-Templates eingefuegt: Claude soll keine
Gedankenstriche verwenden, sondern Kommas, Doppelpunkte oder neue Saetze.
Bestehende Dashes in DB (Summary + 76 Snapshots) bereinigt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:37:03 +01:00
claude-dev
ad41b8f4ea fix: Leere Summary nicht mehr in DB schreiben - Fallback auf vorherige Summary
Wenn die Claude-API-Analyse keinen oder einen leeren Summary zurueckgibt,
wird jetzt die vorherige Summary beibehalten statt mit einem leeren String
ueberschrieben zu werden.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:33:33 +01:00
claude-dev
6b11d643b9 Fix: Analyse-Parser erkennt jetzt Markdown-Code-Fences
Claude-Antworten mit ```json ... ``` Wrapping werden korrekt geparst.
Verhindert den Verlust von Analyse-Ergebnissen bei inkrementellen Refreshes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 20:55:08 +01:00
claude-dev
1f3b3cb858 Zwei-Phasen-Faktencheck + parallele Analyse/Faktencheck-Ausführung
- Neuer Zwei-Phasen-Faktencheck: Haiku-Triage identifiziert betroffene Fakten,
  dann parallele Opus-Verifikation pro thematischer Gruppe (max 8 Fakten/Gruppe)
- Analyse und Faktencheck laufen jetzt parallel via asyncio.gather
- Snapshot-Erstellung vor parallele Verarbeitung verschoben
- Fallback auf Standard-Faktencheck bei Triage-Fehler
- Erwartete Verbesserung: ~19 Min -> ~8 Min pro Refresh bei gleichbleibender Qualität

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 20:16:45 +01:00
claude-dev
984a5a8184 Increase Claude CLI timeout from 420s to 1800s (30 min)
Lage updates with many articles were consistently timing out at 7 minutes.
Increased to 30 minutes to allow complex analyses to complete.
2026-03-09 17:25:39 +01:00
claude-dev
606c60a815 Geoparsing: Laender vor Staedten pruefen, Alias-Tabelle
Behebt falsche Geocodierung bei Laendernamen die auch als Staedte
existieren (Lebanon->US statt Libanon, Jordan->HK statt Jordanien).

- Laender-Aliase (50+ deutsch/englisch) werden zuerst geprueft
- geonamescache Laendersuche vor Staedtesuche
- Stadtsuche in eigene _geocode_city() Funktion extrahiert
- Bestehende falsche Marker in DB korrigiert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:39:45 +01:00
claude-dev
e761c86a28 Fakten ohne Quellen-URL werden automatisch herabgestuft
_validate_facts() prueft nach dem Parsen: confirmed/established ohne
URL in der Evidenz wird zu unconfirmed/unverified herabgestuft.
183 bestehende confirmed-Fakten ohne URL wurden ebenfalls korrigiert.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:41:31 +01:00
claude-dev
22eaa2f80a Fix: Database-locked-Fehler beim Löschen von Lagen behoben
- PRAGMA busy_timeout=5000 in get_db() hinzugefügt (SQLite wartet bis zu 5s auf Lock-Freigabe)
- Try/except im Delete-Endpoint: gibt HTTP 409 statt 500 bei Lock-Konflikt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:23:27 +01:00
claude-dev
204422ced9 Fakten-Konsolidierung: Evidenz zusammenfuehren statt nur loeschen
Beim Mergen von Duplikaten werden jetzt URLs und Quellen aus allen
Duplikaten in den besten Fakt uebernommen, bevor die Duplikate
entfernt werden. So gehen keine Belege verloren.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:03:25 +01:00
claude-dev
e2ea4eaaa0 Faktencheck-Deduplizierung und Auto-Resolve implementiert
3-Ebenen-System gegen Duplikate:
1. Pre-Dedup: LLM-Antwort wird vor DB-Insert dedupliziert (deduplicate_new_facts)
2. Auto-Resolve: Bestaetigte Fakten loesen automatisch stale developing/unconfirmed Fakten auf
3. Periodische Konsolidierung: Haiku clustert alle 6h semantische Duplikate und entfernt sie

Verbessertes Claim-Matching: SequenceMatcher (70%) + Jaccard-Keyword-Overlap (30%)
statt reinem SequenceMatcher. Threshold von 0.7 auf 0.75 erhoeht.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:59:50 +01:00
claude-dev
62aa63c7fb Bestätigungstext beim Ausschließen von Quellen erweitert
Klarerer Hinweis: betrifft alle eigenen Recherchen, nicht andere Nutzer der Organisation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:12:25 +01:00
claude-dev
13143b9447 Fix: Duplikat-Vorschläge + Stale-Check nur für RSS-Feeds
- Duplikat-Check basiert auf source_id+type statt exaktem Titel
- add_source ohne source_id prüft per Domain-Match
- Stale-Check überspringt web_sources (nur RSS-Feeds prüfen)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:05:45 +01:00
claude-dev
5986d03209 Haiku-Suggester: source_id in Issues-Summary für korrekte Zuordnung
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 17:29:16 +01:00
claude-dev
45d4d35f49 CLAUDE.md: Health-Check Services + DB-Tabellen dokumentiert
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:27:22 +01:00
claude-dev
40f2954811 Täglicher Quellen-Health-Check + Haiku-Vorschläge
- Neue Tabellen: source_health_checks, source_suggestions
- source_health.py: Prüft Erreichbarkeit, Feed-Validität, Aktualität, Duplikate
- source_suggester.py: KI-gestützte Vorschläge via Claude Haiku
- APScheduler Job: Automatischer Check täglich um 04:00

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:26:24 +01:00
claude-dev
6f2aac7313 Domain-ausschließen-Formular aus Toolbar entfernt
Redundant - jede Domain hat bereits einen eigenen Ausschließen-Button.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:40:30 +01:00
claude-dev
5e19736a25 Per-User Domain-Ausschlüsse + Grundquellen-Schutz
- Neue Tabelle user_excluded_domains für benutzerspezifische Ausschlüsse
- Domain-Ausschlüsse wirken nur für den jeweiligen User, nicht org-weit
- user_id wird durch die gesamte Pipeline geschleust (Orchestrator → Researcher → RSS-Parser)
- Grundquellen (is_global) können nicht mehr bearbeitet/gelöscht werden im Frontend
- Grundquelle-Badge bei globalen Quellen statt Edit/Delete-Buttons
- Filter Von mir ausgeschlossen im Quellen-Modal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:30:21 +01:00
claude-dev
18954cf70e Wording: Domain sperren → Domain ausschließen
Einheitliche Umbenennung in UI-Texten, Kommentaren und Docstrings:
- Sperren → Ausschließen
- Gesperrt → Ausgeschlossen
- Entsperren → Ausschluss aufheben

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:02:36 +01:00
claude-dev
2dd11c9db7 DB-Migrationen (status_history, category) + Claude CLI stdin-Übergabe
- status_history Spalte für fact_checks (Faktencheck-Verlauf als JSON)
- category Spalte für article_locations (Marker-Klassifizierung)
- Prompt-Übergabe an Claude CLI via stdin statt Argument (ARG_MAX)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:25:21 +01:00
claude-dev
bcad3e9f3c Theme-Toggle als Schalter + Karten-Vollbild + Zoom-Begrenzung
- Theme-Toggle: Button durch Sun/Moon-Slider ersetzt (Dark Mode Standard)
- Karte: Fullscreen-Overlay mit Expand-Icon, Escape zum Schließen
- Karte: Zoom-Limits (minZoom:2, maxBounds, noWrap)
- Karte: Button 'Orte erkennen' -> 'Orte einlesen', rechts ausgerichtet

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:18:04 +01:00
claude-dev
7734eefd35 Dynamische Keyword-Extraktion fuer RSS-Filterung + min_matches-Fix
- researcher.py: Neuer dedizierter Haiku-Call extract_dynamic_keywords()
  analysiert die letzten 30 Headlines und generiert 5 DE+EN Begriffspaare
- orchestrator.py: Dynamische Keywords vor Feed-Selektion aus DB-Headlines
- rss_parser.py: min_matches auf max 2 gedeckelt (vorher n/2, bei 10 Keywords = 5)
- analyzer.py: Fettdruck-Anweisungen entfernt

Vorher: 0 RSS-Treffer (min_matches=5 unerreichbar)
Nachher: 22 RSS-Treffer (Tagesschau 11, Al Jazeera 5, BBC 4, NYT 2)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 23:12:17 +01:00
claude-dev
29dc457ceb Fix: NoneType.upper() Fehler bei country_code=None im Geoparsing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 22:21:00 +01:00
claude-dev
5ae61a1379 Geoparsing von spaCy auf Haiku umgestellt
- geoparsing.py: Komplett-Rewrite (spaCy NER + Nominatim -> Haiku + geonamescache)
- orchestrator.py: incident_context an geoparse_articles, category in INSERT
- incidents.py: incident_context aus DB laden und an Geoparsing uebergeben
- public_api.py: Locations aggregiert im Lagebild-Endpoint
- components.js: response-Kategorie neben retaliation (beide akzeptiert)
- requirements.txt: spaCy und geopy entfernt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 22:00:40 +01:00
claude-dev
7e600184e8 Geoparsing: Nominatim-Plausibilitätsprüfung + regionale Stopwords
- SequenceMatcher prüft ob Nominatim-Ergebnis zum Suchbegriff passt (Schwelle 0.3)
- Verwirft Fehlzuordnungen wie 'Golf-Staaten' -> Uganda
- Regionale/vage Begriffe als Stopwords (Naher Osten, Golf-Staaten, Balkan etc.)
- Falscher DB-Eintrag (Botschafter-Residenz Uganda) bereinigt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 21:00:01 +01:00
claude-dev
3c4170bead Karte: Standard-Höhe auf h:8 (640px) erhöht
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 20:42:38 +01:00
claude-dev
ba8f807c96 Fix: Refresh-Verlauf zeigte Zeiten 1 Stunde in der Zukunft
parseUTC() hängte blind ein 'Z' (UTC) an alle Timestamps, aber
die DB speichert Lokalzeit (Europe/Berlin). Bei CET (UTC+1) wurden
alle Zeiten 1 Stunde zu spät angezeigt.

Neue Logik: Timestamps mit 'Z' oder '+' werden direkt geparst.
Timestamps ohne Zeitzonen-Info werden als Europe/Berlin interpretiert
mit korrektem Offset (auch bei Sommer-/Winterzeit-Wechsel).

Betrifft: Refresh-Verlauf, Artikel-Zeitstempel, Snapshots, Lagebild-Stand

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:33:04 +01:00
claude-dev
1c7adafa70 Fix: Progress-Timer zeigte negative Zahlen (-58:-10)
Ursache: Server sendete started_at als Lokalzeit (Europe/Berlin),
aber der Client interpretierte es als UTC via parseUTC().
Bei UTC+1 lag die Startzeit dadurch 1 Stunde in der Zukunft.

- orchestrator.py: started_at in WebSocket-Nachrichten als echtes UTC
  (ISO 8601 mit Z-Suffix) senden, DB-Timestamps bleiben Lokalzeit
- components.js: elapsed auf min. 0 clampen als Sicherheitsnetz

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:51:33 +01:00
claude-dev
f7809ccc77 Fix: TimeoutError wird nicht mehr verschluckt - Retry greift jetzt
- researcher.py/factchecker.py: TimeoutError wird nach oben durchgereicht
  statt vom breiten except Exception geschluckt zu werden
- orchestrator.py: Built-in TimeoutError zu TRANSIENT_ERRORS hinzugefuegt
  (war nur asyncio.TimeoutError, aber claude_client wirft TimeoutError)
- config.py: CLAUDE_TIMEOUT von 300s auf 420s erhoeht

Vorher: Timeout fuehrte zu "0 Artikel" ohne Retry (8 Timeouts seit 28.02.)
Nachher: Timeout loest bis zu 3 Retries aus (sofort, +2min, +5min)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:48:48 +01:00
claude-dev
ac3291f608 fix: Alle Zeitanzeigen fest auf Europe/Berlin Zeitzone
Alle toLocaleTimeString/toLocaleDateString-Aufrufe und
getHours/getMinutes-Zugriffe verwenden jetzt fix die
Zeitzone Europe/Berlin statt der Browser-Zeitzone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:31:21 +01:00
claude-dev
a69352575d Fix: Komplett auf Europe/Berlin + DB-Migration + Timer-Fix
- ALLE Timestamps einheitlich Europe/Berlin (kein UTC mehr)
- DB-Migration: 1704 bestehende Timestamps von UTC nach Berlin konvertiert
- Auto-Refresh Timer Fix: ORDER BY id DESC statt completed_at DESC
  (verhindert falsche Sortierung bei gemischten Timestamp-Formaten)
- started_at statt completed_at fuer Timer-Vergleich (konsistenter)
- Manuelle Refreshes werden bei Intervall-Pruefung beruecksichtigt
- Debug-Logging fuer Auto-Refresh Entscheidungen
- astimezone() fuer Timestamps mit Offset-Info

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 02:56:51 +01:00