# Pyramid – Arbeitsprotokoll

Neueste Einträge OBEN. Jede Claude-Session schreibt hier nach jedem abgeschlossenen
Schritt (und beim Abbruch mitten im Schritt den Zwischenstand). Format:

```
## JJJJ-MM-TT – Kurztitel
**Erledigt:** was fertig ist (mit Commit-Hash)
**Offen/Nächster Schritt:** der konkrete nächste Handgriff
**Stolperfallen:** was schiefging und wie es gelöst wurde (damit es nie zweimal passiert)
**Fragen an Bernd:** (optional) gesammelte Richtungsfragen, die nicht blockieren
```

---

## 2026-07-03 – Session-Check: weiterhin kein sicher bearbeitbarer Punkt auf dem Pi

**Erledigt:** Vor dem Weiterarbeiten Arbeitszyklus komplett durchlaufen: `PROGRESS.md`
+ `ROADMAP.md` gelesen, `flutter analyze` (weiterhin sauber, nur der eine bekannte
Hinweis in `chat_provider.dart:42`), `git status` (sauber, `master` = `origin/master`),
`flutter doctor` erneut geprüft (Android-SDK, Chrome, Linux-Desktop-Toolchain fehlen
weiterhin auf diesem Pi – keine GUI möglich). Damit alle offenen Punkte in M1/M2
gegengeprüft:
- M1 „SQLCipher" und „Härtetest dokumentieren": unverändert PC-only (siehe Einträge
  weiter unten), keine neue Information seit letzter Session.
