# Chat und Echtzeit ## Zwei Ebenen - **Aktenchat**: hängt an einer Akte, formlos, ausdrücklich kein Aktenbestandteil. Optisch klar vom Verlauf abgesetzt (siehe Design-System). - **Personalaktenchat**: dasselbe an einer Personalakte. Erbt deren Sichtbarkeit und setzt zusätzlich `personnel.read` voraus. Freie Kanäle gab es einmal. Sie sind entfallen: siehe docs/13-entscheidungen.md. ## Kernfunktion: „Als Vermerk übernehmen" Eine Chatnachricht wird per Klick zu einem `CaseEntry` mit `type = NOTE`. Der erzeugte Eintrag trägt einen Hinweis auf den Ursprung (Autor, Zeitpunkt, Nachrichten-ID). Die Chatnachricht bleibt stehen und bekommt eine Markierung „übernommen". Das ist die Brücke zwischen Discord-Gewohnheit und sauberem Aktenbestand und sollte prominent sein, nicht in einem Kontextmenü versteckt. ## Funktionsumfang - Erwähnungen `@` mit Autovervollständigung aus `User` - Antworten auf einzelne Nachrichten (`replyToId`), zitierte Vorschau - Reaktionen (kleiner fester Satz, keine freie Emoji-Auswahl) - Bearbeiten mit sichtbarem `editedAt` - Löschen nur als Zurückziehen: `withdrawnAt` gesetzt, Platzhalter „Nachricht zurückgezogen" - Ungelesen-Markierung über `ReadMarker`, Trennlinie „Neu seit deinem letzten Besuch" ## Aktenzeichen-Erkennung Text wie `AfiS-2026-0038` wird beim Rendern zu einer Vorschaukarte. **Die Karte wird serverseitig aufgelöst und respektiert die Stufe des Lesers.** Wer die Akte nicht sehen darf, bekommt eine neutrale Karte ohne Titel und ohne Status. Nie den Titel mitschicken und im Client ausblenden. ## Echtzeit über SSE Kein WebSocket, kein Redis. Postgres `LISTEN/NOTIFY` plus ein SSE-Endpunkt reicht bei dieser Nutzerzahl vollständig. ``` GET /api/stream?topics=case:,notifications ``` - Ein einziger `pg`-Client hält `LISTEN` offen und verteilt an die Verbindungen im Prozess - Server sendet alle 25 Sekunden einen Kommentar-Heartbeat gegen Proxy-Timeouts - Client nutzt `EventSource` mit `Last-Event-ID`, bei Reconnect werden verpasste Nachrichten über `?since=` nachgeladen - Reconnect mit exponentiellem Backoff, gedeckelt bei 30 Sekunden - Beim Wechsel des Tabs in den Hintergrund wird die Verbindung nach 5 Minuten geschlossen und bei Rückkehr neu aufgebaut Ereignistypen: `chat.new`, `chat.edit`, `entry.new`, `entry.updated`, `entry.retracted`, `case.update`, `attachment.scanned`, `presence`. `entry.updated` und `entry.retracted` tauschen den Eintrag im offenen Verlauf anhand der ID aus. Wer die Akte gerade offen hat, sieht die Änderung sofort und nicht erst beim Neuladen. ## Dedupe mit Client-IDs Der Client erzeugt die Nachrichten-ID (UUIDv7) selbst und zeigt die Nachricht sofort an. Kommt dieselbe ID über SSE zurück, wird der optimistische Eintrag ersetzt statt verdoppelt. Die ID ist gleichzeitig der Idempotenzschlüssel: ein wiederholtes Absenden nach Netzfehler erzeugt keine zweite Nachricht (`INSERT ... ON CONFLICT DO NOTHING`). ## Discord-Benachrichtigungen Der Worker postet in einen konfigurierbaren Channel und optional als DM an Beteiligte: > Neuer Eintrag in **AfiS-2026-0042** · Insp. Vogel · https://afis.example.de/faelle/AfiS-2026-0042 **Niemals Inhalte, niemals Titel bei Akten ab Stufe 3.** Discord kennt eure Klassifizierung nicht. Der Link führt zur App, die dann selbst prüft. Konfigurierbar pro Nutzer: alles / nur meine Fälle / nur Erwähnungen / aus.