Produktentscheidung 07/2026: ungenutzte Credits verfallen zum
Periodenende, es gilt der harte Monatsdeckel wie verkauft. Der
Uebertrag (credits_rollover/credits_carried) fliegt komplett raus:
roll_credit_period setzt nur noch Verbrauch + Warnflag zurueck,
check_license rechnet verfuegbar = credits_total, die Migration legt
die beiden Spalten gar nicht erst an (Live hat sie nie bekommen, die
Promote-Warteschlange enthielt sie noch). ABRECHNUNG.md angepasst.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ergaenzt phone im telegram_session-Status, damit das Portal die
Telegram-Zeile im Reiter Recherche-Zugaenge vollstaendig fuellen kann.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neue Key-Value-Tabelle system_status (idempotente Migration). Der Monitor
prueft die Telethon-Session beim Start und taeglich um 04:30 (eigener
Scheduler-Job) und schreibt ok/account/error unter dem Key
telegram_session. Das Verwaltungsportal zeigt den Eintrag im Reiter
Recherche-Zugaenge an, damit ein Session-Ausfall sichtbar wird statt
nur im Server-Log zu stehen. Fehler beim Pruefen stoeren den Betrieb
nie (try/except, eigene DB-Connection wie beim Health-Nachtjob).
Promote-Reihenfolge wie gehabt: Monitor VOR Verwaltung (Migration).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bisher fiel eine 0 durch das or-80-Muster still auf die Voreinstellung
zurück, ein Abschalten der Warnung war unmöglich. Jetzt gilt, 0 = aus,
NULL = Voreinstellung 80, alles andere wie gehabt. Das Verwaltungsportal
bietet die 0 in seinen Formularen an.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neue Tabelle user_activity_days (user_id, day, tenant_id, ein Eintrag je
Nutzer und Kalendertag), angelegt per idempotenter Startup-Migration.
Geschrieben wird sie in get_current_user bei jeder authentifizierten
Nutzung, ein In-Memory-Tagesmerker begrenzt das auf einen INSERT OR
IGNORE je Nutzer und Tag, Fehler werden geschluckt und blockieren nie
eine Anfrage. Gelesen wird die Tabelle nur vom Verwaltungsportal für
die neue MAU/DAU-Statistik. Ein reines Login-Protokoll wäre zu grob,
weil das JWT 24 Stunden hält.
Getestet gegen eine DB-Kopie, Tabelle entsteht, je Nutzer und Tag
genau ein Eintrag trotz Mehrfachaufruf.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Credits-Sätze je Aktion stehen jetzt in der geteilten Tabelle
billing_tariff statt nur in der Konfiguration. Die Migration legt die
Tabelle an und befüllt sie einmalig mit den wirksamen CREDIT_TARIFF-
Werten, danach liest _charge_credits() bei jeder Buchung die Tabelle,
fehlende Schlüssel oder eine fehlende Tabelle fallen auf die
Konfiguration zurück. Damit können Monitor und Verwaltungsportal
dieselben Sätze anzeigen und das Portal kann sie künftig pflegen,
eine Preisänderung braucht keinen Server-Eingriff mehr.
Getestet gegen eine Kopie der Staging-DB. Seed mit 7 Sätzen (45/40/
12/12/1/1/1), Buchung nach Tabelle, Tabellenänderung greift sofort,
Rückfall auf die Konfiguration ohne Tabelle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Credits-Abrechnung ist jetzt auf beiden Seiten, offen aus dem Fork
bleiben die Takt-Features (5b0b578). Zeilenenden-Hinweis präzisiert,
src/static ist CRLF, die Python-Dateien dieses Repos sind LF.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Portierung aus dem Lokal-Fork (AegisSight-Monitor-Local, Commit d2b9168,
per Cherry-Pick übernommen und an den Server-Stand angepasst).
- Feste Sätze je Aktion (BILLING_MODE=flat) statt echter Kosten geteilt
durch cost_per_credit. Sätze auf die am 23.07.2026 beschlossenen
Verkaufswerte gesetzt. Live-Lauf 45, Recherche je Durchlauf 40,
Studio-Bausteine 12, Chat/Beschreibung/Globe 1. Per ENV überschreibbar,
der alte Modus bleibt als BILLING_MODE=actual erhalten.
- Abrechnungsperiode. licenses.credits_period trennt monthly von total,
träger Monatsreset bei der nächsten Lizenzprüfung, optionaler Übertrag
(credits_rollover, gedeckelt auf ein Monatskontingent). Bestandslizenzen
ohne Periodenmarke behalten ihren Verbrauch.
- Hard-Stop gegen das verfügbare Monatskontingent (Kontingent plus
Übertrag) statt gegen das Lebenszeit-Total.
- Warnschwelle budget_warning_percent (Default 80 Prozent) wird erstmals
ausgewertet, einmalige Meldung an alle aktiven Nutzer der Organisation.
- DB-Migration additiv und idempotent (credits_period, credits_period_start,
credits_rollover, credits_carried, budget_warning_sent, unlimited_budget).
- /api/auth/me liefert credits_available als Bezugsgröße plus
credits_period, das Credits-Widget zeigt "Credits diesen Monat".
- Wortlaut überall Credits (nicht Guthaben/Einheiten), docs/ABRECHNUNG.md
auf den Online-Stand gebracht.
Abweichungen zur Fork-Vorlage. Die Takt-Änderungen (Untergrenze 30 Min,
Kostenvorschau im Anlege-Dialog, Fork-Commit 5b0b578) sind bewusst NICHT
enthalten, die Sätze stehen auf 45/40 statt der Fork-Defaults 24/33.
Getestet gegen eine Kopie der Staging-DB, 20 Prüfungen bestanden.
Migration, Flat-Buchung adhoc/research/chat, Warnschwelle einmalig,
Hard-Stop, Monatsreset, Übertrag gedeckelt, Bestandslizenz ohne Marke.
Die vier Live-Lizenzen stehen auf unlimited_budget und sind unbeeinflusst.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neuer CLAUDE.md-Abschnitt legt fest, dass jede Änderung unter src/static
noch in derselben Sitzung ins jeweils andere Repo portiert wird.
Dokumentiert die gewollten Unterschiede, den einheitlichen
Credits-Wortlaut, den Cherry-Pick-Weg über Cross-Remotes und die
CRLF-Stolperfalle. Der Drift-Check liegt im Lokal-Fork unter
scripts/ui-drift-check.sh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Knopf für den Ansichtswechsel steht jetzt links neben "Abmelden" am
Ende der Kopfzeile statt an erster Stelle. Reihenfolge oben rechts damit
Barrierefreiheit, Theme, Konto, Klassische Ansicht, Abmelden.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Portierung aus dem Lokal-Fork (AegisSight-Monitor-Local, Commit 9613610).
Vier Angleichungen an das klassische Dashboard.
1. "Klassische Ansicht" ist jetzt derselbe Knopf wie "Studio-Ansicht" im
Dashboard, mit Symbol, an erster Stelle im rechten Kopfzeilen-Block.
2. Oben rechts fehlten Barrierefreiheit, Konto-Menü und Abmelden. Der
A11yManager liegt jetzt in js/a11y.js und wird von beiden Oberflächen
eingebunden, denn das Studio lädt app.js nicht. Konto-Menü (Organisation,
Lizenz, Credits, Über KI-Inhalte) und Abmelden nachgezogen, der
Theme-Schalter ist derselbe Schiebeschalter wie im Dashboard.
3. Die Fall-Liste hat jetzt die Umschaltung "Alle" / "Eigene" in der
Knopf-Optik der klassischen Seitenleiste, der Reiter-Zähler zieht mit.
4. Der Hinweis "Wähle links einen Fall aus" endet vor der Bausteine-Spalte
statt am Fensterrand, im gestapelten Layout nimmt er die volle Breite.
Das info@-Gating des Studios bleibt unverändert bestehen.
Versionsmarker der angefassten Dateien auf 20260725a angehoben.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fall-Chat und Ereignis-Timeline, alles ueber Claude, strikt fallorientiert.
incidents.py:
- GET /{id}/events: Aktivitaets-Timeline (article_ingest/refresh/analysis/chat_qa/source_change)
- POST /{id}/ask: Fall-Chat/RAG mit Guardrails:
* tools=None (kein Netz/kein Werkzeug) = zentrale Anti-Exfil-Abwehr (GitLost)
* nur Materialien DIESES Falls (scope=alle entfaellt), [n]-Zitate, strikt aus Materialien
* Claude-Vorauswahl der relevanten Artikel statt lokalem Embedding (kein lokales Modell)
* _escape_prompt_content + Ausgabe-Leak-Filter + EchoLeak-Haertung (externe Bilder/Links/URLs raus)
* Verbotsliste: kein Backend/App/Modell/Anbieter, kein Fremd-Fall
* chat_qa in incident_events protokolliert
- Recherche-Angebot: liefert die Materialien die Frage nicht, haengt der Analyst einen
needs_research-Block an (focus + description_addition, NUR aus der Frage abgeleitet).
- POST /{id}/clarify (nur auf Nutzer-Bestaetigung): Beschreibung ergaenzen + fokussierte
Folge-Recherche AUSSCHLIESSLICH zur Frage (Researcher title=focus/description=frage),
url-dedupliziert in den Fall. Laeuft als Hintergrund-Job (stage_runners.start_job).
stage_runners.py: generischer start_job() fuer Hintergrund-Jobs (run-status-sichtbar).
api.js: clarify(). studio.js + studio.css: Angebot rendern, bestaetigen, run-status pollen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Backend fuer die modularen Studio-Pipeline-Bausteine, alles ueber Claude.
DB (additiv, idempotent):
- Tabellen incident_events + fact_check_runs (+ Indizes)
- Funktion log_incident_event()
- Spalten incidents.summary_at, incidents.executive_summary, articles.geoparsed_at
agents/stage_runners.py (neu): isolierte Bausteine analyze + factcheck auf dem
vorhandenen Bestand, komplett neu, Historie bleibt (Snapshot/fact_check_runs).
orchestrator.py: collect_only in die Lane-Multi-Tenant-Queue eingebaut (enqueue_refresh
4-Tupel, _worker-Entpacken, Multi-Pass-Gate, _run_refresh-Signatur + Analyse/FC-Skip).
Zusaetzlich summary_at beim Schreiben des Lagebilds gestempelt (Studio-Freshness).
incidents.py: Endpunkte POST /{id}/run/{stage} (collect|analyze|factcheck),
GET /{id}/run-status, GET /{id}/freshness, GET /{id}/factcheck-runs[/{run_id}].
Busy-Check auf Online-Orchestrator (_current_tasks), Tenant-Zugriffspruefung.
Kein lokaler LLM-Unterbau, kein lokales Modell. /ask + /events folgen in Phase 3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Das blockierende Fortschritts-Overlay (.progress-overlay.blocking) liegt
position:fixed über dem gesamten Viewport und schluckt damit auch die
Klicks auf den globalen Header. Beim ersten Durchlauf eines Falls waren so
Barrierefreiheit, Theme-Wechsel, Konto-Menü inkl. Org-Switcher und
Abmelden nicht erreichbar.
Neue Body-Klasse first-refresh-blocking hebt den Header per z-index über
das Overlay, solange dieses blockierend sichtbar ist. UI._syncHeaderAccess()
spiegelt den Overlay-Zustand an allen Stellen, die das Overlay ein- oder
ausblenden.
Die fallbezogenen Aktionen (Aktualisieren, Bearbeiten, ...) bleiben
unverändert gesperrt: dafür sorgen weiterhin #incident-view.refresh-blurred
und .incident-header-actions.first-refresh-locked.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ersetzt die globale Warteschlange mit einem einzelnen Worker durch
Per-Tenant-Lanes: jede Organisation bekommt eine eigene Queue und einen
eigenen Worker-Task, die unabhaengig und parallel laufen. Lanes werden
lazy angelegt und bei Leerlauf (60s) wieder beendet.
- Skalarer Zustand (_current_task, _cancel_event) -> Maps pro incident_id
(_current_tasks, _cancel_events); _queued_ids/_cancel_requested bleiben
global (IDs sind lane-uebergreifend eindeutig).
- enqueue_refresh/cancel_refresh: Signatur unveraendert, routen intern per
tenant_id in die passende Lane. Oeffentliche Lagen (tenant_id NULL) in
fester PUBLIC_LANE.
- Optionales Ventil ORCHESTRATOR_MAX_PARALLEL (Default 0 = unbegrenzt)
begrenzt bei Bedarf gleichzeitige Recherchen ueber alle Lanes.
- /refreshing-Endpoint auf _current_tasks-Map umgestellt, Response-Format
unveraendert (pro Tenant laeuft weiterhin hoechstens eine Lage).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Die IONOS-Farm (smtp.ionos.de, 10 Backends im DNS-Round-Robin) hatte am
10.07. teils tote/lahme Backends - ein einzelner Sendeversuch schlug dann
fehl (Timed out waiting for server ready message). Jeder Retry oeffnet
eine neue Verbindung und trifft per Round-Robin ein anderes Backend.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live-Monitoring-Lagen (adhoc) zeigten im Export die Entwicklungen als
Bullet-Liste mit "[DD.MM. HH:MM] Text" und Quellen-Links. Jetzt:
- Abschnitt heisst "Neueste Entwicklungen" statt "Zusammenfassung"
(Ueberschrift, Inhaltsverzeichnis). Title folgt dem Inhalt.
- Pro Eintrag eigene Datum/Uhrzeit-Zeile (TT.MM.JJ, HH:MM Uhr),
darunter als eigener Absatz der Meldungstext.
- Keine Links: Quellen-Klammern {Name|URL} und [N]-Zitate werden entfernt.
- Gilt fuer PDF und DOCX. Research-Lagen und KI-Executive-Summary
unveraendert.
Nebenbei: DOCX-Export crashte bei Beschreibungen ueber 255 Zeichen
(python-docx Core-Property "subject" Limit). Subject wird jetzt gekappt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mindest-Aktualisierungsintervall im Lage-Modal: 30 Minuten Basis, 45 bei X oder Telegram, 60 bei X und Telegram zugleich (internationale Quellen ohne Einfluss). Minutenwerte darunter sind im UI nicht mehr einstellbar (min-Attribut, Clamp am Feld und beim Speichern). Beim Umstellen von Stunden auf Minuten wird das Minimum gesetzt und als Hinweis angezeigt. Gilt für Anlegen und Bearbeiten.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Die Typ-Auswahl (Live-Monitoring/Recherche) steht jetzt als erstes Feld vor Titel und Beschreibung, beim Anlegen und beim Bearbeiten (gemeinsames Modal modal-new). Auf ausdrücklichen Wunsch direkt auf main aufgespielt, Staging-Zyklus umgangen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folgeregel zum Neu-am-Fix: verbietet Telegramm-Verkuerzungen
(Stichwort + [Nr]) und Auffangbloecke ohne Aussage (Fruehere Belege
[...]). Jede Quellennummer muss an einem vollstaendigen Satz haengen,
am Abschnitts- oder Lagebild-Ende keine reine Quellennummern-Liste.
Der inkrementelle Analyse-Prompt liess das LLM neue Erkenntnisse als
datierte Changelog-Bloecke (Neu am DD.MM.) anhaengen, die nie eingefaltet
wurden. Beim Iran-Lagebild summierten sich so 151 solcher Bloecke. Punkt 3
fordert jetzt das Einarbeiten in den thematischen Abschnitt; zusaetzliche
STRUKTUR-Regel loest bestehende Neu-am-Bloecke auf. Die chronologische Sicht
bleibt der separaten Kachel Neueste Entwicklungen vorbehalten.
Folgefix zu 952df87. Der Translator-Block laeuft post-summary bei jp_demo
40+ Min und war bisher fuer das Frontend unsichtbar und fuer den Watchdog
ein blinder Fleck (kein Pipeline-Step-Eintrag).
Aenderungen:
- pipeline_tracker.py: neuer Step 'translate' zwischen 'summary' und 'qc'
(DE+EN Label/Tooltip). Bewusst conditional sichtbar: erscheint nur, wenn
fremdsprachige Artikel ohne DE-Uebersetzung vorliegen UND
translator_enabled fuer die Org an ist.
- orchestrator.py: Translator-Block umrandet mit _pipe_start('translate')
und _pipe_done('translate', count_value=uebersetzt, count_secondary=
pending). Translator-Fehler schliesst Step trotzdem sauber ab.
Bedingung 'pending_translations and translator_enabled' ersetzt das
alte 'pending_translations' - skipped den Block sauber wenn Org-Override
deaktiviert (war vorher redundant in translate_articles selbst).
- main.py: ORPHAN_IDLE_LIMIT 30->60 Min, ORPHAN_HARD_LIMIT 90->120 Min.
Deckt jp_demo Translator-Phase (beobachtet bis 41 Min) mit Puffer ab,
ohne echte Haenger durchzulassen.
Resultierend: Frontend zeigt den Uebersetzungs-Schritt mit Fortschritt
(uebersetzt/gesamt). Watchdog killt nicht mehr vorzeitig.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Der bisherige Watchdog markierte jeden running-Refresh nach 15 Min als
verwaist. Bei jp_demo-Lagen laeuft nach summary aber noch der Translator
(synchron, ~20 Min bei 200+ Artikeln), der den Refresh legitim ueber das
Limit traegt - er wurde dann faelschlich abgebrochen und der Orchestrator
hing in-memory weiter mit incident in _current_task.
Neuer Watchdog:
- ORPHAN_IDLE_LIMIT (30 Min): wird der Refresh nur als verwaist markiert,
wenn seit dieser Zeit kein refresh_pipeline_steps-Eintrag Fortschritt
zeigte (started_at oder completed_at)
- ORPHAN_HARD_LIMIT (90 Min): absolute Obergrenze gegen echte Haenger
- Wenn ueberhaupt keine Pipeline-Steps existieren -> als verwaist markieren
Folge: Long-Running-Refreshes (Translator-Block) laufen sauber durch,
nur echte Haenger werden bereinigt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Der Export-Dialog hat ein neues optionales Feld "Ersteller". Ist es
gefuellt, wird dieser Name im Bericht als Ersteller verwendet; bleibt es
leer, gilt wie bisher die E-Mail des Lage-Erstellers.
- export_incident: optionaler Query-Parameter creator, hat Vorrang vor
der E-Mail-Ableitung
- exportReport (api.js) haengt creator an die Export-URL
- submitExport (app.js) liest das neue Feld aus
- Eingabefeld im Export-Modal (dashboard.html)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Die Branding-Auswahl im Export blieb wirkungslos, weil der
Browser die alten gecachten app.js/api.js weiterverwendete.
Versions-Query der beiden Skripte angehoben.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Beim Bericht-Export lässt sich im Modal nun zwischen "Mit
AegisSight-Branding" und "Ohne Firmen-Branding" wählen. Im
neutralen Modus entfallen Logo, AegisSight-Zeile auf dem
Deckblatt und Branding-Footer; die Datei-Metadaten werden
neutralisiert. Das Deckblatt mit Titel, Stand und Ersteller
bleibt erhalten. Betrifft PDF und DOCX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SOURCE_TYPE_PATTERN kannte kein x_account und SOURCE_CATEGORY_PATTERN
kein x. Dadurch schlug das Speichern einer X-Quelle ueber die Monitor-
Oberflaeche mit HTTP 422 fehl: bei neuen X-Quellen am source_type, beim
Bearbeiten bestehender X-Quellen an der Kategorie x. Beide Patterns
ergaenzt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Die Filter-Chips wurden nur eingeblendet, wenn ein Fall Telegram- oder
X-Quellen hatte. Bei reinen Web-Faellen (z.B. in der Org jp_demo) fehlte
die Filterleiste damit komplett. Sie wird jetzt immer angezeigt, sobald
Quellen vorhanden sind, und zeigt zugleich, welche Quellentypen der Fall
enthaelt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>