- M2 „Call-Schicht entwirren": zum Einstieg `core/voip_manager.dart` (538 Zeilen)
  gelesen, um eine Fassade zu entwerfen. Ergebnis: Das ist **kein** Datei-Split wie die
  letzten 5 Gott-Datei-Umbauten (dort: reines `part`/`part of`, null Logikänderung,
  automatisiert byte-genau verifizierbar). `PyramidVoipManager` ist ein Singleton
  (`_instance`), der gleichzeitig `ChangeNotifier` UND die vom matrix-SDK verlangte
  `WebRTCDelegate`-Schnittstelle implementiert, mit Timer-basiertem Renderer-Polling,
  impliziten Reihenfolgen bei Mute/Unmute/Screenshare und einem `MediaDevicesWrapper`,
  der direkt auf private Felder des Managers zugreift. Eine Fassade davor UND die
  Umstellung der 7 Importstellen lässt sich nicht mehr rein mechanisch verifizieren –
  echtes Risiko für Verhaltensänderung an Calls, die echte Nutzer (Uta) im Einsatz
  haben. Genau das hatte die vorige Session bereits zweimal als „erste Stelle mit
  echtem Verhaltensrisiko in M2" geflaggt und Bernds Entscheidung dazu angefragt
  (siehe „Fragen an Bernd" weiter unten) – hier bestätigt und bewusst NICHT
  eigenmächtig angefasst, weil auf diesem Pi ohnehin kein GUI-Test möglich ist, um
  eine Regression aufzufangen.

**Offen/Nächster Schritt:** Es gibt aktuell **keinen** ROADMAP-Punkt in M1/M2, der auf
diesem Pi ohne (a) Bernds Antwort auf die Call-Pilot-Frage oder (b) echten PC/GUI-Zugriff
sicher bearbeitet werden kann. Nächster sinnvoller Schritt ist entweder ein PC-Termin
(Härtetest + SQLCipher-Migrationsplanung + Call-Fassade mit echtem Test) oder Bernds
kurze Antwort auf die unten stehende Frage, damit die nächste Pi-Session direkt starten
kann.

**Stolperfallen:** Keine neuen – Bestätigung, dass die Vorsicht der letzten Session
richtig war: „sieht aus wie ein weiterer Datei-Split" ist bei der Call-Schicht trügerisch,
weil Singleton-Lifecycle + SDK-Delegate-Vertrag + Timer-Polling nicht mit einem
Textvergleich absicherbar sind wie bei reinen `part`/`part of`-Umbauten.

**Fragen an Bernd:** Unverändert offen (wiederholt aus den letzten beiden Einträgen,
damit sie nicht übersehen wird):
1. Call-Pilot (Fassade für `voip_manager.dart`/`livekit_call_manager.dart`) jetzt
   angehen (nur auf einem PC mit echtem Test), oder erstmal zurückstellen?
2. SQLCipher-Verschlüsselung der lokalen DB: Priorität vor M2, oder reicht die
   App-Sandbox als Schutz vorerst?
Ohne eine dieser beiden Antworten bleibt der Pi bei jedem weiteren Durchlauf an
derselben Stelle stehen – ein kurzer PC-Termin für Härtetest + Call-Pilot würde
gleich mehrere blockierte Punkte auf einmal lösen.

---

## 2026-07-03 – M2: Gott-Datei `chat_view.dart` aufgeteilt (6 Dateien) – ROADMAP-Punkt abgehakt

**Erledigt:** Fünfter und letzter Durchgang derselben `part`/`part of`-Technik:
`features/chat/chat_view.dart` (2116 Zeilen, 22 Klassen, keine losen
Top-Level-Helfer) aufgeteilt in 6 Dateien unter `features/chat/view/`:
`chat_view_core.dart` (`ChatView`, `_ChatViewState` – der komplette
Nachrichten-Senden/Scroll/Attachment-Zustandsautomat, mit 521 Zeilen
weiterhin der größte Einzelblock, aber nicht weiter unterteilt, da es EINE
zusammenhängende State-Klasse ist, keine künstliche Aufspaltung mitten in
einer Klasse), `chat_header.dart` (`_ChatHeader`, DM-Header inkl.
`_DmExpandingHeader`+State, `_DmHeaderAvatar`, `_InitialCircle`,
`_PresenceLine`+State), `chat_connection.dart` (`_ConnectionBanner`+State,
Offline/Reconnect-Banner), `chat_message_list.dart` (`_MessageList`,
`_SelectionHeader`, `_SelBtn`, `_UnreadDivider`), `chat_typing.dart`
(`_TypingIndicator`+State, `_TypingDots`+State), `chat_misc.dart`
(`_DragOverlay`+State für Datei-Drag&Drop, `NoChatSelected` – bleibt
öffentlich, `app_shell.dart` importiert `chat_view.dart` und nutzt es).
`chat_view.dart` selbst: nur noch Bibliothekskopf (33 Zeilen).

Verifikation identisch zu den vier vorigen Umbauten: automatisierte
Klammer-Zählung, alle 22 Blöcke wortwörtlich wiedergefunden,
`class`/`enum`-Anzahl (22) und `Widget build(`-Methoden (15) vorher/nachher
identisch, `flutter analyze` unverändert bei 1 bekanntem Hinweis.

**Damit sind alle 5 in der M2-Ist-Analyse gefundenen Gott-Dateien aufgeteilt**
(`settings_modal.dart`, `message_group.dart`, `rooms_panel.dart`,
`space_admin_dialog.dart`, `chat_view.dart` – siehe die vier Einträge
darunter). ROADMAP-Punkt „Toten Code & Duplikate entfernen, Ordnerstruktur
vereinheitlichen" jetzt abgehakt.

**Offen/Nächster Schritt:** **Wichtig, bevor der nächste ROADMAP-Punkt
angefasst wird:** all diese Aufteilungen sind bisher NUR per `flutter
analyze` + automatisiertem Textvergleich geprüft, NICHT in echter UI
gesehen (kein GUI-Toolchain auf dem Pi verfügbar – kein Android-SDK, kein
Linux-Desktop-Build, siehe `flutter doctor`). Der nächste PC-/Windows-Lauf
MUSS vor dem nächsten inhaltlichen Schritt einmal durch alle 5 betroffenen
Bereiche klicken (Einstellungen komplett, ein Chat mit allen
Nachrichtentypen, die Raumliste, eine Space-Verwaltung, ein normaler
Chat-Screen mit Header/Tippanzeige/Drag&Drop) – das Risiko einer reinen
`part`/`part-of`-Verschiebung ist strukturell gering (Dart-Compiler hätte
jeden fehlenden Import/jede falsche Sichtbarkeit sofort als Analyzer-Fehler
gemeldet, nicht erst zur Laufzeit), aber „gering" ist nicht „null", und
CLAUDE.md verlangt nach jedem Refactoring-Schritt einen echten App-Start.
Danach nächster ROADMAP-Punkt in M2: „Call-Schicht entwirren" – dafür
weiterhin Bernds Entscheidung zum Call-Pilot aus „M2: Modul-Schnitt
definiert" ausstehend, UND dieser Punkt braucht ohnehin einen PC/Windows-Test
(Verhaltensänderung an State-Management, nicht nur Datei-Verschiebung).

**Stolperfallen:** Keine neuen. Generelle Lehre aus allen 5 Durchgängen:
Skript-gestützte Klammer-Zählung + automatisierter Verbatim-Abgleich
(„jeder Originalblock muss wortwörtlich in den neuen Dateien auftauchen")
war der entscheidende Sicherheitsnetz-Schritt, um trotz fehlendem GUI-Test
auf dem Pi mit vertretbarem Risiko fortzufahren – sollte bei künftigen
reinen Datei-Umbauten (z. B. `voice_channel.dart` beim Call-Pilot) als
Vorgehen wiederverwendet werden.

---

## 2026-07-03 – M2: Gott-Datei `space_admin_dialog.dart` aufgeteilt (6 Dateien)

**Erledigt:** Vierter Durchgang derselben `part`/`part of`-Technik:
`features/spaces/space_admin_dialog.dart` (2278 Zeilen, 32 Klassen/Enums +
6 lose Top-Level-Helfer) aufgeteilt in 6 Dateien unter
`features/spaces/admin/`: `space_admin_core.dart` (`SpaceAdminDialog`,
`_SpaceAdminDialogState`, `_DialogHeader` + die breit genutzten Helfer
`_kSpaceBannerEvent`, `updatePowerLevelsSafely` (öffentliche Funktion –
schützt vor Selbst-Aussperrung beim Ändern der Power-Levels, genutzt von
Members- UND Permissions-Tab), `_kPowerAdmin`/`_kPowerMod`/`_kPowerMember`,
`_powerLabel`, `_powerColor`), `space_admin_overview.dart` (`_OverviewTab`+State,
`_SpaceVisibilityToggle`+State, `_DangerZone`), `space_admin_members.dart`
(`_MembersTab`+State, `_InviteButton`+State, `_MemberTile`+State,
`_MemberActions`, `_MemberAction`), `space_admin_permissions.dart`
(`_PermissionsTab`+State, `_PermissionRow`), `space_admin_channels.dart`
(`_ChannelsTab`+State, `_ChannelItem`+State, `_CreateChannelDialog`+State,
`_AddExistingDialog`+State + die dort lokal genutzten Helfer `_kSpaceChild`/
`_kSpaceParent`/`_kVoiceChannelType`), `space_admin_shared.dart`
(`_SectionLabel`, `_Field`, `_SearchField`, `_AvatarEditor`, `_InitialAvatar`).
`space_admin_dialog.dart` selbst: nur noch Bibliothekskopf (19 Zeilen).

Auffällig beim Durchlesen (nicht angefasst, nur notiert): `updatePowerLevelsSafely`
ist als einzige Funktion in dieser Datei öffentlich (kein `_`-Präfix), wird
aber nirgends außerhalb von `space_admin_dialog.dart` importiert/aufgerufen –
vermutlich für einen mal geplanten, nie gebrauchten externen Aufrufer public
gemacht. Bewusst nicht auf privat zurückgestuft, um diesen Schritt nicht mit
einer inhaltlichen Änderung zu vermischen; wäre ein Kandidat für den nächsten
"toter Code"-Durchgang.

Verifikation wie bei den drei vorigen Umbauten: automatisierte
Klammer-Zählung für Blockgrenzen, alle 32 Blöcke + 4 Helfer-Gruppen
wortwörtlich wiedergefunden, `class`/`enum`-Anzahl (32) und
`Widget build(`-Methoden (20) vorher/nachher identisch, `flutter analyze`
unverändert bei 1 bekanntem Hinweis.

**Offen/Nächster Schritt:** UNGETESTET (Pi). Nächster Windows-Lauf: Space-
Verwaltung öffnen – Übersicht (Sichtbarkeit umschalten, Banner ändern,
Danger-Zone-Buttons NUR ansehen, nicht bestätigen), Mitglieder (einladen,
Rolle ändern, kicken/bannen – wenn möglich an einem Test-Space, nicht an
Utas echtem), Berechtigungen, Kanäle (erstellen, bestehenden Raum hinzufügen).
Danach `chat_view.dart` (2116 Zeilen) als letzte der 5 in der Ist-Analyse
gefundenen Gott-Dateien – damit wäre der komplette „Ordnerstruktur
vereinheitlichen"-Katalog aus M2 abgearbeitet (Häkchen dann setzen).

**Stolperfallen:** Keine neuen.

---

## 2026-07-03 – M2: Gott-Datei `rooms_panel.dart` aufgeteilt (8 Dateien)

**Erledigt:** Gleiche `part`/`part of`-Technik ein drittes Mal angewandt:
`features/rooms/rooms_panel.dart` (2555 Zeilen, 37 Klassen/Enums + 4 lose
Top-Level-Helfer) aufgeteilt in 8 Dateien unter `features/rooms/panel/`:
`rooms_panel_core.dart` (`RoomsPanel`, `_RoomsPanelState` + die
Top-Level-Helfer `_kVoiceRoomType`/`_kSpaceBannerType`/`_isVoiceRoom`/
`_compareSpaceChildren`, die von mehreren anderen Teilen genutzt werden –
deshalb bewusst NICHT in ein eigenes „shared"-File, sondern beim Hauptwidget
belassen, wo sie ursprünglich standen), `rooms_header.dart` (`_Header`,
`_MenuRow`, `_NotificationsButton`, `_FilterInput`, `_SpaceMenuItem`),
`rooms_space_invite.dart` (`_SpaceInviteView`+State), `rooms_space_list.dart`
(`_SpaceRoomsList`+State, `_JoinableRoom`+Item+State, `_CategoryHeader`+State),
`rooms_list.dart` (`_RoomsList`, `_SectionHeader`+State, `_RoomItem`+State,
`_DraggableRoomItem`+State), `rooms_voice.dart` (`_VoiceParticipantList`,
`_Initials`, `_VoiceBarBtn`+State), `rooms_invite_item.dart`
(`_InviteItem`+State), `rooms_shared.dart` (`_DmAvatar`+State,
`_InitialCircle`, `_RoomMenuBtn`+State, `_HoverAction`+State, `_UnreadBadge`).
`rooms_panel.dart` selbst: nur noch Bibliothekskopf (Imports + 8
`part`-Direktiven, 31 Zeilen).

Verifikation wie bei den beiden vorigen Umbauten: automatisierte
Klammer-Zählung für Blockgrenzen, alle 37 Blöcke + 4 Helfer wortwörtlich in
den neuen Dateien wiedergefunden, Anzahl `class`/`enum` (37) und
`Widget build(`-Methoden (22) vorher/nachher identisch, `flutter analyze`
unverändert bei 1 bekanntem Hinweis. `dart format` wieder bewusst nicht
gelaufen.

**Offen/Nächster Schritt:** UNGETESTET (Pi). Nächster Windows-Lauf: Raumliste
prüfen – Space wechseln, Raum beitreten/verlassen, Voice-Channel-Teilnehmerliste
in der Raumliste, Einladung annehmen/ablehnen, Drag&Drop zum Umsortieren,
Ungelesen-Badge, DM-Avatare. Danach `space_admin_dialog.dart` (2278 Zeilen)
oder `chat_view.dart` (2116 Zeilen) nach demselben Muster – damit wären alle
in der Ist-Analyse gefundenen Gott-Dateien abgearbeitet.

**Stolperfallen:** Keine neuen.

---

## 2026-07-03 – M2: Gott-Datei `message_group.dart` aufgeteilt (7 Dateien)

**Erledigt:** Gleiche Technik wie beim `settings_modal.dart`-Umbau direkt
darunter angewandt: `features/chat/message_group.dart` (2775 Zeilen, 41
Klassen/Enums + 5 lose Top-Level-Hilfsfunktionen) per Dart `part`/`part of`
in 7 thematische Dateien unter `features/chat/message/` aufgeteilt:
`message_group_core.dart` (`MessageGroup`, `_MessageGroupState`,
`_SendFailedRow`, `_MessageBody`, `_ContinuationMessage`(+State), inkl. der in
der letzten Session vereinheitlichten `_formatMessageTime`), `message_text.dart`
(`_MessageContent`, `_MatrixHtmlText`, `_PlainLinkText`),
`message_link_preview.dart` (`_LinkPreview`(+State/Data), `_ReplyPreview`),
`message_media.dart` (alle Medientypen: Bild/Audio/Video/Doc/Datei/proprietäre
Inline-Karten + die zugehörigen Top-Level-Helfer `_reservedImageSize`/
`_classifyMediaError`/`_checkedDownload`/`_mediaFallback` – mit 1000 Zeilen
weiterhin der größte Teil, aber thematisch klar abgegrenzt),
`message_reactions.dart`, `message_actions.dart` (Hover-Aktionsleiste),
`message_shared.dart` (`_Avatar`, `DateDivider` – bleibt öffentlich, da
`chat_view.dart` es importiert; über `part`/`part of` weiterhin ohne
Umbenennung nutzbar). `message_group.dart` selbst ist jetzt nur noch der
Bibliothekskopf (Imports + 7 `part`-Direktiven, 30 Zeilen).

Verifikation identisch zum Settings-Umbau: automatisierte Klammer-Zählung
für die Blockgrenzen (kein manuelles Abtippen), alle 41 Original-Blöcke +
5 Helfer wortwörtlich in den neuen Dateien wiedergefunden, Anzahl
`class`/`enum` (45) und `Widget build(`-Methoden (27) vorher/nachher
identisch, `flutter analyze` unverändert bei 1 bekanntem Hinweis. `dart format`
diesmal bewusst NICHT ausgeführt (siehe Stolperfalle im Eintrag darunter).

**Offen/Nächster Schritt:** UNGETESTET (Pi). Nächster Windows-Lauf: einen
Chat mit mehreren Nachrichtentypen öffnen (Text mit Link-Vorschau, Bild,
Video, Audio, Datei-Anhang, Reaktionen setzen, Hover-Aktionsleiste,
Datums-Trenner, gescheiterte Nachricht mit Retry) – rein mechanische
Verschiebung, aber `message_media.dart` ist der bisher am wenigsten oft
geprüfte Bereich (Download/Entschlüsselungs-Fehlerpfade), daher dort genauer
hinschauen. Danach ggf. `rooms_panel.dart` (2555 Zeilen) oder
`space_admin_dialog.dart` (2278 Zeilen) nach demselben Muster aufteilen.

**Stolperfallen:** Keine neuen – siehe `dart format`-Hinweis im
`settings_modal.dart`-Eintrag darunter, hier von vornherein vermieden.

---

## 2026-07-03 – M2: Gott-Datei `settings_modal.dart` aufgeteilt (12 Dateien)

**Erledigt:** `widgets/settings_modal.dart` (5541 Zeilen, 50+ Klassen) war laut
Ist-Analyse die größte „Gott-Datei". „Call-Schicht entwirren" (nächster
ROADMAP-Punkt) bleibt weiter blockiert, da es dabei um Verhaltens-/State-
Management-Änderungen an einem CLAUDE.md-„heiligen" Bereich (Calls) geht, die
laut Arbeitsregeln nach jedem Schritt einen echten App-Test brauchen –
auf dem Pi ohne GUI-Toolchain (kein Android-SDK, kein Linux-Desktop-Build,
siehe `flutter doctor`) unmöglich zu prüfen. Stattdessen den in
`docs/M2_MODULE_SCHNITT.md` als zweiten (risikoärmeren) Schritt vorgeschlagenen
Umbau vorgezogen: reine Datei-Aufteilung ohne Verhaltensänderung, komplett über
`flutter analyze` prüfbar.

- Technik: Dart `part`/`part of` statt eigenständiger Libraries – dadurch
  bleiben alle privaten Klassen (`_ProfileSection`, `_SettingsGroup`, …) ohne
  Umbenennung nutzbar, und alle Imports bleiben zentral in `settings_modal.dart`
  (Part-Dateien brauchen keine eigenen Imports). Kein Klassenname geändert,
  kein Zeichen Logik angefasst.
- 12 neue Dateien unter `lib/widgets/settings/`: `settings_shared.dart`
  (gemeinsame Low-Level-Widgets wie `_SettingsGroup`/`_Toggle`/`_Divider`),
  `settings_profile.dart`, `settings_notifications.dart`,
  `settings_appearance.dart`, `settings_voice.dart`, `settings_keybinds.dart`
  (Tastaturkürzel-Anzeige, NICHT zu verwechseln mit Verschlüsselungs-Keys),
  `settings_privacy.dart`, `settings_sessions.dart`, `settings_verification.dart`
  (SAS/QR-Dialog), `settings_encryption.dart` (inkl. der Megolm-Export/Import-
  Kryptofunktionen `_runPbkdf2`/`_aesCtr`/`_encryptMegolmKeys`/`_decryptMegolmKeys`,
  die bisher lose im Datei-Kopf lagen), `settings_account.dart` (Passwort ändern/
  Account löschen), `settings_about.dart`.
- `settings_modal.dart` selbst: von 5541 auf 270 Zeilen geschrumpft – enthält
  nur noch Imports, die 12 `part`-Direktiven, `SettingsModal` und
  `_SettingsModalState` (Navigation/Layout).
- Verifikation (da kein GUI-Test möglich): Skript-gestützte Extraktion per
  Klammer-Zählung (kein manuelles Copy-Paste, um Übertragungsfehler
  auszuschließen); anschließend automatisiert geprüft, dass alle 61
  ursprünglichen Klassen/Enums wortwörtlich (`in`-Substring-Check) in den neuen
  Dateien wieder auftauchen; Anzahl `class`/`enum`-Deklarationen (60) und
  `Widget build(`-Methoden (40) vorher/nachher identisch; alle Lücken zwischen
  den Klassenblöcken im Original einzeln geprüft – ausschließlich Leerzeilen
  und dekorative Trennkommentare, kein Code verloren.
- `flutter analyze`: weiterhin nur der eine bekannte, zurückgestellte Hinweis
  (`chat_provider.dart:42`).
- **Stolperfalle vermieden:** `dart format` versuchte 40 neue Lint-Hinweise
  einzuführen (`curly_braces_in_flow_control_structures`) – Ursache: die
  Originaldatei war selbst nie komplett `dart format`-clean (bestätigt per
  `dart format --set-exit-if-changed` auf dem Original-Git-Stand), und Reflow
  einzelner langer Einzeiler-`if`s ohne Klammern erzeugte neue Zeilenumbrüche,
  die der Linter anders bewertet als die Originalformatierung. Formatierung
  komplett verworfen und die Dateien unformatiert aus dem Original übernommen,
  um „keine Verhaltensänderung ohne Not" strikt einzuhalten und die
  `flutter analyze`-Baseline (1 Hinweis) nicht zu verwässern.

**Offen/Nächster Schritt:** UNGETESTET (Pi) im Sinne von „nicht in echter UI
gesehen". Nächster Windows-Lauf: Einstellungen öffnen, durch alle Reiter
klicken (Profil, Benachrichtigungen, Erscheinungsbild, Voice, Tastaturkürzel,
Privatsphäre, Sessions, Verschlüsselung inkl. SAS/QR-Verifizierung und
Recovery-Key-Anzeige/-Import, Passwort ändern, Account löschen-Dialog öffnen
(ohne zu bestätigen!), Über). Da rein mechanisch verschoben, ist das Risiko
gering, aber Verschlüsselung/Recovery-Key ist laut CLAUDE.md heilig – bitte
gezielt den Recovery-Key-Anzeige- und Passphrase-Export/Import-Flow einmal
durchklicken. Danach ROADMAP-Häkchen für „Ordnerstruktur vereinheitlichen"
erst setzen, wenn auch die übrigen Gott-Dateien (`message_group.dart`,
`rooms_panel.dart`, `space_admin_dialog.dart`, `chat_view.dart`,
`document_viewer.dart`) angegangen sind – aktuell nur `settings_modal.dart`
erledigt.

**Stolperfallen:** `dart format` NICHT blind nach einem Refactoring laufen
lassen, wenn die Baseline vorher nicht schon formatter-clean war – kann neue,
unrelated Lint-Hinweise einführen und die vermeintlich reine Verschiebung mit
Formatierungsrauschen vermischen (siehe oben).

**Fragen an Bernd:** Keine neuen – die offene Frage zum Call-Pilot aus dem
Eintrag „M2: Modul-Schnitt definiert" ist weiterhin unbeantwortet, blockiert
aber nicht (siehe dort).

---

## 2026-07-03 – M2: Toten Code & Duplikate entfernt (Chat-Timeline)

**Erledigt:** Vorgriff auf den ROADMAP-Punkt „Toten Code & Duplikate entfernen"
(technisch der 5. Punkt in M2, aber risikofrei und ohne GUI testbar – im
Gegensatz zum aktuell blockierten „Call-Schicht entwirren", siehe Frage an
Bernd im Eintrag darunter):
- `lib/features/chat/message_bubble.dart` (178 Zeilen) gelöscht – vollständig
  toter Code, wurde nirgends importiert/instanziiert (durch `message_group.dart`
  abgelöst), per `grep` doppelt bestätigt.
- Die zwei identischen `_formatTime`-Methoden in `message_group.dart`
  (`_MessageGroupState` Zeile 322, `_ContinuationMessageState` Zeile 475 –
  Vorher-Zeilennummern) zu einer gemeinsamen Top-Level-Funktion
  `_formatMessageTime()` zusammengeführt, beide Aufrufstellen umgestellt.
  Reine Extraktion, keine Verhaltensänderung (identischer Code vorher/nachher).
  `pinned_messages_panel.dart:_formatTime` bewusst NICHT angefasst – hat
  andere Logik (relatives Datum „vor Xd"), ist kein echtes Duplikat.
- `flutter analyze` weiterhin sauber: nur der eine bekannte, bewusst
  zurückgestellte Hinweis (`chat_provider.dart:42`, `Timeline.onUpdate`
  deprecated, hängt am E2EE-Redecrypt-Pfad, siehe M0-Eintrag).

**Offen/Nächster Schritt:** UNGETESTET (Pi) im Sinne von „nicht in echter UI
gesehen" – kein GUI auf diesem Pi verfügbar. Da es sich aber um reine
Code-Verschiebung ohne Logikänderung handelt (identischer Funktionskörper),
ist das Risiko gering; trotzdem beim nächsten Windows-Lauf kurz einen
Chat mit mehreren aufeinanderfolgenden Nachrichten (Zeit-Anzeige) und eine
Fortsetzungsnachricht (Hover zeigt Uhrzeit) ansehen.

**Stolperfallen:** Keine.

---

## 2026-07-03 – M2: Modul-Schnitt definiert (docs/M2_MODULE_SCHNITT.md)

**Erledigt:** Ausgehend von der Ist-Analyse (Eintrag darunter) die konkreten
Modul-Grenzen + öffentliche Schnittstellen entworfen, dokumentiert in
`docs/M2_MODULE_SCHNITT.md`. Reine Planung, kein Code geändert. Wichtigste
Erkenntnis beim genaueren Lesen von `voip_manager.dart` und
`livekit_call_manager.dart`: „Calls" ist architektonisch **kein einzelnes
Feature**, sondern zwei unabhängige Mechanismen, die getrennt bleiben sollten:
- `PyramidVoipManager` = natives Matrix-1:1-VoIP (`m.call.*`, direkte
  WebRTC-Peer-Connection, kein SFU)
- `LiveKitCallManager` = SFU-basierte Voice-Channels (LiveKit-Raum, Presence
  via Matrix-State-Event, Screensharing, Mehrpersonen) – das ist der
  Discord-artige Kanal aus M3/M4

Modul-Liste im Dokument: `call_signaling`, `voice_channel`, `call_ui`,
`settings` (aufgesplittet in Sektionsdateien), `verification` (neu, aus
Settings herausgelöst – SAS/QR/Recovery-Key), `storage` (Ausbau der
bestehenden `settings_prefs.dart`-Abstraktion zur Pflicht), `auth` (schon
relativ sauber, kein akuter Bedarf), `push` (schon getrennt, eigener
`sqflite`-Zugriff in `background_push.dart` ist bewusst so, kein
Refactoring-Kandidat), `chat_timeline`/`rooms` (niedrigste Priorität, da
strukturell am wenigsten verwoben – Hauptproblem dort ist Dateigröße/Duplikate,
nicht Kopplung). Jeweils mit Dart-Interface-Skizze bzw. Datei-Zielliste.

Empfohlene Umsetzungsreihenfolge: 1) Call-Pilot (`call_signaling` +
`voice_channel` + `call_ui`, deckt gleich zwei ROADMAP-Punkte ab), 2)
`settings_modal.dart` aufsplitten (geringes Risiko, reine Datei-Teilung),
3) `verification` herauslösen, 4) `storage`-Fassade schrittweise erzwingen,
5) `chat_timeline`/`rooms` zuletzt.

