real-debrid-downloader/tasks/audit-loop.md
Sucukdeluxe f1e35f5f41 Fix: Mega "bis Tagesreset gesperrt" parkt das Paket bis zum Reset statt es den ganzen Tag alle 2 min neu zu versuchen
Wenn ALLE Mega-Debrid-Accounts wegen wiederholt leerer/Server-loser Antworten
bis zum Tagesreset geparkt waren (in-memory untilRestart-Park aus Runde 3),
warf unrestrictWithAccounts einen reinen Klartext-Fehler ohne Maschinen-Token.
Im Manager-Catch fiel dieser durch parseMegaDebridCooldownRetry (kein
mega_debrid_cooldown:-Praefix) und isMegaDebridTransientResolveFailure und
landete im generischen isUnrestrictFailure-Zweig — nur weil der Text
"mega-debrid" enthaelt. Folge: das Item wurde den ganzen Tag etwa alle zwei
Minuten neu versucht (120s-Cap) und fuetterte dabei recordProviderFailure den
Provider-Circuit-Breaker, statt einmal bis zum Tagesreset zu parken. Bei
Standard-Einstellungen (retryLimit=0=unendlich) heilte es sich zwar um
Mitternacht selbst (kein Stranding), war aber unnoetige Log-Flut und Churn und
unterlief genau die untilRestart-Park-Absicht aus Runde 3.

Cross-Layer-Synthese-Pass (adversarisch verifiziert) hat parallel die
Advisor-Hypothese eines Key-Mismatch (Manager cool't aufgeloesten
"megadebrid-api", Fallback-Suche liest rohen "megadebrid") WIDERLEGT:
normalizeProviderOrder speichert immer den aufgeloesten Key, also faellt
Write/Check/Clear/Read auf denselben Key — der Routing-Fix (986fbab) ist fuer
den Mega-Fall nachweislich sicher.

Fix (konservativ, spiegelt den bereits korrekten mega_debrid_cooldown-Zweig):
- debrid.ts emittiert beim untilRestart-Park jetzt den Token
  mega_debrid_reset_park:<msBisReset>: (Delay aus megaDebridDailyParkExpiry,
  deckt sich exakt mit dem in-memory Park-Ablauf).
- download-manager.ts: neue reine parseMegaDebridResetPark (kein 15min-Clamp,
  26h-Cap) + Catch-Branch VOR der Cooldown-Klassifikation, der bis zum
  Tagesreset queued OHNE recordProviderFailure (ein geplanter Park ist kein
  Provider-Fehler).
- Token enthaelt weiter "mega_debrid" → jeder Fall-through landet schlimmstenfalls
  im heutigen Verhalten (sichere Untergrenze).

Tests: 5 neue (parseMegaDebridResetPark: parst/embedded/kein-15min-Clamp/Cap+Junk,
plus cooldown-Parser ignoriert den neuen Token) + die all-parked debrid-Assertion
prueft jetzt den Token. Volle Suite 881 gruen, tsc unveraendert.
2026-06-17 10:16:16 +02:00

