Gleiche Technik wie bei settings_modal.dart/message_group.dart: reine Verschiebung per Dart part/part-of, keine Logikänderung. rooms_panel.dart schrumpft von 2555 auf 31 Zeilen (nur noch Bibliothekskopf). Verifiziert per automatisiertem Blockvergleich gegen das Original und flutter analyze. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
32 KiB
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 – 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 ofstatt eigenständiger Libraries – dadurch bleiben alle privaten Klassen (_ProfileSection,_SettingsGroup, …) ohne Umbenennung nutzbar, und alle Imports bleiben zentral insettings_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.dartselbst: von 5541 auf 270 Zeilen geschrumpft – enthält nur noch Imports, die 12part-Direktiven,SettingsModalund_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; Anzahlclass/enum-Deklarationen (60) undWidget 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 formatversuchte 40 neue Lint-Hinweise einzuführen (curly_braces_in_flow_control_structures) – Ursache: die Originaldatei war selbst nie komplettdart format-clean (bestätigt perdart format --set-exit-if-changedauf dem Original-Git-Stand), und Reflow einzelner langer Einzeiler-ifs 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 dieflutter 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 (durchmessage_group.dartabgelöst), pergrepdoppelt bestätigt.- Die zwei identischen
_formatTime-Methoden inmessage_group.dart(_MessageGroupStateZeile 322,_ContinuationMessageStateZeile 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:_formatTimebewusst NICHT angefasst – hat andere Logik (relatives Datum „vor Xd"), ist kein echtes Duplikat. flutter analyzeweiterhin sauber: nur der eine bekannte, bewusst zurückgestellte Hinweis (chat_provider.dart:42,Timeline.onUpdatedeprecated, 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.mdvorgeschlagen (zuerstcall_signaling/voice_channel/call_uientwirren) als nächster Umsetzungsschritt in Ordnung, oder lieber zuerstsettings_modal.dartaufsplitten (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.dartmit 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) – aberchat_view.dart,matrix_client.dart,settings_modal.dartgreifen alle DIREKT auf die Manager zu statt über eine gemeinsame Call-Fassade. Vier verschiedene Module importierenvoice_channel.dartdirekt. 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 überapp_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.dartinstanziiertPyramidVoipManagerdirekt).app_state.dartselbst 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: derrooms_provider.dart-Import dort ist entgegen der ersten Vermutung KEIN toter Altlast-Import –voiceParticipantsProvidernutztroomListProviderdaraus. Zeigt: Subagent-Befunde vor dem Weiterbauen selbst nachprüfen.)- Keine gemeinsame Storage-Schnittstelle:
SharedPreferences.getInstance()wird an 12 Stellen direkt aufgerufen, obwohlcore/settings_prefs.dartbereits eineStateNotifier-Abstraktion (BoolPref,StringSetPref) anbietet – wird meist umgangen.sqflitewird zusätzlich unabhängig inbackground_push.dartnochmal geöffnet (eigene DB-Instanz fürs Background-Isolate).media_cache.dartist dagegen sauber gekapselt – gutes Vorbild für die anderen Speicherzugriffe. - Toter Code bestätigt:
features/chat/message_bubble.dart(178 Zeilen, KlasseMessageBubble) wird nirgends instanziiert – vonmessage_group.dartabgelöst, kann beim Refactoring entfernt werden. - Duplikate:
_formatTime/_formatDateunabhängig implementiert inpinned_messages_panel.dart:206,message_group.dart:322UND:475(zwei Mal im selben File!), sowie im totenmessage_bubble.dart:80/165.
Modul-Kandidaten für den nächsten Punkt („Modul-Schnitt definieren"):
call_signaling(Matrix-VoIP) –core/voip_manager.dartcall_transport(LiveKit) –core/livekit_call_manager.dartcall_ui(nur UI, konsumiert 1+2 über Interface) –features/call/*settings/*aufsplitten: profile, encryption_verification, sessions, notifications, appearance, account – auswidgets/settings_modal.dartverificationals eigenständiges Feature (SAS/QR/Recovery-Key), nicht Teil von Settings – wiederverwendbar (z. B. künftig beim Login)storage– gemeinsame Fassade,settings_prefs.dartals Pflichtzugang statt 12 Direktzugriffsstellenapp_state.dartauflösen in feature-lokale Provider-Dateienchat_timeline–message_group.dart+pinned_messages_panel.dart, dabei toten Code (message_bubble.dart) entfernen,_formatTime/_formatDateinchat/format_utils.dartzusammenfü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.dartaufsplitten, 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 FunktionisKeyBackupMissing(client)inbootstrap_dialog.dart(prüft Cross-Signing +keyManager.isCached()). Beide Logout-Dialoge insettings_modal.dartzeigen 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.dartdeckt beides über denBootstrap-Zustandsautomaten des matrix-SDK ab (askNewSsssfür Neueinrichtung,askUnlockSsssfragt auf einem neuen Gerät nach dem Recovery-Key). Wird automatisch getriggert, wenn Cross-Signing fehlt (app_shell.dart:_triggerBootstrap). - Geräte-/Sessionverwaltung:
_SessionsSectioninsettings_modal.dart– Liste, Umbenennen, SAS/QR-Geräteverifizierung, Einzel- und Massen-Abmeldung (mit Passwort-Reauth bei UIA-Anforderung).
- Key-Backup einrichten/wiederherstellen:
- Echte Sicherheitslücke gefunden:
sqlcipher_flutter_libsist eine Abhängigkeit inpubspec.yaml, wird aber nirgends im Code benutzt.matrix_client.dart(Haupt-App) undbackground_push.dart(Push-Hintergrund) öffnenpyramid.sqlitebeide über den normalen, unverschlüsseltensqflite/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 ALLERERSTESensureNotSoftLoggedOut()auf (matrix-Paketclient.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 Commit9dc83f7) sofort einen echtenclient.logout()auslöst (siehe Eintrag unten).- Der Hintergrund-Client aus
_buildClient()liest dieselbepyramid.sqlitewie der Haupt-Client. Einlogout()/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 →BgEnginezeigt nur noch den „Neue Nachricht"-Platzhalter (Fallback nach 6 s), nie mehr den echten Text. - Der bereits committete Fix (
refreshToken: truebeim 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()behandeltM_UNKNOWN_TOKENvom/sync-Endpunkt so: beisoft_logout: true→_handleSoftLogout()→ versuchtrefreshAccessToken()→ das SDK hatte nie einen Refresh-Token angefordert (client.login(...)inlogin_notifier.dartrief ohnerefreshToken: trueauf) →refreshAccessToken()wirft sofort "No refresh token available" → Catch-Block ruftawait 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-2384und:2496-2509.
- 3 bewusste Nutzer-Logout-Buttons in
- Logging (Commit
63e4919): neuelib/core/auth_log.dartschreibt jeden Login-Status-Wechsel (client.onLoginStateChanged) sowie SDK-Warnungen/-Fehler (Logs().onLog) inauth_log.txtim 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 jetztrefreshToken: truean. 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.refreshTokenist dannnull) – der Fix kann also nichts verschlimmern.
Offen/Nächster Schritt:
- 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. - Danach: den nächsten Punkt in M1 angehen (Push-Entschlüsselungs-Regression).
- 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:42nutzt weiterhin das deprecatedTimeline.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) – Commit057ac1e - Restliche
flutter analyze-Hinweise behoben – Commit401bd6a:document_viewer.dart:Matrix4.translate/scale→translateByDouble/scaleByDouble(PDF-Zoom, gleiches Verhalten)rooms_panel.dart:onReorder→onReorderItem(Index-Korrektur jetzt intern, manuellesnewIdx--entfernt);implementation_imports-Hinweis kommentiert (SpaceChild wird vommatrix-Paket nicht öffentlich exportiert, kein Fix möglich)voip_manager.dart:getSources()-Override kommentiert (Pflicht-Override der abstraktenMediaDevices-Klasse, die die Methode selbst deprecated hat)settings_modal.dart: zweiBuildContext-nach-await-Stellen mitcontext.mountedabgesichert (Geräte umbenennen, Passwortabfrage bei Massenlogout)- Bewusst NICHT gefixt:
chat_provider.dart:42(Timeline.onUpdatedeprecated, Ersatz wäreclient.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, nurlib/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 testläuft ins Leere, keine automatisierte Absicherung.flutter analyzelief sauber (0 Fehler/Warnungen), aber die Lint-Fixes indocument_viewer.dart(PDF-Zoom) undrooms_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 (beivoip_manager.darterst falsch über@overridegesetzt, 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 - Kopieist das EINZIGE aktuelle Repo;C:\Users\nordm\pyramidundMatrixPi\pyramidsind veraltete April-Stände (Commitc9af913)
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).