**Offen/Nächster Schritt:** Wartet auf Bernds Entscheidung, ob der Call-Pilot
wie vorgeschlagen als Nächstes umgesetzt wird (siehe Frage unten). Erst danach
den ROADMAP-Punkt „Call-Schicht entwirren" wirklich anfassen – das ist die
erste Stelle mit echtem Verhaltensrisiko in M2 (CLAUDE.md: nach Refactoring-
Schritten immer App starten + Kernflows prüfen, hier zusätzlich brisant, weil
GUI-Test auf dem Pi nicht möglich ist).

**Stolperfallen:** Der erste Ist-Analyse-Durchgang (Subagent) hatte einen
falschen Befund (`rooms_provider.dart`-Import in `app_state.dart` sei toter
Code) – beim genaueren Lesen für den Modul-Schnitt widerlegt
(`voiceParticipantsProvider` nutzt `roomListProvider` daraus). Bereits im
Ist-Analyse-Eintrag korrigiert. Lehre: Subagent-Ist-Analysen sind ein guter
Startpunkt, aber vor jedem darauf aufbauenden Schritt einzelne Befunde
gegenlesen, nicht blind übernehmen.

**Fragen an Bernd:**
- Call-Pilot wie in `docs/M2_MODULE_SCHNITT.md` vorgeschlagen (zuerst
  `call_signaling`/`voice_channel`/`call_ui` entwirren) als nächster
  Umsetzungsschritt in Ordnung, oder lieber zuerst `settings_modal.dart`
  aufsplitten (geringeres Risiko, aber deckt keinen ROADMAP-Punkt aus M2
  direkt ab)? Ohne GUI-Zugriff auf dem Pi kann der riskantere Call-Umbau
  hier ohnehin nicht praktisch getestet werden – das spricht eher für
  „auf dem PC anfangen", falls Zeitdruck besteht.