192 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Autonomer Audit-Loop — Download/Fehler/Rotation (Goal 2026-06-17, 8h)
Disziplin: erst BELEGEN (Code-Zitat + konkretes Szenario), dann adversarisch verifizieren,
dann TDD-Fix. Kein Blind-Fix. Tests gruen + tsc=6 nach jeder Runde. Periodisch releasen.
## Runde 1 (laeuft)
- Discover+Verify-Workflow ueber 7 Subsysteme (scheduler-slots, unrestrict-retry, mega-rotation,
classify-cooldown, mega-web-token, provider-chain-timeout, account-availability).
### Meine unabhaengigen Verdachtsfaelle (Cross-Check gegen Workflow)
1. **Web-Selbst-Cooldown (Analog zum API-214-Bug, HOCH):** Caller-Timeout = 60s
(`DEFAULT_UNRESTRICT_TIMEOUT_MS`, download-manager 114) umschliesst die GANZE Kette.
Mega-Web braucht legitim laenger (per-Account-Queue bis 90s + Login + Generate).
Feuert die 60s nach >=8s (`MEGA_DEBRID_ABORT_MIN_RUN_MS_DEFAULT`=8000), setzt die
Rotation `aborted:debrid` → 120s Account-Cooldown (debrid.ts ~2037-2048), obwohl der
Account GESUND ist — die App hat aufgegeben. → Kaskade ueber Accounts. Live im jf.zip
belegt: `Mega-Debrid Web | TIMEOUT_COOLDOWN | reason=aborted:debrid | cooldownSec=120`.
Fix-Kandidat: (a) per-Provider-Timeout statt globaler 60s; und/oder (b) Caller-Timeout-
Abort NICHT als Account-Cooldown werten (EMA-Demotion regelt langsame Accounts bereits),
oder nur sehr kurz.
2. **Globaler 60s-Timeout kappt Failover (Advisor-bewiesen, HOCH):** download-manager 8759
`AbortSignal.any([cancel, timeout])` → bei Provider1-Verbrauch des Budgets abortet das
Signal → debrid.ts 3805 `signal.aborted` → throw, kein nextProvider. Fix: per-Provider-
AbortSignal.timeout, Stop nur bei USER-Cancel.
3. **Exponential-Backoff bis 120s** (generic unrestrict retry) — Item sitzt bis 2 min.
Pruefen ob fuer haeufige transiente Faelle zu lang.
## Bestaetigte Bugs (Workflow R1: 14 confirmed / 11 refuted) — priorisiert
- [IN ARBEIT] #1 HIGH Scheduler-Freeze: findNextQueuedItem ohne activeTasks-Guard → synchroner
Admission-Loop dreht endlos wenn ein reset/overwrite-Item noch im activeTasks parkt (non-abort-
observing await, z.B. Integrity-Check). Fix: `if (this.activeTasks.has(itemId)) continue;`. TDD-Test
(Freeze-Repro mit non-abort Mock + resetItems) geschrieben.
- #2/#3 MED mega_debrid_cooldown:<ms> Delay verworfen — kein Parser (nur debrid_link_cooldown). Fix:
Parser fuer beide Praefixe, queueRetry mit echtem delayMs. (Erklaert Rapid-Retry-trotz-Cooldown im jf.zip.)
- #7/#8 MED Mega per-Account Daily-Usage wird am Tagesgrenze NIE resettet → Accounts faelschlich "am
Limit" → schrumpft MEIN neues serialized-limit. Fix: megaDebridAccountDailyUsageBytes in
ensureProviderDailyUsageFresh resetten.
- #4 MED transiente leere Web-Antwort → permanenter until-restart-Park (limitSignal vom generischen
"antwort leer"). Fix: limitSignal nur vom echten Daily-Limit (NO_SERVER_RE).
- #5 MED Web echte Bad-Credentials erreichen invalid-Branch nicht (werden ewig retried). Fix: echte
Web-Login-Fehlerphrasen in invalid-Branch.
- #6 MED onefichier/ddownload-Routing ignoriert autoProviderFallback=off. Fix: Guard in catch.
- #14 LOW Regex-Ordering classify: quota-Branch shadowt rate_limit. Fix: rate_limit vor quota.
- #9 LOW overwrite wipet frisch geclaimten targetPath via altem .finally.
- #10 LOW HTTP416 shared counter mit genericErrorRetries.
- #11 LOW fresh-retry preempt typed transient handlers.
- #12 LOW 15-failure-shelve + shared counters → mehr Retries als retryLimit.
- #13 LOW self-poison: queue-wait zaehlt zu elapsedMs → abort-cooldown (Analog zu meinem Web-Verdacht #1).
## Refutiert / Nicht-Bug (11) — nicht anfassen
providerStartReservations dead-state; debrid_link_cooldown cleanup; supprimé-fallthrough; mega-web 180s
aborts whole rotation; EMA-removed-premise; quota-no-park asymmetry; connectApi single-flight cancel-couple;
per-account queue chain-break (NON-BUG); mega-web slot-hold (NON-BUG); provider abort-vs-timeout heuristic;
daily-limit aggregate early-exit.
## Fixes (TDD, mit Test + Release)
### Batch 1 → v1.7.215 (Suite laeuft)
- [x] #1 HIGH Scheduler-Freeze: `findNextQueuedItem` activeTasks-Guard. Repro-Test (ohne Fix haengt der
Event-Loop so hart, dass nicht mal vitest-Timeout feuert = Freeze empirisch bewiesen). Mit Fix 288ms.
- [x] #2/#3 MED parseMegaDebridCooldownRetry (export) + Handler VOR transient/generic branch → Item wartet
den ECHTEN Cooldown (min ueber alle Accounts) statt 5s-Busy-Loop. 5 Parser-Tests.
- [x] #7/#8 MED megaDebridAccountDailyUsageBytes Reset in ensureProviderDailyUsageFresh (laeuft via
getSnapshot, also auch im Stall). Test: Tagesgrenze → leer.
- [x] #14 LOW rate_limit-Branch VOR quota (quota matchte "limit" in "rate limit"). Test: rate_limit-Kategorie.
- [deferred] #4 empty-response→until-restart-park: 3-consecutive-streak ist reale Mitigation gegen transiente
Blips; Mega-empty-Semantik nicht sicher verifizierbar → kein Blind-Change.
### Strategie-Update (LIVE-Server, Advisor-bestaetigt)
- LOW-Fix-Schwelle HOCH: nur fixen bei NULL plausibler Regression UND einem Test der OHNE Fix rot ist.
Sonst dokumentieren ("gefunden & charakterisiert" ist valides Audit-Ergebnis). Server laeuft live,
auto-update, ~1 TB/h → jede unnoetige Verhaltensaenderung = Risiko.
- Releases BUENDELN (alle 2-3 Runden / Roll-up), nicht pro Fix. Weniger Update-Churn auf dem Live-Server.
- #5 NICHT raten: conversion.log faengt den echten Web-Login-Fehler-String schon (web-queue-Phase-Detail).
Aus naechstem Bundle ernten, dann erst invalid-Phrasen ergaenzen. Kein Phrasen-Halluzinieren.
- #13 defer: Web ist seit v1.7.214 nur noch Fallback (API-first), Selbstcooldown trifft kaum mehr;
braucht workMs-Threading → groesserer Eingriff, nicht LOW-billig.
- Vor Runde 4-5: SYNTHESE-Pass — ist Retry/Cooldown/Rotation END-TO-END kohaerent selbstheilend?
## Runde 3 (Failover/Reconnect/Cooldown-Lifecycle/Scheduler/IPC-Toggle/Updater) — Workflow wcwztx7e9
7 confirmed / 1 refuted. provider-failover-Finder crashte (Socket) → diese Dimension via MEINER
unabhaengigen Code-Verifikation abgedeckt (60s-Timeout kappt Failover, debrid.ts 3845 + dl-mgr 8814).
### Batch 3 → v1.7.217 (GEFIXT, je rot-bewiesener Test)
- [x] #R3-1 HIGH (3/3) Self-Cooldown bis Neustart — DER vom Nutzer gemeldete „Tool sperrt sich selbst".
(a) limitSignal aus MEGA_DEBRID_NO_SERVER_RE-Zweig entfernt (Hoster-Problem != Account-Limit),
(b) until-restart-Park laeuft jetzt zum Tagesreset (lokale Mitternacht) ab statt MAX_SAFE_INTEGER →
heilt <=24h selbst. Texte „bis Neustart"→„bis zum Tagesreset". Commit 76b3f99.
- [x] #R3-5 HIGH (3/3) Fehlgeschlagenes Update → Queue-Stillstand bis Neustart. runInstallWithResume()
(neue reine Funktion) resumt bei started:false UND throw. Commit dfd1926.
- [x] #R3-3 MED (3/3) Account-Edit ueberschreibt megaDebridPreferApi. Hardcode entfernt → ...settings
reicht Nutzerwahl durch. Commit 1e04b7b.
### Dokumentiert / NICHT autonom gefixt (Advisor-Disziplin)
- #R3-2 HIGH (2/3, UMSTRITTEN) Mega API/Web-Account-Zeilen teilen EINE login-only Enable-Flag →
Toggle spiegelt sich (= Nutzer-Report „API aus → Web an"). KEIN Auto-Fix: Daten-Modell-Fix braucht
Settings-Migration (kann deaktivierte Accounts re-aktivieren), UI-Collapse = Layout-Redesign (Nutzer
UI-Geschmack-sensibel). → DEM NUTZER vorlegen: gemeinsamer Schalter vs. unabhaengige pro-Modus-Flags.
- 60s-Failover-Kappung (HIGH, mein Fund, Finder gecrasht) — debrid.ts 3845 wertet JEDEN combined-signal-
Abort (cancel ODER 60s-Timeout) als kein-Failover; langsamer Provider1 hungert Provider2 aus, auch ueber
Retries. Post-214 (API-first) groesstenteils latent. → eigene Runde: gecrashten Finder ERST neu laufen
lassen (unabhaengige Verifikation fehlt), dann per-Provider-Timeout-Design mit Advisor. NICHT in 217.
- #R3-6 MED (3/3) Update-Mirror-Failover feuert nie (nur Gitea). NICHT fixen: aendert den Update-Fetch-Pfad
= der Kanal, ueber den jeder Fix den Nutzer erreicht; faellt heute sicher aus (App behaelt alte Version).
- #R3-4 LOW (2/3) providerPrimary kann auf disabled Mega normalisieren — self-heilt zur Laufzeit. Belassen
(Refuter: Fix riskanter als Bug — schreibt persistierte Absicht um).
- #R3-7 LOW (3/3) Update-Integritaet hash-only, kein Authenticode — ehrliche Grenze, faellt sicher aus.
- REFUTIERT (0/3): all-accounts-parked wirft plain error ohne cooldown-retry-Token.
## Runde 4 (Failover-Routing-Slice + Entscheidungs-Doku)
Follow-on aus R3-Failover-Fund. Advisor-Disziplin: nur die SICHERE Scheibe autonom, der
Produkt-Tradeoff geht an den Nutzer (tasks/entscheidungen-offen.md).
### Autonom gefixt (TDD, rot-bewiesen)
- [x] MED Failover-Routing: Manager berechnete bei Provider-Cooldown (>=20 Fehler in Folge,
auto-Fallback an) einen Ersatz-Provider (`findFallbackProviderNotInCooldown`), WARF ihn aber
weg — `unrestrictLink(item.url, signal)` ohne Hint → debrid.ts baut `order` neu aus
providerOrder und fuehrt WIEDER mit dem ausgebremsten Provider1 an (wahrsch. 60s-Timeout
verschwendet). Fix: reine `leadProviderChainWith(order, preferred)` (debrid.ts, export) +
4. optionaler Param `preferredLeadProvider` an `unrestrictLink`; Manager reicht den Ersatz
durch (dl-mgr 8772/8828). REORDER nicht SKIP → ausgebremster Provider bleibt als letzter
Notnagel in der Kette, kein Stranding. All-cooled-Fall erreicht `unrestrictLink` gar nicht
(else-Zweig queueRetry'd). Tests: integration (control=realdebrid, preferred=debridlink) +
3 reine Helper-Tests (null→unchanged, in-order→leads+keeps-all, not-in-order→unchanged).
Suite 875 gruen, tsc=6. Commit folgt; HALTEN fuer Roll-up-Release (kein HIGH/dringend).
### An den Nutzer vorgelegt (NICHT autonom) → tasks/entscheidungen-offen.md
- 60s-Failover-Kappung (HIGH): A) pro-Provider-Timeout (Failover immer, aber bis 3×60s
Worst-Case, Drehregler ueber Pro-Provider-Wert) vs B) globales Budget mit Failover-Reserve
(langsamer Provider1 frueher abgeschnitten). Produkt-Tradeoff = Nutzerwahl. Post-214 weitgehend latent.
- Gespiegelter Mega API/Web-Schalter (HIGH, #R3-2): gemeinsamer Schalter vs unabhaengige
pro-Modus-Flags (Migration + UI-Redesign noetig, Nutzer UI-sensibel).
### SYNTHESE-Pass (Cross-Layer: Manager-Cooldown-Keys vs Debrid Account/Daily-Park) — Workflow wrfxpjudj
2 Kandidaten, 1 confirmed (3/3), 1 refuted (0/3). Advisor-Hypothese (Key-Mismatch) WIDERLEGT.
- **REFUTED (0/3) KSM-1 Key-Mismatch:** normalizeProviderOrder (storage.ts:144/411) speichert IMMER
den aufgeloesten 'megadebrid-api'/'-web', NIE den virtuellen 'megadebrid'. Also fallen
Cooldown-WRITE (recordProviderFailure), CHECK (getProviderFailureKeyForItem), CLEAR und READ
(findFallbackProviderNotInCooldown) auf denselben aufgeloesten Key → KEINE Divergenz. Der
986fbab-Routing-Fix ist fuer den Mega-Fall nachweislich sicher (No-Stranding haelt).
- **CONFIRMED (3/3) LOW MEGA-UNTILRESTART-MISCLASS (GEFIXT):** Der untilRestart-Park-Throw
(debrid.ts:2167) trug KEINEN Maschinen-Token → Manager-Catch klassifiziert ihn als generischen
Unrestrict-Fehler (isUnrestrictFailure matcht "mega_debrid") → Retry alle ~2min den ganzen Tag
+ recordProviderFailure (Circuit-Breaker-Verschmutzung), statt einmal bis Tagesreset zu parken.
Default-Config (retryLimit=0=∞) self-heilt um Mitternacht, KEIN Stranding. Genau die
Round-3-untilRestart-Park-Absicht, die hier unterlaufen wurde. Predates 986fbab.
Fix: debrid.ts emittiert jetzt `mega_debrid_reset_park:<msBisReset>:` (megaDebridDailyParkExpiry);
neue reine parseMegaDebridResetPark (kein 15min-Clamp, 26h-Cap) + Catch-Branch VOR der
Cooldown-Klassifikation queued bis Tagesreset OHNE recordProviderFailure. Rot-bewiesen
(Parser-Tests + debrid-Token-Assertion). tsc=6.
- **End-to-end-Verdikt:** Retry/Cooldown/Rotation/Failover ist unter Default-Config kohaerent
self-heilend (jeder Park hat Zeitgrenze + Auto-Clear). Einzige Rest-Inkohaerenz war die
Layering-Naht oben (in-memory untilRestart-Park unsichtbar fuer getAvailableMegaDebridAccounts
+ isProviderDailyLimited) — mit dem konservativen String-Klassifikations-Fix geschlossen.
Der breitere Fix (Selektierbarkeit cooldown-aware machen) bewusst NICHT gemacht (groesserer
Blast-Radius auf Live-Server).
## Runde 2 (Download-Ausfuehrung: stream/resume/disk/integrity/extract/persist) — Workflow whspc8ddv
14 confirmed / 7 refuted (>=2/3 adversarisch). Alle HIGH/MED unten unabhaengig am echten Code
verifiziert (Zeilen zitiert) bevor gefixt. Jeder Fix mit rot-bewiesenem Test, tsc bleibt 6.
### Batch 2b → noch nicht released (buendeln, dann v1.7.216)
- [x] #R2-1/4 HIGH Pre-alloc-stat-Reconciliation blaeht `written` auf Padding-Groesse auf → stille
Null-Byte-Korruption auf win32. reconcileFinalizedSize() (download-completion.ts, rein+getestet),
nur noch ABWAERTS-Korrektur bei preAllocated. Commit 2646cba.
- [x] #R2-3 HIGH Nicht-Archiv-".001" ohne Signatur wurde als extrahiert gezaehlt → ganze .00x-Familie
beim Cleanup geloescht (Datenverlust). skippedNonArchives (pathSetKey) aus cleanupSources gefiltert,
frisch + resume. End-to-end-Test. Commit 56bae4a.
- [x] #R2-9 MED + #R2-14 LOW Settings-async-Writer ohne Generations-Schutz → Lost Update; shutdown rief
cancelPendingAsyncSaves nicht. Eigener syncSettingsSaveGeneration (Spiegel des Session-Pfads) +
cancel in shutdown. Commit 1a33fc2.
- [x] #R2-6 MED Teildatei verwaist beim Entfernen eines laufenden Downloads (catch-early-return vor
Cancel-Cleanup). rmSync im catch vor dem return (nach Stream-Close, kein Race). Commit 2b639b7.
- [x] #R2-8 MED Hash-Manifest: Pro-Zeile-Algorithmus von Dateiendung ueberschrieben → gute Datei
geloescht bei fehl-etikettiertem Manifest. parseHashLine-Algorithmus uebernehmen. Commit 4578991.
- [x] #R2-7 MED Startup-Dedup ersetzt gute kanonische Datei durch kleineres Duplikat (+ EXDEV-Loss-
Fenster). Size-Guard (kanonisch >= Duplikat → behalten) + rename-zu-.dedupbak-Reihenfolge mit
Restore. Commit folgt nach voller DM-Suite.
- [deferred/dokumentiert] #R2-5 stream-end akzeptiert truncated download (ohne Laengensignal nicht
entscheidbar; Web ist post-214 nur Fallback) — nur WARN-Log sinnvoll, kein sicherer Fix.
- [deferred/dokumentiert] #R2-10 CRC-verifizierte Kleindatei vom suspicious-small-Heuristik geloescht;
#R2-11 Resume-empty-output-Bypass; #R2-12 7z-Exit-1-Warnung als Erfolg; #R2-13 Companion-.srt/.nfo-
Overwrite. Je narrow/heuristisch → charakterisiert, nicht blind gefixt (LIVE-Server-Schwelle).
### Batch 2 (commit, noch nicht released — buendeln mit Runde-2-Findings)
- [x] #6 MED onefichier/ddownload-catch respektiert autoProviderFallback=off (Guard nach abort-rethrow).
Test: 1fichier KO + Fallback aus → reject, mega getLink NICHT aufgerufen (rot-ohne-Fix beweisbar).
ACHTUNG-Notiz: replace_all matchte faelschlich auch den getLinkInfos-Filename-catch (~3614) → revertet
(Filename-Aufloesung muss nicht-fatal bleiben). Nur die zwei Hoster-catch-Bloecke geaendert. Commit 21fb09b.
- [deferred LOW, dokumentiert statt blind-fix] #9 overwrite targetPath-wipe, #10 HTTP416 shared counter,
#11 fresh-retry preempt typed handlers, #12 shelve+shared counter, #13 queue-wait→elapsedMs, #5 Web-bad-creds.
→ je nur fixen wenn rot-ohne-Fix billig beweisbar + null Regression; sonst bleibt's charakterisiert.