343545b59e
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1457 lines
90 KiB
Markdown
1457 lines
90 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
|
||
```
|
||
|
||
---
|
||
|
||
## 2026-07-04 – Fable-5-Review (p): Interims-Gate geprüft (korrekt) + halbfertige Threading-Härtung einer abgebrochenen Session vollendet
|
||
|
||
**Erledigt:** Review-Pass über die jüngste erledigte Arbeit (Interims-Gate,
|
||
Commits e97a748/455742d) als Punkt (p) im Fable-5-Review-Abschnitt abgehakt.
|
||
Das Gate selbst ist korrekt (Socket-IP als belastbare Prüfung, Cf-Header nur
|
||
Zweitschranke, Deny-Log gedeckelt, kein Angreifer-Text im Log). Dabei über das
|
||
CHANGES.md-Hook-Log entdeckt: Eine Session hat um 09:43 (NACH dem letzten
|
||
Commit) **drei unprotokollierte server.py-Edits** gemacht und wurde mitten in
|
||
einer Threading-Härtung abgebrochen – `threading`-Import + `DIAG_LOCK` waren da,
|
||
aber der Lock wurde **nirgends benutzt**, und die eigentliche Umstellung auf
|
||
`ThreadingHTTPServer` fehlte (der Kommentar behauptete sie schon). Obendrein
|
||
lief der Dienst noch mit dem Code von 09:14 – nichts davon war je geladen.
|
||
Die angefangene Härtung ist fachlich richtig (der single-threaded `HTTPServer`
|
||
ließ EINEN langsamen Client den ganzen Server blockieren – inkl. des öffentlich
|
||
erreichbaren `/api/livekit-token`), darum vollendet statt zurückgebaut:
|
||
`ThreadingHTTPServer` in `__main__`, Diagnose-Log-Kappen+Anhängen in den
|
||
`DIAG_LOCK` gewickelt (einziger öffentlich beschreibbarer Pfad), `timeout = 30`
|
||
der Vorsession belassen. Dienst-Neustart (kill/`Restart=always`), headless
|
||
verifiziert (11 Prüfungen): stats 200; Admin-Route localhost UND 192.168.178.71
|
||
→ 400 „Kein Nutzer angegeben"; öffentlich UND gespoofter Cf-Header → 403;
|
||
livekit-token ungültig → 401; Diagnose falscher Token 403, gültig 200 + im Log,
|
||
300 KB → 413; **Slow-Loris-Test: parallele Anfrage in 10 ms beantwortet,
|
||
während ein Client hing** (vorher unmöglich), hängender Body-Read nach exakt
|
||
~30 s serverseitig beendet, Dienst danach gesund. Test-Einträge aus beiden
|
||
Logs wieder entfernt.
|
||
|
||
**Offen/Nächster Schritt:** Unverändert – alle offenen ROADMAP-Punkte brauchen
|
||
PC/Gerät (SQLCipher, LiveKit-Client-Umstellung, Härtetest) oder Bernd
|
||
(Secret-Rotation, Dashboard-Fernzugriff A/B, Call-Pilot-Go).
|
||
|
||
**Stolperfallen:** (1) Das CHANGES.md-Hook-Log ist der zuverlässigste Detektor
|
||
für unprotokollierte Arbeit abgebrochener Sessions – Zeitstempel der letzten
|
||
Einträge mit dem letzten Commit vergleichen. (2) „Datei geändert" heißt NICHT
|
||
„läuft": Dienststart-Zeit (`systemctl status`) gegen Datei-mtime prüfen – hier
|
||
lagen 30 Minuten Code unausgeführt auf der Platte. (3) Ein definierter, aber
|
||
nie benutzter Lock ist ein Halbfertig-Marker, kein Schutz.
|
||
|
||
---
|
||
|
||
## 2026-07-04 – Dashboard-Befund (o): akutes Loch mit aussperr-sicherem Interims-Gate geschlossen
|
||
|
||
**Erledigt:** Der KRITISCHE Befund (o) (Dashboard-Admin-Endpoints ohne Auth,
|
||
öffentlich erreichbar) ist entschärft, OHNE Bernds A/B-Entscheidung
|
||
vorwegzunehmen und OHNE ihn auszusperren. Erkenntnis, die das möglich machte:
|
||
Öffentlicher Verkehr erreicht `server.py` (Port 8080) ausschließlich über den
|
||
Cloudflare-Tunnel-Container (Absender-IP 172.x + `Cf-*`-Header) – Bernds
|
||
Heimnetz-Zugriffe kommen dagegen direkt mit `192.168.*`-Socket-IP an. Daher
|
||
„Weg C" als Interims-Gate in `server.py`: Die 5 Admin-Routen (`/api/ban`,
|
||
`/api/unban`, `/api/toggle-registration`, `/api/create-invite`, `/api/run-stats`)
|
||
akzeptieren nur noch Anfragen mit Heimnetz-/localhost-Socket-IP (nicht spoofbar)
|
||
und ohne Cloudflare-Header; sonst 403 mit deutscher Erklärung inkl.
|
||
Heimnetz-URL – das Dashboard zeigt `d.error` per `alert()`, Bernd sieht also
|
||
genau, was zu tun ist. Abgelehnte Versuche landen größenbegrenzt (5-MB-Deckel,
|
||
Lehre aus Befund (l)) in `security_alerts.log` (nur Fixpfad + Socket-IP, kein
|
||
attacker-kontrollierter Text → kein HTML-Injection-Risiko in der
|
||
Dashboard-Anzeige). Dienst-Neustart per kill/`Restart=always`, headless
|
||
verifiziert (9 Prüfungen): öffentlich ban/toggle → 403, localhost UND
|
||
192.168.178.71 → 400 „Kein Nutzer angegeben" (Gate passiert, App-Validierung
|
||
erreicht), gespoofter `Cf-Connecting-Ip` aus dem LAN → 403, `GET /` öffentlich
|
||
200, `/api/livekit-token` 401 und `/api/e2ee-diagnostics` 403 wie bisher
|
||
(bleiben bewusst öffentlich, die App braucht sie). Test-Einträge aus dem
|
||
Alert-Log wieder entfernt. Rückbau jederzeit: `server.py.bak-20260704-dashgate`.
|
||
Doku/ROADMAP nachgezogen (`docs/DASHBOARD_AUTH_HARDENING.md`: Status, Weg C,
|
||
Restrisiken).
|
||
|
||
**Zusätzlicher Befund (für Bernds Secret-Rotation wichtig):** Das Gitea-Repo
|
||
einfach privat zu schalten ist KEINE schnelle Lösung für den Secret-Leak (k):
|
||
Der In-App-Updater (`lib/core/update_checker.dart`) holt Releases **anonym**
|
||
über die öffentliche Gitea-API – Repo privat = Utas installierte App findet
|
||
keine Updates mehr, und ein Fix ließe sich nicht mehr per Update verteilen
|
||
(Henne-Ei). In ROADMAP M0 beim Rotations-Punkt als Warnung vermerkt.
|
||
|
||
**Offen/Nächster Schritt:** Bernds Entscheidung bleibt für den FERNZUGRIFF
|
||
relevant (Weg A empfohlen – schließt auch die weiterhin öffentliche
|
||
Nutzerlisten-/Präsenz-Ansicht; Restrisiken im Doc: LAN-CSRF, offene Ansicht).
|
||
Danach ggf. Gate ergänzen/ersetzen. Sonst unverändert: übrige M0-Punkte
|
||
brauchen PC/Gerät oder Bernd (SQLCipher, Rotation, LiveKit-Client-Umstellung,
|
||
Härtetest).
|
||
|
||
**Stolperfallen:** (1) `stats.html` wird alle 5 Minuten von `stats.sh` (Cron)
|
||
NEU GENERIERT – Änderungen an der HTML-Datei sind flüchtig, UI-Änderungen
|
||
müssten in `stats.sh` (deshalb bewusst NUR server-seitig gefixt). (2) Auf dem
|
||
Pi laufen ZWEI cloudflared-Instanzen: die lokal konfigurierte
|
||
(`cloudflared.yml`, nur Jellyfin) und eine remote-verwaltete
|
||
(`cloudflared-pingvin`, Ingress liegt im Cloudflare-Dashboard, lokal nicht
|
||
einsehbar) – Letztere trägt `dashboard.`/`git.steggi-matrix.work`. Wer nur die
|
||
lokale YAML liest, hält die Subdomains fälschlich für tot. (3) Der
|
||
Header-Check allein würde nicht reichen (Header sind von Direktverbindern
|
||
spoofbar/wegzulassen) – die Socket-IP ist die belastbare Wahrheit, der
|
||
Cf-Header-Check ist nur die zweite Schranke, falls der Tunnel je im
|
||
Host-Netz läuft.
|
||
|
||
---
|
||
|
||
## 2026-07-04 – Fable-5-Review (n)+(o): LiveKit-Route live+geprüft, KRITISCHER Auth-Befund am Dashboard
|
||
|
||
**Erledigt:** Review-Pass über das, was seit (l)/(m) am Server dazugekommen ist –
|
||
zwei neue Punkte (n)/(o) im Fable-5-Review-Abschnitt abgehakt (Details dort).
|
||
- **(n):** Beim Gegenlesen von `server.py` gefunden, dass die geplante
|
||
LiveKit-Token-Route `POST /api/livekit-token` von einer vorher abgebrochenen
|
||
(unprotokollierten) Session **bereits live gebaut** wurde. Kritisch geprüft und
|
||
headless verifiziert: Auth per whoami greift (ungültig→401), Body-Limit (413),
|
||
gültig→200; das JWT unabhängig dekodiert und die **Signatur gegen `livekit.yaml`
|
||
gegengerechnet – gültig**; Identität = geprüfte Matrix-user_id (kein Spoofing).
|
||
Route ist gefahrlos additiv. Server-Seite des M0-LiveKit-Punkts damit erledigt;
|
||
Client-Umstellung + Rotation bleiben PC/Gerät (heiliger Call-Pfad).
|
||
- **(o) KRITISCH:** Derselbe Dashboard-Server hat für seine **Admin-Endpoints
|
||
(`/api/ban`, `/api/unban`, `/api/toggle-registration`, `/api/create-invite`,
|
||
`/api/run-stats`) KEINE Authentifizierung** und ist über
|
||
`https://dashboard.steggi-matrix.work` **öffentlich erreichbar**. Belegt per
|
||
`curl` (App-eigene 400-Antwort über die öffentliche URL, kein Login davor).
|
||
Ein Fremder könnte damit Uta (jeden Nutzer) sperren oder die offene
|
||
Registrierung am Homeserver aktivieren. Der Hostname ist über CT-Logs auffindbar.
|
||
|
||
**Offen/Nächster Schritt:** **Bernd muss entscheiden, WIE das Dashboard künftig
|
||
authentifiziert** – Weg A (Cloudflare Access vor die Subdomain, kein Code, kein
|
||
Aussperr-Risiko, empfohlen) oder Weg B (eigener `DASH_TOKEN` + `stats.html`-Prompt).
|
||
Fertiger Plan mit beiden Wegen: `docs/DASHBOARD_AUTH_HARDENING.md`. Danach sofort
|
||
umsetzbar + headless verifizierbar. Nicht blind gefixt, weil jede Auth-Ergänzung
|
||
Bernds eigenes Admin-Panel bis zur Token-Eingabe lahmlegt („kein Aussperren") –
|
||
das mitten in einer autonomen Session zu deployen ist die falsche Reihenfolge.
|
||
|
||
**Stolperfallen:** Ein Review, der sich auf den *geänderten* Endpoint (Diagnose,
|
||
Disk-Fill) konzentriert, übersieht leicht die *unveränderten Nachbarn* in
|
||
derselben Datei. Lehre: bei einem Sicherheits-Review einer Datei nicht nur den
|
||
Diff prüfen, sondern die ganze Angriffsfläche der Datei (hier: alle Routen des
|
||
`do_POST`-Handlers auf Auth abklopfen, nicht nur die neu berührte).
|
||
|
||
---
|
||
|
||
## 2026-07-04 – Fable-5-Review (l)+(m): Diagnose-Endpoint hatte kein Größenlimit (gefixt)
|
||
|
||
**Erledigt:** Review-Pass über die zwei seit (k) dazugekommenen Arbeiten – als
|
||
Punkte (l) und (m) im Fable-5-Review-Abschnitt abgehakt (Details dort). Bei (l)
|
||
ein **echter Befund** am Server-Gegenpart des Admin-Token-Fixes: Der neue
|
||
`DIAG_TOKEN` steht bewusst im öffentlichen Repo (eng begrenzt, nur Log-Anhängen) –
|
||
aber `/api/e2ee-diagnostics` in `/home/steggi/matrix/server.py` las den
|
||
Request-Body **ohne Größenlimit** und hängte ihn unbegrenzt an die Log-Datei an.
|
||
Jeder Fremde hätte damit die Pi-Platte volllaufen lassen können (Disk-Fill-DoS
|
||
auf dem Host, der auch den Homeserver trägt). Gefixt in `server.py`:
|
||
Body-Limit 256 KB pro Upload (413 bei Überschreitung) + harte Obergrenze 20 MB
|
||
für die Log-Datei (ältere Hälfte fliegt raus). Headless verifiziert nach
|
||
Dienst-Neustart (kill, `Restart=always`): stats.html 200, gültiger Upload 200
|
||
und im Log gelandet, falscher Token 403, 300-KB-Body 413; Kappungslogik isoliert
|
||
getestet (100 Zeilen → 50, neueste überleben). Test-Einträge wieder entfernt.
|
||
|
||
**Offen/Nächster Schritt:** Weiter mit dem obersten offenen ROADMAP-Punkt
|
||
(M0 LiveKit-Token): Server-Route nach `docs/LIVEKIT_TOKEN_MIGRATION.md` bauen –
|
||
rein additiv, Client bleibt unangetastet (heiliger Call-Pfad wartet auf PC/Gerät).
|
||
|
||
**Stolperfallen:** Ein „eng begrenzter" Token im öffentlichen Repo ist nur dann
|
||
wirklich harmlos, wenn das Endpoint dahinter auch Missbrauch begrenzt
|
||
(Größe/Menge) – „keine Admin-Rechte" allein reicht nicht. Bei künftigen
|
||
Endpoints mit öffentlich bekanntem Token immer an Body-/Speicher-Limits denken.
|
||
|
||
---
|
||
|
||
## 2026-07-04 – LiveKit-Token-Minting: Migrationsplan geschrieben (Prep, kein Code)
|
||
|
||
**Erledigt:** Für den offenen M0-Punkt „LiveKit-Token server-seitig minten"
|
||
(letzter Client-eingebetteter Secret nach der Diagnose-Entkopplung) den fertigen
|
||
Umsetzungsplan `docs/LIVEKIT_TOKEN_MIGRATION.md` geschrieben – gegen den echten
|
||
Client-Code (`livekit_token.dart`, `livekit_call_manager.dart:154`) und die echte
|
||
Pi-Konfig (`livekit.yaml`, `server.py`, Continuwuity `whoami`). Kern: Token-Route
|
||
`POST /api/livekit-token` am bestehenden Dashboard-Server, die den Matrix-Access-Token
|
||
per `whoami` prüft und daraus die LiveKit-Identität ableitet (kein Spoofing), JWT
|
||
per stdlib-HMAC mintet (kein neues Paket); Client holt nur noch das fertige JWT und
|
||
`apiSecret` verschwindet aus dem App-Code. Analog zum bewährten
|
||
`SQLCIPHER_MIGRATION.md`-Muster.
|
||
|
||
**Offen/Nächster Schritt:** Umsetzung braucht echten Call-Test auf einem Gerät
|
||
(Calls „heilig", auf dem Pi kein GUI) – daher bewusst nur der Plan, kein Code.
|
||
Danach Secret-Rotation zusammen mit einem Release. Verbleibende M0-Secrets
|
||
(Admin-Token, Gitea-, Giphy-Token) brauchen Bernds Konten-Zugriff.
|
||
|
||
**Stolperfallen:** Keine – reine Doku.
|
||
|
||
---
|
||
|
||
## 2026-07-04 – Admin-Token aus dem Client entfernt (E2EE-Diagnose entkoppelt)
|
||
|
||
**Erledigt:** Den kritischsten Teil des Secret-Befunds (k) wirklich behoben, statt
|
||
ihn nur zu dokumentieren – und zwar sicher, weil diese Session direkt auf dem Pi
|
||
läuft, der den Dashboard-Server hostet (frühere Sessions kannten den Server-Code
|
||
nicht). Kernursache gefunden: `/home/steggi/matrix/server.py` benutzte für das
|
||
Diagnose-Endpoint `/api/e2ee-diagnostics` **denselben String** wie den
|
||
Matrix-Admin-Token (`DIAG_TOKEN = TOKEN`). Deshalb musste der Client diesen Token
|
||
mitliefern – d. h. jeder App-Nutzer (auch Uta) trug den vollen Server-Admin-Token
|
||
im Gerät.
|
||
|
||
- **Server (`server.py`):** `DIAG_TOKEN` ist jetzt ein eigener, zufälliger,
|
||
eng begrenzter Token (`pyr-diag-…`), unabhängig vom Admin-`TOKEN`. Das
|
||
Diagnose-Endpoint hängt nur an den Log an (keine Admin-Rechte) – der neue Token
|
||
ist also von geringer Sensibilität. Der Admin-`TOKEN` (für Ban/Invite/
|
||
Registrierung) bleibt **unverändert**, damit das Dashboard weiterläuft.
|
||
- **Client (`settings_encryption.dart`):** `_uploadDiagnostics` schickt jetzt den
|
||
neuen Diagnose-Token statt des Admin-Tokens. **`grep J5lax lib/` ist jetzt leer**
|
||
– der Admin-Token ist raus aus dem App-Code.
|
||
- **Verifiziert (headless, kein GUI nötig):** Dashboard-Server nach Neustart
|
||
(Prozess gekillt, systemd `Restart=always` lud den neuen Code): `stats.html` 200,
|
||
`registration-status` 200, `/api/e2ee-diagnostics` mit neuem Token **200**, mit
|
||
altem Admin-Token **403**. `flutter analyze` unverändert (1 bekannter Hinweis),
|
||
`flutter test` 24/24 grün. Meine Test-Einträge aus dem Diagnose-Log wieder
|
||
entfernt.
|
||
|
||
**Offen/Nächster Schritt:** Die **Rotation des Admin-Tokens `J5lax…` bleibt
|
||
Pflicht und Bernds Sache** – der Token ist weiterhin gültig und steht in der
|
||
Git-History sowie in Utas bereits installierter APK. Erst nach der Rotation ist
|
||
der Leak wirklich wertlos. Ebenso offen: LiveKit-`apiSecret` (server-seitiges
|
||
Minting), Gitea- und Giphy-Token (siehe ROADMAP M0). **Wichtig für den nächsten
|
||
PC-Lauf / das nächste Release:** Utas aktuell installierte App schickt beim
|
||
Diagnose-Upload noch den alten Token → dieser Button liefert bei ihr jetzt 403,
|
||
bis sie ein neues Release bekommt (nur die Diagnose-Funktion betroffen, nichts
|
||
Heiliges). Der Diagnose-Upload-Button gehört damit in den PC-Testplan (mit neuem
|
||
Build einmal drücken, Log auf dem Pi prüfen).
|
||
|
||
**Stolperfallen:** `systemctl restart matrix-stats.service` scheiterte an
|
||
fehlender polkit-Auth (kein Terminal für sudo). Lösung ohne root: den von systemd
|
||
verwalteten Prozess killen – `Restart=always`/`RestartSec=5` lädt server.py neu
|
||
(neue MainPID bestätigt). Nur anwendbar, weil der Dienst als User `steggi` läuft
|
||
und der eigene Prozess killbar ist.
|
||
|
||
---
|
||
|
||
## 2026-07-04 – Fable-5-Review (k): Secret-Scan – ECHTER Sicherheitsbefund, teils gefixt
|
||
|
||
**Erledigt:** Beim Review-Pass zusätzlich einen systematischen Secret-Scan über
|
||
`lib/`, `scripts/`, `docs/`, `android/` gemacht (CLAUDE.md-Leitplanke „Keine
|
||
Secrets ins Repo"). **Ergebnis: mehrere echte, aktive Zugangsdaten liegen im
|
||
Repo – und das Gitea-Repo ist als `private:false` unter der Internet-Domain
|
||
`git.steggi-matrix.work` erreichbar** (per `curl` bestätigt: Repo-Metadaten und
|
||
Releases anonym abrufbar). Damit sind alle folgenden Secrets als **kompromittiert
|
||
zu behandeln** (seit Commit 25ed765 in der History):
|
||
|
||
- **Matrix-Server-ADMIN-Token** `J5lax…` – lag doppelt drin:
|
||
(1) als Bearer-Token in `docs/matrix-sdk/13-server-admin-continuwuity.md`
|
||
(19 Stellen) und (2) **im ausgelieferten App-Code**
|
||
`lib/widgets/settings/settings_encryption.dart:431` (E2EE-Diagnose-Upload).
|
||
Per `whoami` gegen den Homeserver geprüft: **Token ist noch gültig**
|
||
(`@todesneutron:steggi-matrix.work`, voller Server-Admin). Kritischster Fund –
|
||
jeder App-Nutzer (auch Uta) trägt den Server-Admin-Token im Client.
|
||
- **Gitea-Release-Token** `bb522f96…` (Repo-Schreibrechte) hartcodiert in
|
||
`scripts/release.ps1:14`.
|
||
- **LiveKit-`apiSecret`** `rYUT2PRa…` in `lib/core/livekit_token.dart:6` – die
|
||
App mintet LiveKit-JWTs **client-seitig**, das SFU-Secret muss also im Client
|
||
liegen (Architekturproblem, nicht nur ein Repo-Leak).
|
||
- **Giphy-API-Key** `QtEy…` in `lib/features/chat/gif_sticker_picker.dart:9`
|
||
(geringe Sensibilität, aber ebenfalls im Client).
|
||
|
||
**Sofort (gefahrlos, kein Laufzeit-Bezug) bereinigt:**
|
||
- `scripts/release.ps1`: Token raus, kommt jetzt aus Umgebungsvariable
|
||
`PYRAMID_GITEA_TOKEN`; fehlt sie, bricht das Skript mit klarer Anleitung ab.
|
||
- `docs/matrix-sdk/13-server-admin-continuwuity.md`: alle 19 Token-Stellen durch
|
||
`<ADMIN_TOKEN>`-Platzhalter ersetzt.
|
||
|
||
**Offen/Nächster Schritt (BRAUCHT BERND / PC – nicht blind auf dem Pi fixen):**
|
||
1. **Alle vier Secrets rotieren** (sie sind bereits geleakt, egal was im Code
|
||
steht): neuen Matrix-Admin-Token erzeugen und alten widerrufen; neuen
|
||
Gitea-Token; LiveKit-`apiKey`/`apiSecret` neu setzen (Hetzner-LiveKit +
|
||
`ice.json`); Giphy-Key neu ziehen. **Das ist der wichtigste Schritt** – die
|
||
Repo-Bereinigung allein nützt nichts, solange die alten Werte gültig sind.
|
||
2. **Client-seitige Secrets entschärfen (Architektur):** LiveKit-Token gehört
|
||
server-seitig gemintet (kleiner Token-Endpoint auf dem Pi, App holt sich nur
|
||
das fertige JWT) – dann verschwindet `apiSecret` aus dem Client. Der
|
||
E2EE-Diagnose-Upload darf **niemals** den Server-Admin-Token benutzen: eigenen,
|
||
eng begrenzten Token (nur der Dashboard-Endpoint) oder besser einen
|
||
unauthentifizierten Upload mit Rate-Limit. Beides als ROADMAP-M0-Punkt neu
|
||
aufgenommen. Blind entfernen verbietet sich, weil Calls/Diagnose sonst brechen
|
||
(Calls sind „heilig") und auf dem Pi kein GUI-Test möglich ist.
|
||
3. **Git-History:** Die alten Werte bleiben in der Gitea-History sichtbar. Nach
|
||
der Rotation sind sie wertlos; ein History-Rewrite (force-push) ist optional
|
||
und Bernds Entscheidung (das Repo dient auch als Backup) – daher NICHT
|
||
eigenmächtig gemacht.
|
||
|
||
**Stolperfallen:** Der Diagnose-„Token" in `settings_encryption.dart` ist
|
||
NICHT ein harmloser Dashboard-Key, sondern derselbe String wie der
|
||
Matrix-Server-Admin-Token – beim Lesen leicht zu übersehen, weil er dort nur
|
||
`token:` heißt. Lehre: bei Secret-Funden immer prüfen, ob derselbe Wert an
|
||
mehreren Stellen (Doku UND Client) wiederkehrt und WAS er wirklich autorisiert.
|
||
|
||
---
|
||
|
||
## 2026-07-03 – Fable-5-Review (h)–(j): Test-/Doku-Commits geprüft, 1 echter Befund gefixt
|
||
|
||
**Erledigt:**
|
||
- **Review-Pass über die drei seit (g) dazugekommenen Commits** – als Punkte
|
||
(h)–(j) im Fable-5-Review-Abschnitt abgehakt (Details dort): AuthLog-Tests
|
||
e0ac5cb korrekt (h), PC-Testplan-Update 03b84e0 korrekt (j), und bei den
|
||
settings_prefs-Tests 7dfdbf1 (i) beim Gegenlesen des Produktivcodes ein
|
||
**echter Befund**: Lade-Race in `BoolPref`/`StringSetPref` –
|
||
`StringSetPref.add()` vor abgeschlossenem `_load` konnte **bereits
|
||
persistierte Einträge löschen** (Read-Modify-Write auf ungeladenem State;
|
||
per Regressionstest am alten Code bewiesen, dann gefixt: 7b90f20).
|
||
Vorgehen: Test zuerst geschrieben, Fehlschlag am alten Code gesehen, dann
|
||
Fix, dann alles grün – kein blinder Fix.
|
||
- Unabhängige Verifikation: `flutter test` jetzt **24/24 grün auf dem Pi**
|
||
(2 neue Lade-Race-Tests), `flutter analyze` unverändert 1 bekannter Hinweis
|
||
(`chat_provider.dart:42`). PC-Testplan Schritt 0 auf 24 Tests nachgezogen.
|
||
|
||
**Offen/Nächster Schritt:** Unverändert blockiert – alle offenen ROADMAP-Punkte
|
||
(M0 SQLCipher, M1 Härtetest, M2 Call-Schicht/State-Management) brauchen PC/GUI
|
||
oder Bernds Antworten (Call-Pilot, SQLCipher-Priorität), siehe
|
||
`docs/PC_TESTPLAN.md` und „Fragen an Bernd" weiter unten. Der Lade-Race-Fix
|
||
ist headless voll getestet, gehört aber der Vollständigkeit halber mit in den
|
||
nächsten PC-Klickdurchlauf (Einstellungs-Toggles, Space-Beitritt).
|
||
|
||
**Stolperfallen:** Der BoolPref-Teil des Race ist im Test-Mock NICHT
|
||
reproduzierbar (Microtask-Reihenfolge dort zufällig gutartig: `set()`
|
||
persistiert vor dem `_load`-Read) – nur der StringSetPref-Teil schlug real
|
||
fehl. Lehre: Bei Race-Befunden nicht vom „Test wird schon rot"-Reflex leiten
|
||
lassen, sondern die konkrete Microtask-Reihenfolge durchdenken; ein Guard kann
|
||
richtig sein, auch wenn der Test ihn in dieser Umgebung nicht erzwingen kann
|
||
(dokumentiert im Review-Punkt (i)).
|
||
|
||
---
|
||
|
||
## 2026-07-03 – Review-Nachzügler abgeschlossen + AuthLog-Regressionstests
|
||
|
||
**Erledigt:**
|
||
- **Fable-5-Review vervollständigt (eebf8ae):** Die drei seit Review-Abschluss
|
||
dazugekommenen erledigten Arbeiten kritisch geprüft und als Punkte (e)–(g) im
|
||
Review-Abschnitt abgehakt: Unit-Tests 99cde9a (korrekt, Fake-Clients schlagen
|
||
bei jedem Fremd-Aufruf laut fehl), Doku-Commits 16561bb/566938f (alle
|
||
Behauptungen gegen Code/pubspec/matrix-6.2.0 verifiziert – Factory-Stellen,
|
||
Tabellenname `box_inbound_group_session`), Autopilot-Konto-Wechsel bfe5876
|
||
(kein App-Code, Logik in Ordnung; harmloser False-Positive der
|
||
Limit-Erkennung dokumentiert). **Kein Befund, keine Code-Änderung nötig.**
|
||
Damit sind ALLE als erledigt markierten Punkte reviewt.
|
||
- **`test/auth_log_test.dart` (4 Tests):** `AuthLog` ist die einzige weitere
|
||
öffentliche Schnittstelle auf dem heiligen Pfad, die headless testbar ist
|
||
(der Soft-Logout-Guard ruft `AuthLog.write` im catch-Pfad auf – würde es
|
||
werfen, eskalierte das SDK zu `logout()`+`clear()`). Getestet: write/read
|
||
werfen NIE, auch wenn die Plattform fehlt (kaputter PathProvider);
|
||
Platzhalter statt Fehler bei leerem Log; ISO-Zeitstempel pro Zeile;
|
||
500-Zeilen-Kappung (älteste Einträge fliegen raus, neueste überleben).
|
||
Dafür `path_provider_platform_interface ^2.1.2` als dev_dependency ergänzt –
|
||
offizieller Mock-Weg, war schon transitiv im Lockfile, kein neues externes
|
||
Paket. `flutter test`: alle 14 Tests grün, `flutter analyze`
|
||
unverändert (1 bekannter Hinweis `chat_provider.dart:42`).
|
||
- **`test/settings_prefs_test.dart` (8 Tests):** `core/settings_prefs.dart`
|
||
ist die Storage-Fassade, die laut `docs/M2_MODULE_SCHNITT.md` zur
|
||
Pflicht-Schnittstelle ausgebaut werden soll – dieses Vertragsnetz muss beim
|
||
M2-Umbau stehen bleiben. Getestet mit dem offiziellen In-Memory-Mock
|
||
(`SharedPreferences.setMockInitialValues`, kein neues Paket): `BoolPref`
|
||
(persistierter Wert überstimmt Default, set/toggle persistieren),
|
||
`StringSetPref` (Laden/add/remove, No-Ops bei Duplikat/Fehlendem),
|
||
Theme-Prefs (Defaults, Roundtrip, Teil-Save lässt Rest unangetastet),
|
||
Server-Banner (Roundtrip, Entfernen per null/leer, **korruptes JSON liefert
|
||
leere Map statt Crash** – echter Fehlerpfad). `flutter test`: **alle
|
||
22 Tests grün auf dem Pi.**
|
||
|
||
**Offen/Nächster Schritt:** Unverändert blockiert: Alle offenen ROADMAP-Punkte
|
||
(M0 SQLCipher, M1 Härtetest, M2 Call-Schicht/State-Management) brauchen PC/GUI
|
||
oder Bernds Antworten (Call-Pilot, SQLCipher-Priorität) – siehe
|
||
`docs/PC_TESTPLAN.md` und die Fragen an Bernd weiter unten. Weitere sichere
|
||
Pi-Testkandidaten sind jetzt wirklich erschöpft (Rest ist bibliotheksprivat
|
||
oder braucht Widget-Tests mit gemocktem Matrix-Client – lohnt erst nach dem
|
||
M2-Modulschnitt mit echten öffentlichen Schnittstellen).
|
||
|
||
**Stolperfallen:** `AuthLog` cached sein File-Handle statisch – der Test für
|
||
„Plattform kaputt" muss deshalb VOR den Tests mit funktionierender Plattform
|
||
laufen (im Testfile kommentiert). Bei künftigen Tests an Singletons mit
|
||
statischem Zustand auf Testreihenfolge/Isolate-Grenzen achten (flutter test
|
||
isoliert pro Datei, nicht pro Test).
|
||
|
||
---
|
||
|
||
## 2026-07-03 – Erste Unit-Tests: Regressionsnetz für die zwei heiligsten Codepfade
|
||
|
||
**Erledigt:** Eine Vorsession hatte (unprotokolliert, Abbruch vor dem Testlauf –
|
||
sichtbar nur am untrackten `test/`-Ordner + CHANGES.md-Hook-Log 18:16/18:17)
|
||
zwei Testdateien angelegt. Diese Session hat sie gegen den echten Code
|
||
gegengelesen, ausgeführt und committet (99cde9a) – damit hat das Projekt erstmals
|
||
überhaupt eine Testsuite, und zwar genau dort, wo CLAUDE.md „heilig" sagt:
|
||
|
||
- `test/soft_logout_guard_test.dart` (4 Tests): `guardedSoftLogoutRefresh`
|
||
wirft NIE nach außen (weder Exception noch Error) – ein Throw würde im SDK
|
||
zu `logout()`+`clear()` eskalieren (der Uta-Bug). Geprüft: Erfolg beim
|
||
1. Versuch = genau 1 Aufruf, transienter Fehler wird per Retry geheilt,
|
||
dauerhafter Fehler = genau 3 Versuche + normale Rückkehr. Der Fake-Client
|
||
schlägt bei JEDEM anderen SDK-Aufruf laut fehl – riefe der Guard je
|
||
`logout()`/`clear()` auf, fiele der Test sofort um.
|
||
- `test/power_levels_safety_test.dart` (6 Tests): `updatePowerLevelsSafely`
|
||
(Selbst-Aussperr-Schutz im Space-Admin) – Server-Stand bleibt erhalten
|
||
(fremde users-Einträge überleben; genau der Bug, der schon mal Admins
|
||
ausgesperrt hat), Selbst-Degradierung bricht ab ohne zu schreiben,
|
||
users_default-Fallback, explizites `events[m.room.power_levels]`-
|
||
Erfordernis, M_NOT_FOUND-Start mit leerem Event, fremde Serverfehler
|
||
(M_FORBIDDEN) werden durchgereicht.
|
||
- `flutter test`: **alle 10 Tests grün auf dem Pi** (läuft headless, im
|
||
Gegensatz zu `flutter run` – d. h. dieser Punkt ist NICHT „UNGETESTET (Pi)").
|
||
`flutter analyze` unverändert 1 bekannter Hinweis (`chat_provider.dart:42`).
|
||
- Wert fürs Refactoring: Beide Funktionen sind Vertragsgrenzen, die bei
|
||
M2-Umbauten (Call-Fassade, Storage) nicht wackeln dürfen – künftige
|
||
Sessions sollten `flutter test` fest in Schritt 3 des Arbeitszyklus
|
||
aufnehmen (geht auch auf dem Pi).
|
||
|
||
**Offen/Nächster Schritt:** Unverändert: PC-Termin (docs/PC_TESTPLAN.md) oder
|
||
Bernds Antworten (Call-Pilot, SQLCipher-Priorität). Weitere Test-Kandidaten
|
||
scheitern derzeit an der Sichtbarkeit: `_detectType`, `_formatMessageTime`,
|
||
`_compareSpaceChildren`, die Megolm-Export-Kryptohelfer usw. sind alle
|
||
bibliotheksprivat – testbar erst, wenn beim M2-Modulschnitt echte öffentliche
|
||
Schnittstellen entstehen (dann Tests gleich mitschreiben).
|
||
|
||
**Stolperfallen:** Die Vorsession wurde mitten im Schritt abgebrochen, OHNE
|
||
den Zwischenstand zu protokollieren (Pflicht laut CLAUDE.md) – der untrackte
|
||
`test/`-Ordner war nur per `git status` auffindbar. Lehre: `git status` am
|
||
Sessionstart ernst nehmen; untrackte Dateien können unprotokollierte,
|
||
halbfertige Arbeit einer abgebrochenen Session sein.
|
||
|
||
---
|
||
|
||
## 2026-07-03 – M1-Vorbereitung: SQLCipher-Migrationsplan + konsolidierter PC-Testplan
|
||
|
||
**Erledigt:** Nach Abschluss des Fable-5-Reviews (Abschnitt darunter) die zwei
|
||
obersten offenen ROADMAP-Punkte (SQLCipher, Härtetest) so weit gebracht, wie es
|
||
auf dem Pi ohne GUI/Testgerät sicher geht – beide bleiben offen, aber der
|
||
nächste PC-Termin kann jetzt direkt loslegen statt zu planen:
|
||
|
||
- `docs/SQLCIPHER_MIGRATION.md`: vollständiger Umsetzungsplan, gegen den echten
|
||
Code geschrieben (beide DB-Öffnungsstellen, Factory-Wahl pro Plattform).
|
||
Kernpunkte: gemeinsame DB-Fassade `app_database.dart` (zahlt aufs
|
||
M2-Storage-Modul ein), Android via `databaseFactoryFfi` +
|
||
`openCipherOnAndroid`, im Push-Isolate zwingend `databaseFactoryFfiNoIsolate`
|
||
(dieselbe Isolate-Falle, an der schon `NativeImplementationsIsolate`
|
||
scheiterte), Schlüssel im Android Keystore (`flutter_secure_storage`, schon
|
||
Abhängigkeit), Migration per `sqlcipher_export` in NEUE Datei mit
|
||
Backup/Verifikation/atomarem Tausch – kein Fehlerpfad darf die Klartext-DB
|
||
beschädigen. Marker-Datei gegen das Migration/Push-Race. Phase 1 nur
|
||
Android (sqlcipher_flutter_libs liefert keine Windows-Binaries).
|
||
Tabellenname für die Verifikation gegen matrix-6.2.0 geprüft
|
||
(`box_inbound_group_session`).
|
||
- `docs/PC_TESTPLAN.md`: ALLE aufgelaufenen UNGETESTET(Pi)-Punkte aus den
|
||
bisherigen PROGRESS-Einträgen an einer Stelle konsolidiert (Härtetest/
|
||
Krypto-Kreislauf zuerst, dann die 6 Split-Bereiche, Reorder-Off-by-One-Check,
|
||
PDF-Zoom, Push-Beobachtung, „Uta einmal neu einloggen"). Bisher waren die
|
||
über 7 Einträge verstreut – Gefahr, dass der PC-Lauf etwas übersieht.
|
||
|
||
**Offen/Nächster Schritt:** Unverändert: PC-Termin (jetzt mit fertigem
|
||
Testplan + Migrationsplan) oder Bernds Antworten (Call-Pilot, SQLCipher-
|
||
Priorität). Auf dem Pi ist damit alles abgearbeitet, was ohne GUI/Entscheidung
|
||
sicher geht – inklusive des kompletten Qualitäts-Reviews.
|
||
|
||
**Stolperfallen:** Keine neuen – reine Dokumentation, kein Code angefasst.
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
- [x] **(e) Unit-Tests (99cde9a) gegengelesen – korrekt, keine Änderung nötig.**
|
||
Beide Fake-Clients schlagen bei jedem nicht implementierten SDK-Aufruf laut
|
||
fehl (NoSuchMethodError) – ein versehentlicher `logout()`/`clear()`-Aufruf im
|
||
Guard würde sofort auffallen. `soft_logout_guard_test`: deckt Erfolg,
|
||
transienten Fehler (Retry), dauerhaften Fehler (genau 3 Versuche, kein Throw)
|
||
und `Error` (nicht nur `Exception`) ab – passt zum `catch (e)` im Guard, das
|
||
bewusst alles fängt. Dass die Tests grün sind, beweist nebenbei, dass
|
||
`AuthLog.write` ohne path_provider-Plattform (Testumgebung) sauber schluckt
|
||
statt zu werfen – genau die Eigenschaft, an der der Guard hängt.
|
||
`power_levels_safety_test`: alle 6 Fälle gegen den echten Code
|
||
(`space_admin_core.dart:10-56`) nachvollzogen, inkl. der wichtigen Kante
|
||
„explizites `events[m.room.power_levels]` schlägt `state_default`".
|
||
Einzige Anmerkung: die zwei Retry-Tests schlafen real 2s+4s (Suite ~14s) –
|
||
bewusst so gelassen, statt für Testbarkeit einen Delay-Parameter in den
|
||
heiligen Produktivpfad zu bohren.
|
||
- [x] **(f) Doku-Commits 16561bb/566938f (SQLCipher-Plan, PC-Testplan) gegen
|
||
Code verifiziert – Behauptungen stimmen:** Factory-Stellen exakt wie
|
||
dokumentiert (`matrix_client.dart:24` nativ Android/iOS, `:27` FFI Desktop;
|
||
`background_push.dart:188` nativ), `sqlcipher_flutter_libs ^0.6.8` +
|
||
`flutter_secure_storage ^10` in pubspec vorhanden/ungenutzt wie beschrieben,
|
||
Verifikations-Tabellenname `box_inbound_group_session` steht wirklich in
|
||
matrix-6.2.0 `matrix_sdk_database.dart:139`, beide Prozesse öffnen dieselbe
|
||
`pyramid.sqlite` mit WAL + busy_timeout 5000. PC-Testplan deckt alle in
|
||
PROGRESS verstreuten UNGETESTET-Punkte ab (stichprobenartig gegengeprüft).
|
||
- [x] **(g) Autopilot-Konto-Wechsel (bfe5876, dazu 73420fe) geprüft – kein
|
||
App-Code, funktional in Ordnung:** Wechsel-Logik (pyramid↔haupt, `JUST_SWITCHED`
|
||
verhindert Endlos-Ping-Pong, 20-min-Wartepause wenn beide Konten am Limit),
|
||
`CLAUDE_CONFIG_DIR` wird vor jeder Session neu gesetzt, Matrix-Token kommt aus
|
||
`~/gatus/.env` (nicht im Repo). Einzige Anmerkung (belassen): die
|
||
Limit-Erkennung greppt die GESAMTE Session-Ausgabe auf „usage limit" u. ä. –
|
||
zitiert eine Session diese englischen Phrasen wörtlich, gibt es einen
|
||
unnötigen (aber harmlosen) Konto-Wechsel.
|
||
|
||
- [x] **(h) AuthLog-Tests (e0ac5cb) geprüft – korrekt, keine Änderung nötig.**
|
||
Alle 4 Tests gegen `core/auth_log.dart` nachvollzogen: Der Kaputt-Plattform-
|
||
Test läuft korrekt VOR den Tests mit funktionierender Plattform (das statische
|
||
`_file`-Handle wird beim Throw nie befüllt, daher kein vergifteter Cache);
|
||
Kappungs-Test rechnet richtig (521. Zeile triggert Kappung auf 500, „Hallo
|
||
Log" aus dem Vortest fliegt raus). pubspec-Ergänzung sauber (offizielles
|
||
`path_provider_platform_interface`, war transitiv im Lockfile). Unabhängig
|
||
verifiziert: `flutter test` 22/22 grün, `flutter analyze` 1 bekannter
|
||
Hinweis. Anmerkung (belassen): Die Kappung schreibt die Datei nicht-atomar
|
||
neu und ist nicht gegen parallele Schreiber aus dem Push-Isolate gesperrt –
|
||
für ein reines Diagnose-Log akzeptabel, kein Fix nötig.
|
||
- [x] **(i) settings_prefs-Tests (7dfdbf1) geprüft – Tests korrekt, aber beim
|
||
Gegenlesen des Produktivcodes 1 echter Befund gefunden und gefixt (7b90f20):
|
||
Lade-Race in `BoolPref`/`StringSetPref`.** `StringSetPref.add/remove` machten
|
||
Read-Modify-Write auf möglicherweise noch ungeladenem State – ein `add()`
|
||
direkt nach Erstellung persistierte nur den neuen Eintrag und LÖSCHTE alle
|
||
bereits gespeicherten (per neuem Regressionstest am alten Code bewiesen:
|
||
`['!a:hs']` im Store, `add('!b:hs')` → Store enthielt nur noch `['!b:hs']`).
|
||
Fix: `add`/`remove` warten jetzt auf `_loaded`. `BoolPref` zusätzlich mit
|
||
`_userSet`-Guard, damit ein spät landender persistierter Wert nie eine schon
|
||
getätigte Nutzeraktion zurücküberschreibt (die Microtask-Reihenfolge ist
|
||
plattform-/versionsabhängig – im Test-Mock zufällig gutartig). Einordnung:
|
||
in der heutigen App kaum treffbar (die Provider werden beim App-Start geladen,
|
||
lange bevor jemand klickt – deshalb nie aufgefallen), aber die Fassade wird
|
||
laut `docs/M2_MODULE_SCHNITT.md` zur Pflicht-Schnittstelle für neue, ggf.
|
||
früh laufende Aufrufer. 2 neue Regressionstests, alle 24 grün auf dem Pi.
|
||
- [x] **(j) PC-Testplan-Update (03b84e0) geprüft – stimmt.** Teststand „22
|
||
Tests" war korrekt (durch (i) jetzt 24 – im Testplan nachgezogen, siehe
|
||
Eintrag oben).
|
||
- [x] **(k) Secret-Scan über das ganze Repo – ECHTER Sicherheitsbefund
|
||
(2026-07-04).** Vier aktive Zugangsdaten im öffentlich erreichbaren Repo
|
||
(Matrix-Admin-Token doppelt – auch im Client!, Gitea-Release-Token,
|
||
LiveKit-`apiSecret`, Giphy-Key). Gefahrlose Stellen sofort bereinigt
|
||
(release.ps1 → Env-Var, Admin-Doku → Platzhalter); Rotation aller Secrets +
|
||
server-seitiges LiveKit-Token-Minting + nicht-Admin-Diagnose-Token als neue
|
||
M0-Punkte aufgenommen (brauchen Bernd/Server-Zugriff). Details im
|
||
PROGRESS-Eintrag 2026-07-04 oben.
|
||
- [x] **(l) Admin-Token-Entkopplung (925aafb) geprüft – Client-Seite korrekt,
|
||
am Server-Gegenpart 1 echter Befund gefunden und gefixt (2026-07-04).**
|
||
Client-Diff verifiziert: neuer `pyr-diag-…`-Token stimmt mit `DIAG_TOKEN` in
|
||
`server.py` überein, `grep J5lax lib/ scripts/` unabhängig nachgeprüft (leer;
|
||
einziges verbliebenes Client-Secret ist das LiveKit-`apiSecret`, eigener
|
||
M0-Punkt). Befund: `/api/e2ee-diagnostics` hatte **kein Body-/Log-Größenlimit**
|
||
bei öffentlich bekanntem Token → Disk-Fill-DoS möglich. Gefixt + headless
|
||
verifiziert (200/403/413, Kappung), Details im PROGRESS-Eintrag 2026-07-04.
|
||
- [x] **(m) LiveKit-Migrationsplan (78e4174) gegen Code + Pi-Konfig geprüft –
|
||
Behauptungen stimmen.** `livekit_token.dart:6` enthält das `apiSecret`,
|
||
Aufrufstelle `livekit_call_manager.dart:154` mit `ttlSeconds: 21600`, Grants
|
||
im Plan identisch mit dem Dart-Code (roomJoin/room/canPublish/canSubscribe/
|
||
canPublishData, metadata = Anzeigename, iss = LKMatrixPi, sub = identity),
|
||
`livekit.yaml` `keys:` wie beschrieben, Continuwuity antwortet auf
|
||
`127.0.0.1:6167` (whoami-Basis). Python-Snippet (stdlib-HS256) fachlich
|
||
korrekt (base64url ohne Padding, kompaktes JSON, HMAC-SHA256). Anmerkung:
|
||
Der Plan setzt zusätzlich `nbf`/`name` – LiveKit akzeptiert beides, harmlos.
|
||
- [x] **(n) LiveKit-Token-Route ist inzwischen LIVE auf dem Pi gebaut (bisher
|
||
unprotokolliert) – geprüft und funktionsfähig, aber Client noch nicht
|
||
umgestellt (2026-07-04).** Beim Review von `server.py` gefunden: Eine
|
||
Vorsession hat die in `docs/LIVEKIT_TOKEN_MIGRATION.md` geplante Route
|
||
`POST /api/livekit-token` bereits in `server.py` implementiert (whoami-Auth,
|
||
stdlib-HS256-Minting aus `livekit.yaml`) – der CHANGES-Hook zeigt drei
|
||
server.py-Edits um 04:33, Dienst-Neustart 04:34, aber kein PROGRESS-Eintrag
|
||
(Session vor dem Protokoll abgebrochen). Headless verifiziert (127.0.0.1:8080
|
||
UND öffentlich): ungültiger Matrix-Token → 401, fehlende Felder → 400, 5-KB-Body
|
||
→ 413 (4-KB-Limit), gültiger Admin-Token → 200; das geminete JWT unabhängig
|
||
dekodiert: iss=LKMatrixPi, sub/name/metadata = geprüfte user_id (kein Spoofing,
|
||
Client kann Identität NICHT wählen), video-Grants wie im Client, exp−nbf =
|
||
21600 s, **Signatur unabhängig gegen `livekit.yaml` gegengerechnet: gültig**.
|
||
Die Route ist gefahrlos additiv (altes Client-Secret bleibt bis zur Rotation
|
||
gültig, nichts bricht). **Offen bleibt:** Client-Umstellung
|
||
(`livekit_token.dart`/`livekit_call_manager.dart`) + Secret-Rotation – beides
|
||
weiterhin PC/Gerät (heiliger Call-Pfad), siehe Migrationsplan. Server-Seite des
|
||
M0-LiveKit-Punkts ist damit erledigt.
|
||
- [x] **(o) KRITISCHER Befund beim server.py-Review: die Admin-Endpoints des
|
||
Dashboards haben KEINE Authentifizierung und sind öffentlich erreichbar
|
||
(2026-07-04).** Der Review von (l) hatte nur das Diagnose-Endpoint auf
|
||
Disk-Fill gehärtet – der viel größere Nachbar blieb unbemerkt:
|
||
`https://dashboard.steggi-matrix.work` ist über Cloudflare offen aus dem
|
||
Internet erreichbar, und `POST /api/ban`, `/api/unban`, `/api/toggle-registration`,
|
||
`/api/create-invite`, `/api/run-stats` prüfen **keinen** Token. Belegt:
|
||
`curl -X POST .../api/ban -d '{"user":""}'` liefert die App-eigene 400-Antwort
|
||
`{"error":"Kein Nutzer angegeben"}` – identisch zu 127.0.0.1, also KEIN
|
||
Cloudflare-Login davor. Damit könnte jeder Fremde Uta (jeden Nutzer) sperren
|
||
oder die offene Registrierung am Homeserver aktivieren. Der Hostname ist über
|
||
Certificate-Transparency-Logs praktisch auffindbar – „geheim" trägt nicht.
|
||
**Bewusst NICHT blind gefixt:** Jede Auth-Ergänzung macht Bernds
|
||
Browser-Dashboard funktionslos, bis er einen Token einträgt / Cloudflare Access
|
||
einrichtet – das mitten in einer autonomen Session zu deployen sperrt ihn ohne
|
||
Vorwarnung aus seinem eigenen Admin-Panel („kein Aussperren"). Fertiger,
|
||
gegen den echten Code geschriebener Plan mit zwei Wegen (A: Cloudflare Access =
|
||
Königsweg, kein Code; B: eigener `DASH_TOKEN` + `stats.html`-Prompt) liegt in
|
||
`docs/DASHBOARD_AUTH_HARDENING.md`. **Braucht Bernds Wahl A oder B, dann
|
||
sofortige Umsetzung + headless-Verifikation.** In ROADMAP M0 als dringender
|
||
Punkt aufgenommen. **Update (später am 2026-07-04):** Das akute Loch ist mit
|
||
einem aussperr-sicheren Interims-Gate geschlossen (Admin-Routen nur noch
|
||
Heimnetz/localhost, siehe PROGRESS-Eintrag „Dashboard-Befund (o)" oben) –
|
||
offen bleibt nur noch die Fernzugriffs-Entscheidung A/B.
|
||
- [x] **(p) Interims-Gate (e97a748/455742d) reviewt – korrekt; dabei halbfertige,
|
||
unprotokollierte Threading-Härtung einer abgebrochenen Session gefunden und
|
||
vollendet (2026-07-04).** Gate-Logik bestätigt: Socket-IP ist die belastbare
|
||
Prüfung (nicht spoofbar), Cf-Header nur Zweitschranke, Deny-Log mit 5-MB-Deckel
|
||
und ohne attacker-kontrollierten Text; Restrisiken (LAN-CSRF, offene
|
||
Nutzerlisten-Ansicht) waren bereits in `docs/DASHBOARD_AUTH_HARDENING.md`
|
||
dokumentiert – kein neuer Befund am Gate. ABER: CHANGES-Hook zeigte drei
|
||
server.py-Edits um 09:43 ohne Commit/Protokoll – abgebrochene Session hatte
|
||
`DIAG_LOCK` definiert, aber nie benutzt, und `ThreadingHTTPServer` im Kommentar
|
||
behauptet, aber nicht umgestellt; der Dienst lief zudem noch mit altem Code.
|
||
Vollendet (Threading + Lock-Nutzung), Dienst neu gestartet, 11 Headless-
|
||
Prüfungen grün inkl. Slow-Loris (parallele Anfrage 10 ms statt blockiert;
|
||
hängender Body-Read nach 30 s gekappt). Details im PROGRESS-Eintrag „(p)" oben.
|
||
|
||
**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).
|