---

## 2026-07-03 – M2: Ist-Analyse der Architektur geschrieben

**Erledigt:** M1 hat aktuell keinen auf dem Pi bearbeitbaren Punkt mehr offen
(SQLCipher wartet auf Bernds Priorisierung, Härtetest braucht GUI/PC – siehe
Fragen an Bernd unten und Eintrag 2026-07-03 „3 schon vorhanden, 1 echte
Lücke"). Deshalb mit M2 weitergemacht, erster Punkt: Ist-Analyse. Per
Code-Lektüre (Zeilenzahlen, Import-Graph, grep) folgende Schwachstellen
gefunden:

- **Gott-Dateien:** `widgets/settings_modal.dart` mit 5541 Zeilen / 50+ Klassen
  bündelt mindestens 8 unabhängige Bereiche (Profil, Benachrichtigungen,
  Voice/TURN, E2EE-Verifizierung inkl. SAS/QR, Sessions, Account, Erscheinungsbild,
  Updates). Weitere große Dateien: `message_group.dart` (2780), `rooms_panel.dart`
  (2555), `space_admin_dialog.dart` (2278), `chat_view.dart` (2116),
  `document_viewer.dart` (1773), `voice_channel.dart` (1123), `app_shell.dart` (1073).
- **Call-Schicht unsauber verteilt statt hinter einer Schnittstelle:**
  `core/voip_manager.dart` (Matrix-VoIP-Signalisierung), `core/livekit_call_manager.dart`
  (LiveKit-Transport), `features/call/voice_channel.dart` + `mini_call_widget.dart`
  (UI) – aber `chat_view.dart`, `matrix_client.dart`, `settings_modal.dart` greifen
  alle DIREKT auf die Manager zu statt über eine gemeinsame Call-Fassade. Vier
  verschiedene Module importieren `voice_channel.dart` direkt.
  Genau das Gegenteil des „austauschbare Bausteine"-Ziels aus CLAUDE.md.
- **State-Management gemischt:** 2 zentrale `ChangeNotifier`-Singletons
  (`voip_manager.dart`, `livekit_call_manager.dart`) statt Riverpod-`Notifier`,
  eingebunden über `app_state.dart` (`callStateProvider`, `voipStateProvider`).
  Migration sinnvoll, aber riskant wegen breiter Call-Sites und enger
  SDK-Callback-Kopplung (LiveKit-`Room`-Events, Matrix-`WebRTCDelegate`) – sollte
  zusammen mit der Call-Fassade angegangen werden, nicht isoliert.
- **`core/` ist nicht rein generisch:** importiert Feature-Code direkt
  (`app.dart`/`router.dart` → `features/auth/*`, `matrix_client.dart` instanziiert
  `PyramidVoipManager` direkt). `app_state.dart` selbst ist ein Sammelbecken für
  Theme-, Call-, Voice-, Share-Target- und Spaces-Provider aus praktisch allen
  Features – zentraler Kopplungspunkt, der jedem Modulschnitt im Weg steht.
  (Korrektur beim genaueren Hinsehen für den Modul-Schnitt-Punkt: der
  `rooms_provider.dart`-Import dort ist entgegen der ersten Vermutung KEIN
  toter Altlast-Import – `voiceParticipantsProvider` nutzt `roomListProvider`
  daraus. Zeigt: Subagent-Befunde vor dem Weiterbauen selbst nachprüfen.)
- **Keine gemeinsame Storage-Schnittstelle:** `SharedPreferences.getInstance()`
  wird an 12 Stellen direkt aufgerufen, obwohl `core/settings_prefs.dart` bereits
  eine `StateNotifier`-Abstraktion (`BoolPref`, `StringSetPref`) anbietet – wird
  meist umgangen. `sqflite` wird zusätzlich unabhängig in `background_push.dart`
  nochmal geöffnet (eigene DB-Instanz fürs Background-Isolate). `media_cache.dart`
  ist dagegen sauber gekapselt – gutes Vorbild für die anderen Speicherzugriffe.
- **Toter Code bestätigt:** `features/chat/message_bubble.dart` (178 Zeilen,
  Klasse `MessageBubble`) wird nirgends instanziiert – von `message_group.dart`
  abgelöst, kann beim Refactoring entfernt werden.
- **Duplikate:** `_formatTime`/`_formatDate` unabhängig implementiert in
  `pinned_messages_panel.dart:206`, `message_group.dart:322` UND `:475` (zwei Mal
  im selben File!), sowie im toten `message_bubble.dart:80/165`.

**Modul-Kandidaten für den nächsten Punkt („Modul-Schnitt definieren"):**
1. `call_signaling` (Matrix-VoIP) – `core/voip_manager.dart`
2. `call_transport` (LiveKit) – `core/livekit_call_manager.dart`
3. `call_ui` (nur UI, konsumiert 1+2 über Interface) – `features/call/*`
4. `settings/*` aufsplitten: profile, encryption_verification, sessions,
   notifications, appearance, account – aus `widgets/settings_modal.dart`
5. `verification` als eigenständiges Feature (SAS/QR/Recovery-Key), nicht
   Teil von Settings – wiederverwendbar (z. B. künftig beim Login)
6. `storage` – gemeinsame Fassade, `settings_prefs.dart` als Pflichtzugang
   statt 12 Direktzugriffsstellen
7. `app_state.dart` auflösen in feature-lokale Provider-Dateien
8. `chat_timeline` – `message_group.dart` + `pinned_messages_panel.dart`,
   dabei toten Code (`message_bubble.dart`) entfernen, `_formatTime`/`_formatDate`
   in `chat/format_utils.dart` zusammenführen

**Offen/Nächster Schritt:** M2, zweiter Punkt – „Modul-Schnitt definieren":
aus der obigen Kandidatenliste konkrete Modul-Interfaces (Dart-Abstract-Classes
o. ä.) entwerfen, dann EIN Modul zuerst umbauen (Vorschlag: `call_*`, da am
klarsten abgegrenzt und am dringendsten laut CLAUDE.md-Leitprinzip) als Pilot,
bevor der Rest folgt. Kein Codeumbau in diesem Schritt, nur Schnittstellen-Design.

**Stolperfallen:** Keine – reine Lesearbeit, kein Risiko für Bestandsdaten.
Analyse wurde von einem Explore-Subagent erstellt und stichprobenartig
plausibilisiert (Zeilenzahlen/Imports selbst nachgezählt), nicht jede
Einzelbehauptung im Detail nachverifiziert.

**Fragen an Bernd:**
- SQLCipher-Priorität weiterhin offen (siehe Eintrag „3 schon vorhanden, 1 echte
  Lücke" weiter unten) – blockiert nicht, aber ohne Antwort bleibt der M1-Punkt
  bis zum nächsten PC-Praxistest liegen.
- Für M2: Soll ich die Call-Schicht als erstes Pilot-Modul umbauen (mein
  Vorschlag oben), oder hast du eine andere Präferenz (z. B. erst die
  `settings_modal.dart` aufsplitten, weil die am unübersichtlichsten ist)?

---

## 2026-07-03 – M1: Restliche Punkte geprüft – 3 schon vorhanden, 1 echte Lücke gefunden

**Erledigt:**
- **Logout-Warnung bei fehlendem Key-Backup** (Commit b5762ea): neue Funktion
  `isKeyBackupMissing(client)` in `bootstrap_dialog.dart` (prüft Cross-Signing +
  `keyManager.isCached()`). Beide Logout-Dialoge in `settings_modal.dart` zeigen
  jetzt eine rote Klartext-Warnung statt des neutralen Textes, wenn kein Backup
  eingerichtet ist.
- **Drei ROADMAP-Punkte waren bereits umgesetzt** (nur nicht abgehakt) – durch
  Code-Lektüre verifiziert, nicht neu gebaut:
  - Key-Backup einrichten/wiederherstellen: `bootstrap_dialog.dart` deckt beides
    über den `Bootstrap`-Zustandsautomaten des matrix-SDK ab (`askNewSsss` für
    Neueinrichtung, `askUnlockSsss` fragt auf einem neuen Gerät nach dem
    Recovery-Key). Wird automatisch getriggert, wenn Cross-Signing fehlt
    (`app_shell.dart:_triggerBootstrap`).
  - Geräte-/Sessionverwaltung: `_SessionsSection` in `settings_modal.dart` –
    Liste, Umbenennen, SAS/QR-Geräteverifizierung, Einzel- und
    Massen-Abmeldung (mit Passwort-Reauth bei UIA-Anforderung).
- **Echte Sicherheitslücke gefunden:** `sqlcipher_flutter_libs` ist eine
  Abhängigkeit in `pubspec.yaml`, wird aber **nirgends im Code benutzt**.
  `matrix_client.dart` (Haupt-App) und `background_push.dart` (Push-Hintergrund)
  öffnen `pyramid.sqlite` beide über den normalen, unverschlüsselten
  `sqflite`/`sqflite_common_ffi`-Factory. Die Krypto-DB (Access-Token,
  gepickelter Olm-Account, Megolm-Sessions, komplette Nachrichtenhistorie)
  liegt also **im Klartext** auf der Platte – nur durch Androids App-Sandbox
  geschützt, nicht durch eine eigene Verschlüsselung.

**Offen/Nächster Schritt:** SQLCipher-Anbindung NICHT blind auf dem Pi
umgesetzt – siehe Stolperfalle unten. Braucht einen eigenen, vorsichtig
geplanten PC-Termin: Backup der Test-DB, Migrationscode (bestehende
Klartext-DB einmalig nach SQLCipher überführen), Test auf einem Gerät mit
echten Daten, erst dann Rollout.

**Fragen an Bernd:**
- Priorität der SQLCipher-Lücke: Soll das vor M2 (Refactoring) angegangen
  werden, oder reicht die App-Sandbox als Schutz erstmal aus (Angreifer bräuchte
  Root oder physischen Zugriff + ADB, um die Datei überhaupt zu lesen)? Die
  Migration bestehender Installationen (Uta!) ist der aufwändige/riskante Teil,
  nicht die Verschlüsselung selbst.

**Stolperfallen:**
- Eine DB-Verschlüsselungs-Migration ist genau die Art Änderung, die laut
  CLAUDE.md nicht "mal eben" auf dem Pi ohne Testgerät gemacht werden darf:
  scheitert die Migration (falscher Schlüssel, falsche PRAGMA-Reihenfolge,
  Timing zwischen Haupt-App und Push-Hintergrund-Client), sind Utas
  Nachrichten und Krypto-Schlüssel unwiederbringlich weg. Deshalb hier nur
  dokumentiert, nicht implementiert.

---

## 2026-07-03 – M1: Push-Regression vermutlich dieselbe Ursache wie Random-Logout

**Erledigt:** Vor dem Blick in den Push-Code die Vermutung geprüft, ob die
Push-Entschlüsselungs-Regression und der Uta-Random-Logout-Bug (siehe Eintrag
weiter unten) zusammenhängen – Ergebnis: **sehr wahrscheinlich ja, exakt
dieselbe Ursache.**

- `client.getEventByPushNotification(...)` (aufgerufen in `_bgDecryptAndShow`,
  `background_push.dart:84`) ruft als ALLERERSTES `ensureNotSoftLoggedOut()`
  auf (matrix-Paket `client.dart:1878`). Diese Funktion prüft, ob der
  Access-Token in < 1 Minute abläuft, und löst dann `_handleSoftLogout()` aus
  — **dieselbe Funktion**, die ohne Refresh-Token (unser Zustand vor Commit
  9dc83f7) sofort einen echten `client.logout()` auslöst (siehe Eintrag unten).
- Der Hintergrund-Client aus `_buildClient()` liest **dieselbe** `pyramid.sqlite`
  wie der Haupt-Client. Ein `logout()`/`clear()` aus dem Hintergrund-Isolat
  würde also die komplette Session (inkl. Krypto-Schlüssel) für die GESAMTE
  App zerstören – nicht nur für den einen Push. Das erklärt, warum das Problem
  als dauerhafte Regression erscheint ("seit dem letzten Update nie wieder"):
  Nach dem ersten Auto-Logout-Ereignis (ausgelöst beim Sync ODER beim
  Entschlüsseln eines Pushs) sind die Schlüssel weg, jede folgende
  Entschlüsselung schlägt fehl → `BgEngine` zeigt nur noch den
  „Neue Nachricht"-Platzhalter (Fallback nach 6 s), nie mehr den echten Text.
- Der bereits committete Fix (`refreshToken: true` beim Login, 9dc83f7) behebt
  damit vermutlich BEIDE Symptome gleichzeitig: der Refresh-Token liegt in der
  gemeinsamen DB, also kann auch der Hintergrund-Client ihn lesen und einen
  drohenden Soft-Logout durch stillen Refresh abwenden, statt die Session zu
  zerstören.

**Offen/Nächster Schritt:** Gehört zum selben Praxistest wie der
Random-Logout-Punkt oben – zusätzlich beobachten, ob nach einer Weile
(mehrere Stunden/Tage) echte Push-Inhalte statt Platzhalter ankommen. Schlägt
das fehl, liegt eine zweite, unabhängige Ursache vor (dann `docs/NOTIFICATIONS.md`
+ Memory „Pyramid Push" erneut konsultieren, insbesondere die 3 dort gelisteten
Fallstricke der Hintergrund-Entschlüsselung).

**Stolperfallen:** Diese Verbindung wurde rein durch Lesen des matrix-SDK-
Quellcodes hergeleitet (`~/.pub-cache/hosted/pub.dev/matrix-6.2.0/lib/src/client.dart`),
nicht durch Beobachtung auf einem echten Gerät – bewusst als Vermutung
markiert, bis der PC-Praxistest sie bestätigt.

---

## 2026-07-03 – M1: Uta-Random-Logout – Ursache gefunden + Fix (UNGETESTET/Pi)

**Erledigt:**
- Alle Logout-Auslöser im Code kartiert:
  - 3 bewusste Nutzer-Logout-Buttons in `settings_modal.dart` (alle hinter
    Bestätigungsdialog) – kein Kandidat für "random"
  - Der eigentliche Verdächtige: `matrix`-Paket (v6.2.0) `Client._innerSync()`
    behandelt `M_UNKNOWN_TOKEN` vom `/sync`-Endpunkt so: bei `soft_logout: true`
    → `_handleSoftLogout()` → versucht `refreshAccessToken()` → **das SDK
    hatte nie einen Refresh-Token angefordert** (`client.login(...)` in
    `login_notifier.dart` rief ohne `refreshToken: true` auf) → `refreshAccessToken()`
    wirft sofort "No refresh token available" → Catch-Block ruft `await logout()`
    auf: ein **echter, destruktiver Logout**, ausgelöst rein durch einen normalen
    Soft-Logout (z. B. abgelaufenes Access-Token), ganz ohne Nutzerinteraktion.
    Quelle: `~/.pub-cache/hosted/pub.dev/matrix-6.2.0/lib/src/client.dart:2374-2384`
    und `:2496-2509`.
- **Logging (Commit 63e4919):** neue `lib/core/auth_log.dart` schreibt jeden
  Login-Status-Wechsel (`client.onLoginStateChanged`) sowie SDK-Warnungen/-Fehler
  (`Logs().onLog`) in `auth_log.txt` im App-Support-Verzeichnis – übersteht
  App-Neustart. Zusätzlich loggen alle 3 Nutzer-Logout-Buttons sich selbst, damit
  sich künftig unterscheiden lässt: Nutzer-Logout vs. SDK-Auto-Logout. Reine
  Zusatzfunktion, keine Verhaltensänderung.
- **Fix (Commit 9dc83f7):** `client.login(...)` fordert jetzt `refreshToken: true`
  an. Damit kann das SDK einen Soft-Logout künftig durch stillen Token-Refresh
  überstehen, statt die Sitzung zu zerstören. Unterstützt der Server keine
  Refresh-Tokens, bleibt das Verhalten exakt wie vorher (`response.refreshToken`
  ist dann `null`) – der Fix kann also nichts verschlimmern.

**Offen/Nächster Schritt:**
1. **Praxistest auf dem PC (zwingend vor Haken in ROADMAP!):** Login → Nachrichten
   senden/lesen → Logout → erneuter Login → alte verschlüsselte Nachrichten noch
   lesbar? Dabei prüfen, ob `auth_log.txt` (App-Support-Verzeichnis) die
   Login-Status-Wechsel sauber mitschreibt.
2. Danach: den nächsten Punkt in M1 angehen (Push-Entschlüsselungs-Regression).
3. **Wichtig für Bernd/Uta:** Der Fix wirkt nur für NEUE Logins. Utas bereits
   bestehende Sitzung bekommt erst nach einem einmaligen manuellen Neu-Login
   einen Refresh-Token und ist bis dahin weiterhin anfällig. Sobald der PC-Test
   grün ist, sollte Uta sich einmal aus- und wieder einloggen.

**Stolperfallen:**
- Konnte auf dem Pi nicht mit echtem Login/Logout getestet werden (kein GUI,
  kein Matrix-Testaccount zur Hand) – Fix beruht auf genauem Lesen des
  matrix-SDK-Quellcodes, nicht auf Beobachtung im Betrieb. Deshalb zwingend
  vor dem nächsten Haken in ROADMAP am PC nachprüfen.
- `chat_provider.dart:42` nutzt weiterhin das deprecated `Timeline.onUpdate`
  (Re-Entschlüsseln nach Schlüssel-Empfang) – hängt mit demselben Sync-Bereich
  zusammen, aber bewusst nicht angefasst, um nicht zwei riskante Änderungen im
  selben Bereich gleichzeitig ungetestet zu committen.

---

## 2026-07-03 – M0 abgeschlossen: Ordnung, flutter analyze sauber, README

**Erledigt:**
- Liegen gebliebene Deprecation-Fixes aus einer abgebrochenen Session committet
  (`withOpacity`→`withValues`, `activeColor`→`activeThumbColor`, `cacheExtent`→
  `ScrollCacheExtent`, `PublicRoomsChunk`→`PublishedRoomsChunk`) – Commit 057ac1e
- Restliche `flutter analyze`-Hinweise behoben – Commit 401bd6a:
  - `document_viewer.dart`: `Matrix4.translate/scale` → `translateByDouble`/
    `scaleByDouble` (PDF-Zoom, gleiches Verhalten)
  - `rooms_panel.dart`: `onReorder` → `onReorderItem` (Index-Korrektur jetzt intern,
    manuelles `newIdx--` entfernt); `implementation_imports`-Hinweis kommentiert
    (SpaceChild wird vom `matrix`-Paket nicht öffentlich exportiert, kein Fix möglich)
  - `voip_manager.dart`: `getSources()`-Override kommentiert (Pflicht-Override der
    abstrakten `MediaDevices`-Klasse, die die Methode selbst deprecated hat)
  - `settings_modal.dart`: zwei `BuildContext`-nach-`await`-Stellen mit
    `context.mounted` abgesichert (Geräte umbenennen, Passwortabfrage bei Massenlogout)
  - **Bewusst NICHT gefixt:** `chat_provider.dart:42` (`Timeline.onUpdate` deprecated,
    Ersatz wäre `client.onSync` + Raum-Filter). Hängt am E2EE-Redecrypt-Pfad
    (Re-Entschlüsseln nach Schlüssel-Empfang) – laut CLAUDE.md heiliger Bereich,
    Umbau nur mit dediziertem Login→Nachrichten→Logout→Login-Test, nicht nebenbei.
- `lib/features/calls/` (Leiche) geprüft: existiert nicht mehr, nur `lib/features/call/`
  mit den echten Dateien – Punkt war bereits erledigt, nur nicht abgehakt.
- README.md durch echte Projektbeschreibung ersetzt (Funktionsumfang, Build, Struktur) – Commit 74d38fd
- ROADMAP.md: M0 komplett abgehakt.

**Offen/Nächster Schritt:** M1, erster Punkt – Uta-Random-Logout-Bug untersuchen
(Logging an allen Logout-Pfaden einbauen, dann Ursache finden). Direkt danach die
Push-Entschlüsselungs-Regression (`docs/NOTIFICATIONS.md` + Memory „Pyramid Push"
vorher lesen).

**Stolperfallen:**
- Kein `test/`-Ordner vorhanden → `flutter test` läuft ins Leere, keine automatisierte
  Absicherung. `flutter analyze` lief sauber (0 Fehler/Warnungen), aber die Lint-Fixes
  in `document_viewer.dart` (PDF-Zoom) und `rooms_panel.dart` (Drag-Reorder der
  Raumliste) sind **UNGETESTET** in echter UI – nächster Windows-Lauf sollte kurz
  PDF-Zoom und Raum-Umsortieren per Drag prüfen.
- Der `ignore:`-Kommentar für einen deprecated-Aufruf muss auf der Zeile *unmittelbar*
  über der betroffenen Codezeile stehen, nicht nur "in der Nähe" – sonst wirkt er nicht
  (bei `voip_manager.dart` erst falsch über `@override` gesetzt, dann korrigiert).

---

## 2026-07-03 – Arbeitsstruktur angelegt + Vollbackup (Setup, noch kein Code)

**Erledigt:**
- Vollbackup inkl. Git-Historie: `MatrixPi\backups\pyramid-kopie_backup_2026-07-03.tar.gz` (510 MB)
- `CLAUDE.md` (Arbeitsregeln), `ROADMAP.md` (M0–M7 nach Bernds Prioritäten), dieses Protokoll
- Klargestellt: `C:\Users\nordm\pyramid - Kopie` ist das EINZIGE aktuelle Repo;
  `C:\Users\nordm\pyramid` und `MatrixPi\pyramid` sind veraltete April-Stände (Commit c9af913)

**Offen/Nächster Schritt:** M0, Punkt 2 – die ~140 uncommitteten Dateien mit `git status`
sichten und in thematische Commits aufteilen (diese drei neuen Dateien mit committen),
dann `git push origin master`.

**Stolperfallen:**
- Letzter Commit ist vom 2026-04-28 (d706ace), gearbeitet wurde aber bis mindestens
  2026-06-12 → zwischen Commits und Realität liegen 6 Wochen. Genau deshalb: ab jetzt
  kleine Commits nach jedem Schritt.
- Git-Remote (Gitea auf dem Pi) enthält Zugangsdaten in der URL – funktioniert, aber
  nur erreichbar, wenn der Pi läuft (unterwegs: WireGuard nötig).
