87b29b5912
Review-Protokoll in PROGRESS.md nachgeholt (die Vorsession hattef9979e4und891f348gepusht, wurde aber vor dem Protokollieren abgebrochen) und den Review zu Ende geführt: (a) Splits unabhängig per Zeilen-Multiset verifiziert, (b) Soft-Logout-Guard gegen SDK-Quelle gegenverifiziert, (c) Lint-Fixes äquivalent (onReorderItem gegen Flutter-SDK geprüft), (d) ROADMAP-Haken stimmen mit dem Code überein. Nebenbefund: docs/ PROGRESS.md als Archiv markiert (Verwechslungsgefahr mit dem echten Protokoll im Root). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
867 lines
52 KiB
Markdown
867 lines
52 KiB
Markdown
# 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
|
||
```
|
||
|
||
---
|
||
|
||
## Fable-5-Review (Qualitäts-Review aller Autopilot-Commits 25ed765..HEAD)
|
||
|
||
Bernds Auftrag (ROADMAP M0): alle bisherigen Sonnet-5-Commits kritisch und in
|
||
der Tiefe nachprüfen. Jeder Punkt hier wird abgehakt, sobald er geprüft ist –
|
||
**bereits abgehakte Punkte in künftigen Sessions NICHT erneut prüfen.**
|
||
|
||
Hinweis zur Historie: Eine frühere Fable-5-Session hatte den Review begonnen
|
||
und zwei Fix-Commits gepusht (f9979e4, 891f348), wurde aber VOR dem
|
||
Protokollieren abgebrochen – dieser Abschnitt holt das Protokoll nach und
|
||
schließt den Review ab.
|
||
|
||
- [x] **(a) Die 6 Gott-Datei-Aufteilungen fachlich geprüft – Ergebnis: korrekt,
|
||
1 Befund bereits gefixt (f9979e4).** Unabhängige Nachprüfung (nicht nur den
|
||
alten Blockvergleich wiederholt): eigener Zeilen-Multiset-Vergleich
|
||
(Code-Zeilen ohne Kommentare/Imports/part-Direktiven) Original vor dem Split
|
||
vs. heutiger Stand für alle 6 Bibliotheken → **keine Code-Zeile verloren,
|
||
keine hinzugekommen** (settings_modal 5055, message_group 2409, rooms_panel
|
||
2308, space_admin_dialog 2079, chat_view 1813, document_viewer 1543 Zeilen,
|
||
jeweils exakt identisch). Alle `part`-Direktiven == Dateien auf Platte,
|
||
alle Part-Dateien mit korrektem `part of`. Befund der Vorsession bestätigt:
|
||
Beim Split gingen 24 **Erklärkommentar-Blöcke** verloren (u. a. die
|
||
Selbst-Aussperr-Warnung über `updatePowerLevelsSafely`, HTML-Subset-Doku,
|
||
PDF-Cache-Doku) – mit f9979e4 an den richtigen Klassen wiederhergestellt
|
||
(Stichprobe geprüft: sitzt korrekt). `flutter analyze`: unverändert 1
|
||
bekannter Hinweis (`chat_provider.dart:42`).
|
||
- [x] **(b) Uta-Logout-/Push-Fix (63e4919, 9dc83f7) gegen das matrix-SDK 6.2.0
|
||
verifiziert – Analyse war richtig, aber unvollständig; Lücke mit 891f348
|
||
geschlossen (Vorsession), heute vollständig gegenverifiziert:**
|
||
- SDK-Quelle bestätigt (client.dart:2374 ff.): Ohne eigenen `onSoftLogout`-
|
||
Handler eskaliert JEDER fehlgeschlagene Token-Refresh (auch bloße
|
||
Netzwerkfehler) zu `logout()` + `clear()` → Session inkl. Krypto-Schlüssel
|
||
weg. Mit `refreshToken: true` (9dc83f7) wären Refreshes Routine geworden –
|
||
das Restrisiko wäre also GESTIEGEN. Der Guard (`soft_logout_guard.dart`,
|
||
891f348) ist die notwendige zweite Hälfte des Fixes.
|
||
- Guard-Code geprüft: wirft nie (alle Pfade gefangen; `AuthLog.write` fängt
|
||
intern selbst alles – wichtig, sonst würde ein Logging-Fehler im
|
||
catch-Pfad doch wieder `logout()` auslösen). SDK ruft bei nicht-werfendem
|
||
Handler nachweislich nie `logout()` auf.
|
||
- „Retry heilt das Token-Rotations-Race mit dem Push-Isolate"-Behauptung
|
||
gegen SDK-Quelle geprüft: stimmt – `refreshAccessToken()` liest den
|
||
Refresh-Token bei jedem Aufruf frisch per `database.getClient()` aus der
|
||
geteilten DB.
|
||
- Beide `Client(`-Konstruktionen der App (matrix_client.dart,
|
||
background_push.dart) haben den Guard – keine ungeschützte übrig.
|
||
- `isLoggedInProvider`-Änderung (softLoggedOut ≠ ausgeloggt) korrekt:
|
||
verhindert Login-Screen-Flackern bei Routine-Refresh; echtes `loggedOut`
|
||
führt weiterhin zum Login-Screen.
|
||
- **Dokumentiertes Restrisiko (kein Fix möglich/nötig):** Ein hartes
|
||
`M_UNKNOWN_TOKEN` OHNE `soft_logout`-Flag führt im SDK weiterhin direkt zu
|
||
`clear()`. Das ist ein echter serverseitiger Session-Widerruf (z. B.
|
||
Admin-Logout) – lokal nicht abwendbar; Schlüssel kommen dann übers
|
||
Key-Backup zurück. Kein stiller Fehlerpfad, sondern legitimer Server-Befehl.
|
||
- [x] **(c) analyze-/Lint-Fixes (9dc1175, 133a8ac, 99f10c5, 2395db5, 057ac1e,
|
||
401bd6a) auf Verhaltensänderungen geprüft – alle äquivalent, 1 Kleinigkeit
|
||
gefunden (bewusst belassen):**
|
||
- `onReorder` → `onReorderItem` (401bd6a): gegen Flutter-SDK-Quelle
|
||
verifiziert – der neue Callback korrigiert den Index intern, das entfernte
|
||
manuelle `newIdx--` ist korrekt; einzige Aufrufstelle migriert. Kein
|
||
Off-by-One beim Raum-Umsortieren.
|
||
- `Matrix4.translate/scale` → `translateByDouble/scaleByDouble` (PDF-Zoom):
|
||
mathematisch äquivalent (uniforme Skalierung, w=1).
|
||
- `withOpacity`→`withValues`, `activeColor`→`activeThumbColor`,
|
||
`cacheExtent`→`scrollCacheExtent`, `PublicRoomsChunk`→`PublishedRoomsChunk`,
|
||
print→debugPrint, `__`→`_`-Parameter, Curly-Braces: reine Renames/Syntax.
|
||
- Toter Code (9dc1175): entfernte Funktionen (`_showNotif`, `_fetchTitle`,
|
||
`_get`, `_applyNoiseSuppression`, `destructive`-Param, `_saved`-Feld) per
|
||
grep bestätigt nirgends referenziert.
|
||
- **Kleinigkeit:** In `mini_call_widget.dart` wurde das ungenutzte
|
||
`_hovered`-Feld nicht gelöscht, sondern verdrahtet (Hover-Farbe auf
|
||
Call-Buttons) – streng genommen eine Mini-Verhaltensänderung in einem
|
||
Lint-Commit. Offensichtlich die ursprünglich gemeinte Funktion des Feldes,
|
||
rein kosmetisch, kein Risiko → belassen, hier nur dokumentiert.
|
||
- [x] **(d) Abgehakte ROADMAP-Punkte gegen Code gegengeprüft – alle stimmen:**
|
||
`isKeyBackupMissing`-Warnung sitzt nach dem Settings-Split weiterhin in
|
||
beiden Logout-Pfaden (settings_about.dart, settings_shared.dart);
|
||
`refreshToken: true` in login_notifier.dart:84; Bootstrap-Flows
|
||
(`askUnlockSsss`, `_triggerBootstrap`) vorhanden; `features/calls/`-Leiche
|
||
existiert nicht; `docs/M2_MODULE_SCHNITT.md` vorhanden; `flutter analyze`
|
||
sauber (1 bekannter Hinweis). Nebenbefund gefixt: `docs/PROGRESS.md` war ein
|
||
eingefrorener Alt-Stand (2026-06-04) mit Verwechslungsgefahr zum echten
|
||
Protokoll im Root → Archiv-Banner eingefügt.
|
||
|
||
**Fazit:** Die Sonnet-5-Arbeit war handwerklich sauber; die zwei echten Lücken
|
||
(verlorene Kommentare, fehlender Soft-Logout-Guard) hatte die abgebrochene
|
||
Fable-5-Vorsession bereits gefixt – beide Fixes heute unabhängig verifiziert.
|
||
Alles weiterhin UNGETESTET (Pi) im Sinne von „nicht in echter UI gesehen";
|
||
der ausstehende PC-Praxistest (siehe Einträge unten) bleibt Pflicht.
|
||
|
||
---
|
||
|
||
## 2026-07-03 – M2: Gott-Datei `document_viewer.dart` aufgeteilt (5 Dateien)
|
||
|
||
**Erledigt:** Vor dem Weiterarbeiten wie immer Arbeitszyklus durchlaufen
|
||
(`PROGRESS.md`/`ROADMAP.md` gelesen, `flutter doctor` geprüft – GUI-Lücken
|
||
auf dem Pi unverändert). Die Ist-Analyse aus M2 hatte ursprünglich 8 große
|
||
Dateien gefunden, aber nur 5 wurden als „Gott-Dateien" tatsächlich
|
||
aufgeteilt (`settings_modal.dart`, `message_group.dart`, `rooms_panel.dart`,
|
||
`space_admin_dialog.dart`, `chat_view.dart`); `document_viewer.dart` (1773
|
||
Zeilen, 31 Klassen/Enums + 3 lose Top-Level-Helfer) war übrig, ist aber
|
||
inhaltlich genau derselbe Fall (viele unabhängige Widget-Klassen in einer
|
||
Datei) und – anders als `voice_channel.dart`/`app_shell.dart` – reines
|
||
Medien-/PDF-Anzeige-Feature ohne Berührung mit Login/Session/Verschlüsselung
|
||
oder der noch unentschiedenen Call-Fassade. Deshalb mit derselben
|
||
bewährten Technik aufgeteilt:
|
||
|
||
- `part`/`part of` wie bei den 5 vorigen Umbauten, per Skript extrahiert
|
||
(Klammer-Zählung für Blockgrenzen inkl. der 3 losen Top-Level-Funktionen
|
||
`_saveBytes`, `_extractPsdThumbnail`, `_detectType` – nicht nur
|
||
`class`/`enum`, das wurde diesmal zusätzlich automatisiert gesucht, um
|
||
keine lose Funktion zu übersehen).
|
||
- 5 neue Dateien unter `lib/features/chat/document/`:
|
||
`document_viewer_core.dart` (`DocumentViewer`-Dispatcher, `_DocHeader`,
|
||
`_ExtBadge`, `_DocContent`, `_DocType`-Enum + `_detectType`, `_ErrorView`,
|
||
`_UnsupportedViewer`, `_FileFallback`), `document_viewer_pdf.dart`
|
||
(größter Teil: `_RenderCache`, `PdfInlinePreview`+State für die
|
||
Chat-Inline-Vorschau, `_NavArrow`, `_saveBytes`, `_PdfFullViewer`+State,
|
||
`_PdfFullscreenViewer`+State, `_FsBtn`, `_ThumbnailStrip`+State),
|
||
`document_viewer_text.dart` (SVG/Markdown/Text/Office-Text-Extraktion),
|
||
`document_viewer_archive.dart` (ZIP/TAR-Archiv-Browser, PSD-Thumbnail-
|
||
Extraktion inkl. `_extractPsdThumbnail`, Sketch/XD-ZIP-Preview),
|
||
`document_viewer_misc.dart` (`_ProprietaryViewer`-Infokarte für nicht
|
||
darstellbare Formate). `document_viewer.dart` selbst: nur noch
|
||
Bibliothekskopf (Imports + 5 `part`-Direktiven, 20 Zeilen).
|
||
- Verifikation wie bei den 5 vorigen Umbauten: automatisierte
|
||
Klammer-Zählung fand alle 34 Top-Level-Blöcke (31 Klassen/Enums + 3 lose
|
||
Funktionen), alle wortwörtlich in den neuen Dateien wiedergefunden,
|
||
`class`/`enum`-Anzahl (31) vorher/nachher identisch, `flutter analyze`
|
||
unverändert bei 1 bekanntem Hinweis (`chat_provider.dart:42`). Einzige
|
||
externe Importstelle (`message_group.dart`) bleibt unverändert gültig, da
|
||
`document_viewer.dart` weiterhin der Bibliothekseinstieg ist.
|
||
- **Bewusst NICHT angefasst:** `voice_channel.dart` (1123 Zeilen) und
|
||
`app_shell.dart` (1073 Zeilen) – auch große Dateien mit vielen Klassen,
|
||
aber `voice_channel.dart` gehört zur noch unentschiedenen Call-Fassade
|
||
(siehe Fragen an Bernd unten) und `app_shell.dart` enthält den
|
||
Bootstrap-Trigger (`_triggerBootstrap`) für Cross-Signing/Key-Backup –
|
||
laut CLAUDE.md heiliger Bereich. Eine reine Datei-Aufteilung wäre zwar
|
||
vermutlich genauso risikoarm wie bei den anderen 6, aber ohne GUI-Test auf
|
||
dem Pi wollte ich das Risiko nicht zusätzlich zum ohnehin schon
|
||
aufgelaufenen UNGETESTET-Stapel erhöhen, bevor nicht wenigstens einmal ein
|
||
PC-Durchlauf stattgefunden hat.
|
||
|
||
**Offen/Nächster Schritt:** UNGETESTET (Pi) – siehe ROADMAP-Eintrag oben,
|
||
gehört zum selben ausstehenden Windows-Praxistest wie die 5 vorigen
|
||
Datei-Aufteilungen (PDF/Text/Archiv/Bild im Chat öffnen, Vollbild-PDF-Viewer
|
||
inkl. Thumbnail-Leiste und Speichern-Button prüfen). Danach bleibt exakt
|
||
dieselbe Blockade wie im Eintrag direkt darunter: kein M1/M2-Punkt mehr
|
||
ohne Bernds Antwort (Call-Pilot, SQLCipher-Priorität) oder PC/GUI-Zugriff
|
||
sicher bearbeitbar.
|
||
|
||
**Stolperfallen:** Anders als bei den 5 vorigen Umbauten gab es diesmal 3
|
||
lose Top-Level-Funktionen zusätzlich zu den Klassen/Enums
|
||
(`_saveBytes`/`_extractPsdThumbnail`/`_detectType`) – eine reine
|
||
`grep -E '^(class|enum) '`-Suche hätte sie übersehen und stillschweigend aus
|
||
der neuen Datei-Struktur fallen lassen. Deshalb diesmal zusätzlich per
|
||
Diff aller durch Klammer-Zählung abgedeckten vs. nicht abgedeckten
|
||
Zeilenbereiche geprüft, dass keine „Lücke" zwischen zwei Klassenblöcken
|
||
mehr als Kommentar/Leerzeile ist. Für künftige Datei-Aufteilungen (z. B.
|
||
`voice_channel.dart`/`app_shell.dart`, falls Bernd grünes Licht gibt): immer
|
||
zuerst auf lose Top-Level-Funktionen prüfen, nicht nur auf Klassen/Enums.
|
||
|
||
---
|
||
|
||
## 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).
|