Bug (Code-Review, verifiziert): _Angaben (Titel/Künstler/Fortschritt/
Controls) hat eine intrinsische Mindesthöhe, die während der Öffnen-Geste
(progress zwischen ~0.3 und ~1.0, siehe main.dart Positioned.fromRect mit
Rect.lerp Mini-/Vollbild-Player) das noch kleinere, interpolierte
Rechteck übersteigt — Flutter meldete dann "RenderFlex overflowed" bei
praktisch jeder normalen Öffnen-Geste.
Fix: der Inhalt-Slot (in beiden responsiven Zweigen — Row nebeneinander
und Column untereinander) steckt jetzt in
ClipRect(child: ConstrainedBox(maxHeight: raum.maxHeight - Lücke,
child: OverflowBox(maxHeight: infinity, fit: deferToChild, child: _Angaben))).
ConstrainedBox deckelt nur, was OverflowBox nach AUSSEN als eigene Größe
meldet (verhindert, dass der umgebende Row/Column selbst überläuft);
OverflowBox gibt _Angaben davon unabhängig weiterhin unbegrenzte Höhe
(verhindert, dass _Angaben intern überläuft) — überschüssiger Inhalt wird
vom ClipRect während der kurzen, ohnehin noch recht durchsichtigen
Übergangsphase einfach nicht gemalt statt eine Overflow-Warnung
auszulösen. Bei ausreichend Platz (Endzustand progress==1) ist
raum.maxHeight groß genug, dass nichts geklemmt wird — die Optik bleibt
dort unverändert (now_playing_heart_test.dart bestätigt das weiterhin).
Test zuerst (RED: "RenderFlex overflowed by 13 pixels" reproduziert, mit
NowPlayingScreen in einem echten, interpolierten Zwischenrechteck bei
progress≈0.4 auf Basis der HomeShell-Rect-Formel): test/player/
now_playing_screen_overflow_test.dart.
Volle Suite (626 Tests) grün.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SM6e73V9ZethPiqHSocxUA
Bug (Code-Review, P0 — blockierte den Push): PlayerExpansionController
wurde bisher nur INNERHALB von HomeShell.build() bereitgestellt. Per
Navigator.push geöffnete Screens (PlaylistDetailScreen,
ServerTitelScreen) sind im Widget-Baum aber Geschwister von HomeShell im
selben Navigator/Overlay, keine Nachfahren — sie sahen den Provider nicht.
context.read<PlayerExpansionController>().open(context) warf dort zur
Laufzeit ProviderNotFoundException, vom bestehenden try/catch abgefangen
zu einer "Wiedergabe fehlgeschlagen"-Snackbar — der Player öffnete sich
nie.
MeloApp ist jetzt ein StatefulWidget (TickerProviderStateMixin), erzeugt
PlayerExpansionController in initState() und stellt ihn in seiner
MultiProvider-Liste bereit — oberhalb von MaterialApp/Navigator, wie alle
anderen App-weiten Provider. HomeShell erzeugt keinen eigenen mehr,
sondern liest die Referenz per context.read<PlayerExpansionController>()
einmalig (keine Subscription) und reicht sie an seine AnimatedBuilder
weiter, die weiterhin direkt am Controller lauschen — die bewusste
"Consumer nur um PopScope"-Optimierung (Scaffold wird nicht bei jedem
progress-Tick neu gebaut) bleibt dabei erhalten.
Test zuerst (RED mit der heutigen, echten ProviderNotFoundException
reproduziert — via echtem HomeShell + echtem Navigator.push, nicht dem
bisherigen Kurzschluss-Testaufbau, der den Provider manuell direkt neben
das Test-Widget klebt): test/player/provider_scope_navigator_push_test.dart.
Bestehende Tests, die HomeShell direkt aufbauen (hauptmenue_test.dart,
home_shell_test.dart, home_shell_real_expansion_test.dart), an die neue
Verdrahtung angepasst — der Controller kommt jetzt aus ihrer eigenen
MultiProvider-Liste statt aus HomeShell selbst.
Volle Suite (625 Tests) grün.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SM6e73V9ZethPiqHSocxUA
Bug (Code-Review, P0-nah): dragEnd() wählte die Entscheidungslogik allein
über das Vorzeichen von strecke. Bei bereits vollständig offenem Player
klappte ein schwaches, versehentliches Fingerzittern nach oben (kleine
negative Strecke, weit unter der Wisch-Schwelle) den Player ungewollt zu,
weil das Vorzeichen in den ÖFFNEN-Zweig führte statt in den (korrekten)
Schließen-Zweig, der bei so einer schwachen Geste keine Wirkung hätte.
dragEnd() bekommt jetzt den Pflichtparameter `ausVollbild`, der den
Aufrufer-Kontext explizit benennt statt ihn zu erraten. mini_player.dart
(Öffnen-Geste) ruft mit ausVollbild: false, now_playing_screen.dart
(Schließen-Geste) mit ausVollbild: true.
Test zuerst (RED mit der alten 3-Parameter-Signatur reproduziert, dann
GREEN nach dem Fix): test/player/player_expansion_controller_test.dart.
Bestehende Tests (player_expansion_controller_test.dart,
home_shell_expansion_test.dart) an die neue Signatur angepasst.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SM6e73V9ZethPiqHSocxUA
Zusätzlich test/player/now_playing_heart_test.dart angepasst (Plan-Lücke:
nutzt denselben SongZeile-Tap-Pfad, war nicht in Task 3s Datei-Liste
erwähnt) — Provider ergänzt, Route für die Layout-Prüfung selbst separat
geöffnet, bis Task 5 NowPlayingScreen zu Overlay-Inhalt umbaut.
Budget-Adversarial-Panel (3 Reviewer, unabhängig konvergent) fand einen P0
(AnimatedBuilder.child-Fehlgebrauch macht Blur-Layer nicht live) und mehrere
P1s (State-Persistenz-Widerspruch zur Spec, fehlende Hit-Test-/Accessibility-
Isolation, verworfene Loslass-Geschwindigkeit, fehlendes extendBody, Tests
nur gegen Test-Doppelgänger). Alle behoben: eigene AnimatedBuilder pro
reaktiver Ebene (Reaktivitäts-Regel in Global Constraints), Visibility statt
Conditional-Unmount, IgnorePointer/ExcludeSemantics für Hintergrund-Layer,
echte Fling-Geschwindigkeit, extendBody + BottomNavigationBar-Fade, neuer
Integrationstest gegen das echte HomeShell, State-Persistenz-Test.
Review-Fix: abmelden() setzte _verbleibend bisher nicht zurück,
anders als GastZugang.verwerfen() (_verbleibend = null). Ohne den
Fix bliebe nach einem Account-Wechsel (z.B. Baka -> Tinker) der
alte Kontingent-Stand sichtbar, bis der nächste Download einen
frischen Stand liefert. Aktuell folgenlos (verbleibend ist bis zum
Server-Rollout ohnehin immer null), aber ein latenter Bug.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012A2pmbnVNPiHdyf2GW8eLP
BakaAuth bekommt verbleibend/merkeVerbleibend analog zu GastZugang.
YtDownloadService liest cloud_remaining bei Cloud-Zugriff (nicht bei
Gast-Zugriff — Felder bleiben getrennt). UI zeigt "Noch N von 100
heute" für angemeldete Cloud-Accounts. Reine App-Seite, wirkungslos
bis der Server (separates Vorhaben) cloud_remaining liefert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012A2pmbnVNPiHdyf2GW8eLP
Dustin hat beim Durchsprechen der Proxy-Architektur bestätigt: auch
Dustin/Baka/Tinker bekommen ein Tages-Limit (100 statt 5 bei Gästen),
nicht mehr unbegrenzt. Serverseitiger Vertrag (cloud_quota-Tabelle,
cloud_remaining-Feld) und App-Seite (BakaAuth.verbleibend, UI-Text)
dokumentiert, analog zum bestehenden Gast-Limit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012A2pmbnVNPiHdyf2GW8eLP
Items in song_list.dart und artist_list.dart faden/rutschen beim ersten
Erscheinen gestaffelt statt abrupt ein — angelehnt an HyperOS' fließende
Motion-Sprache. Läuft einmal pro Element, respektiert Reduce-Motion und
deckelt die Verzögerung ab Index 12.
Nach erfolgreichem manuellem Login im "Baka-Konto"-Dialog wird das neue
Passwort jetzt zusätzlich in NavidromeService persistiert (dafür ergänzt:
öffentlicher serverUrl-Getter) — vorher schrieb der Dialog es nur ins
BakaAuth-Token, nie zurück in die lokale Navidrome-Ablage, aus der
autoAnmelden() sich beim nächsten App-Start bedient. Ohne diesen Fix kam
der Auto-Login-Fehler nach Ablauf des 7-Tage-Tokens garantiert wieder.
Zusätzlich hält BakaAuth.letzterAutoLoginFehler jetzt den Fehlertext des
letzten Auto-Login-Versuchs fest, statt ihn zu verwerfen — bisher war aus
der App nicht diagnostizierbar, woran ein Auto-Login scheiterte.
Betraf alle drei Nutzer (Dustin, Baka, Tinker), nicht nur einen: IndexedStack
baut Download-Tab und YT-Suche beim App-Start gleichzeitig, beide loesen
unabhaengig voneinander autoAnmelden() aus. Ohne In-Flight-Absicherung gingen
zwei fast-gleichzeitige Login-Requests an denselben Server, von denen einer
abgelehnt wurde - obwohl die Zugangsdaten korrekt waren.
- BakaAuth.autoAnmelden() haengt einen ueberlappenden Aufruf jetzt an den
bereits laufenden Login-Versuch an, statt einen zweiten Request zu senden.
- Anmelde-Dialog im Download-Tab zeigt den tatsaechlich bekannten
Navidrome-Benutzernamen statt immer "Baka" vorauszufuellen.
- Navidrome-Dialog in den Einstellungen waehlt den zuletzt bekannten
Benutzer vor statt immer "Baka".
- MeloDb.watchRecentlyPlayed(): wertet PlaybackHistory statt dateAddedMs aus,
ein mehrfach gehörter Song erscheint nur einmal, an der Position seines
jüngsten Abspielens (Subquery mit GROUP BY songId / MAX(playedAtMs)).
- "Zuletzt" (Karte + RecentlyPlayedScreen, vormals RecentlyAddedScreen) in
my_music_screen.dart nutzt jetzt watchRecentlyPlayed() statt watchRecent().
- Herz im Vollbild-Player: aus der AppBar-actions-Row entfernt, sitzt jetzt
direkt neben dem Songtitel in _Angaben (now_playing_screen.dart).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012A2pmbnVNPiHdyf2GW8eLP
Tab-Wechsel in HomeShell laufen nur über setState (IndexedStack +
BottomNavigationBar), nie über Navigator.push — eine System-Zurück-Geste
hatte auf Nicht-Start-Tabs also nie eine Route zum Poppen und minimierte
stattdessen die App. build() jetzt in ein PopScope gewickelt, das bei
_index != 0 zuerst zu Tab 0 zurückspringt.
Hermes hat den irreversiblen Löschpfad in melo_cloud.py behoben: Der
Dedup-Zweig von upload() stellt die Datei jetzt aus den hochgeladenen
Bytes wieder her, wenn registry_pfad(sid) leer ist, statt sie zu
verwerfen.
End-to-end verifiziert (eigene Testdatei, Fake-Nutzer, Testdaten danach
restlos entfernt — Registry vor und nach dem Test bei 325 Titeln):
Upload → beide Kopien da · Löschung → beide weg, Download 404 (Bug
reproduziert) · erneuter Upload → beide Kopien zurück, Download
bitgenau identisch mit dem Original.
Damit ist die letzte offene Frage der Spec (A6) beantwortet und keine
Stufe mehr blockiert. Tasks 5-7 des Plans sind entblockt; Tasks 10-12
hängen nur noch an der Produktfrage "wird Feature 2 überhaupt gebaut?"
(0 Playlisten am Server).
Nebenbefund aus dem Test, als Backlog-Notiz festgehalten: _link_user()
legt im Dedup-Zweig einen dritten Hardlink unter users/<user>/ an, den
_entferne_datei_wenn_verwaist() nicht abräumt. Server-Hygiene für
Hermes, kein Datenverlust.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcDiyJdVRqh1TtJk5JiabX
Spec nach dem Review-Panel-Urteil überarbeitet (alle 17 Aktionspunkte).
Der Umfang schrumpft deutlich: 6→5 Ziele, 4→2 neue Dateien, neue
Dependencies 1→0. SSE, persistente Notification und der beidseitige
Playlist-Merge werden spätere Stufen; der Favoriten-Fix schreibt jetzt
additiv (POST /favorites gestrichen), womit der Datenverlust-Bug
strukturell unmöglich wird statt nur per Regel verhindert.
Implementierungsplan: 13 Tasks nach TDD, Favoriten-Fix zuerst (hängt an
keiner offenen Frage). Gegengelesen und geprüft; die Prüfung fand drei
echte Server-Vertragsfehler, die in den eigenen Tests grün geworden
wären (playlist.id statt id, positions statt song_ids, not_found ohne
error-Feld) — alle korrigiert und am Servercode belegt.
OFFEN: Rückfrage an Dustin zum irreversiblen Löschpfad (siehe Spec,
Abschnitt "OFFENE ENTSCHEIDUNG"). Tasks 5-7 und 10-12 sind bis dahin
als blockiert markiert; Tasks 1-4 können sofort starten.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcDiyJdVRqh1TtJk5JiabX
Adversariales 5-Reviewer-Panel (Opus) mit 2 Debattenrunden,
Vollständigkeits-Audit, konsolidierter Faktenprüfung (~70 Belege,
keine Halluzination) und Richterurteil.
Ergebnis: SPEC NACHSCHÄRFEN DANN FREIGEBEN. Kein P0 im Geltungsbereich.
17 Aktionspunkte, wichtigste: POST /favorites (Voll-Ersatz) streichen
statt Regel, SSE und beidseitigen Playlist-Merge herausnehmen,
irreversiblen Bestands-Löschpfad benennen (Backlog-P0, Rückfrage an
Dustin offen).
Bemerkenswert: Der vom Panel selbst empfohlene Basis-Snapshot wurde
von der Verifikation als gefährlicher entlarvt als das Problem, das
er lösen sollte — abgelehnt.
Reviewer-Rohtexte bleiben lokal (.gitignore), nur Bericht + Urteil
im Repo.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcDiyJdVRqh1TtJk5JiabX
Behebt die Findings aus dem abschließenden Gesamt-Branch-Review für
feature/youtube-gast-zugang, alle in einem Rutsch:
- KRITISCH: Gast-Modus war eine Einbahnstraße — einmal "Als Gast
fortfahren" getippt, war _AnmeldeDialog (einziger Baka-Login-Einstieg,
auch für Melo-Cloud-Sync) dauerhaft unerreichbar. Neu:
GastZugang.verwerfen() plus "Anmelden"-Knopf neben der Gast-Statuszeile
in beiden Screens.
- WICHTIG: zugriffOk prüfte den rohen gast.hatToken statt istGastModus —
ein alter Gast-Token konnte den Auto-Login-Rückfallweg für Server-User
verdecken. istGastModus wird jetzt vor zugriffOk berechnet und dort
verwendet.
- WICHTIG: CHANGELOG ergänzt — der Server-Endpunkt POST /api/guest-token
fehlt noch (separates Vorhaben).
- Doppel-Tap-Schutz für den Gast-Button in beiden Screens.
- Widersprüchlicher Hinweistext im YT-Suche-Tab (verlangte Baka-Login
direkt über dem Gast-Button) korrigiert.
- Zwei Gast-Tests in youtube_search_screen_test.dart bekommen jetzt
echtes gast:-Wiring in YtSearchService/YtDownloadService.
- 2 neue Regressionstests fürs Anmelden-Escape-Hatch.
Getestet: volle Suite grün (602 Tests, 1 skipped, 0 failed),
flutter analyze ohne Befund.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcDiyJdVRqh1TtJk5JiabX
Analog zum Download-Tab: echte Gäste sehen zusätzlich zum
bestehenden Hinweis den Button "Als Gast fortfahren (5
Downloads/Tag)"; im Gast-Modus ersetzt ein Zähler den Hinweis.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Echte Gäste sehen jetzt zwei Buttons: "Beim Baka-Konto anmelden"
und "Als Gast fortfahren (5 Downloads/Tag)". Im Gast-Modus wird
der Cookie-Schalter ausgeblendet (Server erzwingt cookies=false
für Gast-Zugriffe ohnehin) und ein Zähler "noch N von 5 heute"
gezeigt, sobald der Server ihn meldet.
_YouTubeBereich.build() beobachtet jetzt app-weit GastZugang; das
Qualitäts-Gate deckte auf, dass hauptmenue_test.dart und
home_shell_test.dart eigene Provider-Bäume ohne GastZugang bauen
(sie spiegeln main.dart nach, statt es zu importieren) — dort
GastZugang ergänzt, analog zum bestehenden Muster für die anderen
Dienste.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Neue Instanz wird beim Start geladen (gespeicherten Gast-Token
wiederherstellen), an YtDownloadService/YtSearchService gereicht
und als Provider bereitgestellt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Analog zu YtDownloadService: optionaler gast-Parameter, X-Guest-
Token statt Authorization, automatisches Token-Nachholen bei 401.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Optionaler gast-Parameter (GastZugang) — sendet bei fehlender
Baka-Anmeldung X-Guest-Token statt Authorization, übernimmt
guest_remaining aus der Erfolgsantwort, holt bei 401 automatisch
einen neuen Gast-Token nach.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Analog zu BakaAuth, aber ohne Passwort: holt/speichert einen
geräte-gebundenen Gast-Token vom neuen /api/guest-token-Endpunkt
(siehe docs/superpowers/specs/2026-08-26-youtube-guest-quota-design.md).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_$(uuidgen | cut -d- -f1)
Design-Dokument für den neuen anonymen Gast-Zugang zum YouTube-Proxy
(5 Downloads/Tag, keine Cookies) neben dem bestehenden
Baka-Auth-Cloud-Zugang. Mit Dustin im Brainstorming abgestimmt:
geräte-gebundener Gast-Token, bewusste Wahl in der UI, Reset nach
Server-Kalendertag, Suche bleibt unbegrenzt. Server-seitiger Teil
(yt_proxy.py) liegt außerhalb dieses Worktrees — die Spec beschreibt
den Vertrag dafür.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcDiyJdVRqh1TtJk5JiabX
Root-Cause-Analyse zu Bakas Login-Problem (mit Hermes): Ihr Baka-Auth-
Account existiert, aber Navidrome- und Baka-Auth-Passwort sind
auseinandergelaufen. Der Rückfall-Hinweis erklärt jetzt, dass eine
einmalige manuelle Anmeldung mit dem aktuellen Baka-Passwort die
beiden Passwörter wieder synchronisiert (baka_auth.py macht das bei
jedem erfolgreichen Login automatisch).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcDiyJdVRqh1TtJk5JiabX
Server-User (Dustin, Baka, Tinker) melden sich beim Öffnen des
YouTube-Bereichs automatisch im Hintergrund mit ihren Navidrome-
Zugangsdaten beim Baka-Konto an (BakaAuth.autoAnmelden) — die
manuelle Anmeldung bleibt nur für Gäste sichtbar. Schlägt die
Auto-Anmeldung fehl, gibt es einen Rückfallweg zur manuellen
Anmeldung, statt den User auszusperren. Login-Buttons vergrößert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcDiyJdVRqh1TtJk5JiabX
- openSystemPanel(): Xiaomi-Sound-Settings-Intent zuerst, dann
Settings.ACTION_SOUND_SETTINGS, dann Android-Standard-Intent als
Fallback. Behebt "kein System-Klangeffekte" trotz Dolby Atmos auf
MIUI/HyperOS (POCO X7 Pro), wo der Standard-Intent keine Activity findet.
- Neuer Tab "YT-Suche": YouTube-Suche über den bestehenden
/api/search-Endpunkt des Baka-Proxys, Ergebnisliste mit Thumbnail
(YouTube-CDN), Titel, Dauer und Download-Knopf pro Treffer — derselbe
Download-Weg wie im bestehenden Download-Tab.
574 Tests grün, flutter analyze ohne Befund, Kotlin kompiliert sauber
(gradlew :app:compileDebugKotlin). Xiaomi-Fix konnte nicht auf echter
Hardware getestet werden.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VM2JK5mV7AL1g2Rt6H6h9w