# Lessons ## 2026-04-21 — DOM-Doppelrender bei Bulk-State-Changes **Symptom:** User klickt auf "Erneut versuchen" mit 500+ Jobs → App hängt sekundenlang. **Root cause:** `retrySelectedJobs()` ruft `renderQueueTable + updateQueueActionButtons + updateStatusBar` auf, `startSelectedUpload()` ruft direkt danach genau dieselben Funktionen nochmal auf. **Regel:** Wenn ein Click-Handler `await anotherHandler()` aufruft und der innere Handler seinen eigenen kompletten Render-Zyklus hat, NIEMALS noch einen davor. Einmal ist genug — der folgende innere Render sieht die frischen State-Mutationen ohnehin. **Wie anwenden:** Vor jeder `await fn()`-Folge in einem Handler prüfen: macht `fn` schon `renderQueueTable()`? Wenn ja, äußere Render-Calls löschen. ## 2026-04-21 — State-Checks MÜSSEN hinter die Semaphore-Queue **Symptom:** Pre-Job-Swap prüfte `_failedAccounts` vor `semaphore.acquire`. Bei N parallelen Workers war der Check zum Start für ALLE leer — niemand hat geswapt. Erst nachdem alle im Semaphore ordentlich gewartet hatten und einer fehlschlug, wurde _failedAccounts befüllt, aber die anderen hatten ihren Check längst hinter sich. **Regel:** State-basierte Entscheidungen (failed accounts, overrides, cached stats) gehören direkt vor die Aktion die sie betreffen — **nach** jeder async `await` die die Position in der Queue bestimmt. Nicht am Task-Start für später wichtigen State abfragen. **Wie anwenden:** Bei Queue-basierten Pipelines prüfen: "Was kann sich zwischen Task-Start und dem tatsächlichen Execute ändern?" Alles was sich ändern kann, muss direkt vor dem Execute geprüft werden, nicht davor. ## 2026-04-21 — Reaktive Config-Updates für laufende State-Maschinen **Symptom:** User fügt mid-batch einen neuen Account hinzu, aber der UploadManager merkt nicht dass die Config sich geändert hat. `account-failed` Event feuert nur einmal pro Account → keine zweite Re-Resolve-Chance. **Regel:** Wenn ein State nur bei Events neu evaluiert wird und Events "nur einmal" feuern, muss jede externe Zustandsänderung (Config-Save, User-Action) den State explizit triggern. **Wie anwenden:** Save-Handler müssen aktive State-Maschinen informieren. Lieber einen überflüssigen Re-Resolve-Call als einen verpassten. Für Upload-Manager: nach saveConfig → re-evaluate failed accounts ohne Override. ## 2026-04-21 — Error-Klassifikation: fileRejected vs accountError **Symptom:** Voller Byse-Account wurde nicht rotiert — `skip-rotation-file-rejected` geloggt für jede Datei. **Root cause:** Generisches Match auf Prefix-String (`"lehnte Datei ab"`) klassifizierte ALLE Byse-Errors als file-level, inklusive Account-voll-Meldungen. **Regel:** Hoster-Parser setzen den **spezifischen Flag** (`fileRejected` ODER `accountError`), nicht beide nie. Classifier matcht **konkrete Phrasen** (Duplicate, Not video format, …), niemals generische Wrapper-Strings die für mehrere Fehlerarten benutzt werden. **Wie anwenden:** - Bei neuen Hostern: per-status-Klassifikation bereits im Parser, nicht erst im Upload-Manager. - Classifier-Regexes auf Rejection-Kernphrasen, nicht auf UI-Prefix. - Defensive: `accountError === true` gewinnt immer gegen `fileRejected` — Account-Rotation ist weniger schlimm als endlose Fails auf einem toten Account. ## 2026-04-21 — Keine fake Build-ETAs **Symptom:** User wartet 5+ min auf Tauri-Build den ich mit "1-2min" angekündigt habe. **Regel:** Tauri-Release-Builds brauchen real 3-6 min (Rust + NSIS + MSI). Keine Zeitangabe oder ehrlich "kann 3-6min dauern" schreiben. **Wie anwenden:** Wenn User nach Status fragt: sofort `tail` des Logs + `ls` des Bundle-Ordners zitieren, nicht raten.