Bei Serien, deren per-Episode-Ordner nur einen Episode-only-Token + Titel tragen
("Show.E01.Titel...-GRP", KEIN S01), benannte der Collect eine vom Auto-Rename bereits
korrekt benannte Datei ("Show.S01E01...-GRP.mkv") neu — und haengte den Staffel/Folgen-
Token HINTER die Scene-Gruppe ("...-GRP.S01E01"). In der Library stand dann der Episoden-
titel + ein angehaengtes S01E01 statt sauber S01E01 (gemeldet fuer "Steven Spielbergs Taken").
decideAutoRenameBaseName behaelt im Guard-B-Zweig "Ziel-Ordner ohne SxxExx" jetzt die
QUELLE, wenn sie ein nicht obfuskierter Scene-Name ist (sie traegt dort den einzigen echten
SxxExx-Token) — statt den Token an den Ordnernamen anzuhaengen. Obfuskierte/rohe Quellen
werden weiter aus dem Ordner sauber benannt. Wirkt in Collect und Auto-Rename.
Adversarial (Workflow) abgesichert: der Diskriminator ist allein "Quelle obfuskiert?" —
die Praefix-Laenge ist KEIN Kriterium, sonst fielen kurze Serien (ER, V, 24, Yu) durch und
zeigten denselben Bug. Regressionstest mit ER.S01E01 gepinnt. 4 Unit- + 1 Integrationstest.
24 KiB
Lessons
2026-05-31 — Fix-Diagnose EMPIRISCH bestätigen, bevor man released (Timeout ≠ Account-Hänger)
Muster: "acc2/acc3 nie versucht" wurde als "acc1 hängt → Per-Account-Timeout +
Rotation" diagnostiziert und als v1.7.168 released. Falsch: Mega-Debrid-Web ist eine
180s-Polling-Schleife (mega-web-fallback.ts) — acc1 pollte legitim, der 60s-Global-
Timeout (nicht "Hängen") schnitt es ab. Mein 25s-Per-Account-Cap machte es SCHLIMMER
(endlose 25s-Rotation, Datei nie aufgelöst). Erst der User-Log + Lesen der Provider-
Impl deckte es auf. Revert v1.7.169.
Regel:
- Ein Timeout bei einem langsam-pollenden Provider ist KEIN Account-Fehler → darf keine Rotation/kein Skippen auslösen. Vor "Account hängt"-Annahmen die Provider-Impl lesen (Polling? internes Ceiling? wie lange dauert ein Erfolg legitim?).
- Bei zwei gegensätzlichen Diagnosen (hier: Timeout-zu-kurz vs. IP-Block — stand in der EIGENEN Memory!) NICHT die bequeme wählen + releasen. Erst empirisch diskriminieren (Env-Var auf Server, Beobachtung, oder gezielte User-Frage). Ein Symptom, das BEIDE Hypothesen gleich gut erklärt ("Timeout nach Xs"), beweist keine.
- NICHT lokal "verifizieren" wenn das Problem umgebungsspezifisch ist (geblockte Server-IP) — lokaler Erfolg ist falsch-positiv.
2026-05-30 — Abgestürzten/„aufgehängten" Chat fortsetzen: zuerst reflog lesen
Muster: User bat, einen anderen, aufgehängten Chat-Strang „zu Ende zu bringen".
Der Working Tree sah harmlos aus (nur untracked), aber der eigentliche Fortschritt lag
in einem per reset --hard HEAD~1 weggesetzten Commit, der nur noch im reflog
(dangling) lebte.
Regel: Bei „mach weiter wo es hing":
git reflog+git log --oneline -20zuerst — Ground Truth, NICHT der (evtl. stale) gitStatus-Snapshot oder Konversations-interne Annahmen.- Reset-weggesetzte/dangling Commits (
git fsck --lost-found, reflog) inspizieren (git show <sha>) — dort steckt oft die unfertige Arbeit. - Verstehen WARUM weggesetzt, bevor man blind cherry-picked: hier brach ein
bestehender Test (
.toBe(signal)-Identitätscheck), den der Fix zwingend ändert. Der Reset war die Reaktion darauf, nicht „Fix war falsch". Erst die Reset-Ursache beheben (Test auf Verhalten umstellen), dann den Fix recovern. - Eigene Memory (
project_*) lesen — sie dokumentierte Bug + intendierten Fix exakt.
2026-05-30 — Release verifizieren BEVOR "fertig" gesagt wird; curl -F mit Leerzeichen im Pfad
Muster A (Edit ins Leere + trotzdem released): Ein Edit schlug fehl ("String not
found"), ich habe es übersehen, committet und v1.7.165 released — die Datei enthielt
das Feature NICHT. Erst der nächste Blick zeigte es.
Regel: Nach jedem Feature-Edit VOR dem Release git show HEAD:datei | grep <marker>
— bestätigen dass der Code wirklich im Release-Commit ist, nicht nur dass git commit
durchlief.
Muster B (Gitea UNIQUE constraint): npm run release:gitea pusht erst den Tag,
dann erstellt es den Release. Gitea legt beim Tag-Push automatisch einen Tag-Release-
Eintrag an (name=null). fetchExistingRelease im Script matcht den nicht → POST create
→ UNIQUE constraint failed: release.repo_id, release.tag_name. Commit + Tag sind dann
schon gepusht, nur der Release+Assets fehlen.
Recovery: GET /api/v1/repos/.../releases/tags/<tag> → id holen → PATCH releases/<id>
mit name/body/draft:false → Assets per POST releases/<id>/assets?name=<url-encoded> hochladen.
Muster C (curl -F Datei mit Leerzeichen): curl -F "attachment=@release/Datei mit Leerzeichen.exe.blockmap" lädt FALSCHEN Inhalt hoch (Server-Size != lokale Size).
Regel: Datei mit Leerzeichen im Namen erst nach /tmp/leerzeichenfrei kopieren,
DAS hochladen, Asset-Name über ?name=<url-encoded> setzen. Danach Server-Size gegen
lokale Size prüfen.
2026-05-30 — Nicht in chaotische Parallel-Tool-Batches verfallen (User-Korrektur: "bist du in nem endless loop")
Muster: Bei einem großen Multi-File-Edit habe ich Dutzende Tool-Calls (Bash-Probes,
Reads, Edits, Python-Inline-Skripte, mehrfache tsc-Läufe) in EINEN Message-Block gepackt.
Resultat: Ein einzelner Fehler/Cancel hat die ganze parallele Kette abgebrochen, Edits
landeten halb, ich verlor den Überblick welche Änderung wirklich auf Disk war, und es
wirkte wie eine Endlosschleife. Dazu: wegwerf-scripts/_*.py/_*.txt als Workaround
gegen Output-Encoding statt der dedizierten Tools.
Regel:
- Edits über mehrere Dateien sequenziell, einer nach dem anderen, mit kurzer Verifikation dazwischen — nicht 20 spekulative Calls auf einmal.
- Nach jedem Edit, der fehlschlagen kann (Anchor evtl. nicht eindeutig), das Ergebnis lesen, bevor der nächste folgt. Edit/Write erroren laut — darauf vertrauen.
- KEINE Wegwerf-Python-Skripte ins Repo schreiben, um Shell-Output zu parsen.
Grep/Read/Editnutzen. Wenn doch ein Temp nötig ist: nachos.tmpdir(), nie nachscripts/, und sofort wieder löschen. - Verifikation gebündelt am ENDE (1× tsc, 1× build, 1× vitest), nicht 10× zwischendrin.
2026-05-28 — Analyse-Befund gegen beobachtete Realität gaten (Advisor-Korrektur)
Muster: Meine Analyse sagte einen häufigen Bug voraus (jede letzte Datei im Standard-Modus + jede Nested-Datei landet unbenannt), während der User nur "1-2 pro Staffel" meldete. Ich habe die Diskrepanz bemerkt ("zu schwer um unbemerkt zu bleiben") und sie mit weiterem Timing-Argument wegrationalisiert.
Regel: Wenn die eigene Analyse etwas vorhersagt, das der beobachteten Realität widerspricht, NICHT die bequeme Lesart wählen — mit einem Reproduktions-Test gaten, bevor man fixt. Failing Test gegen den Ist-Stand zuerst (TDD/systematic-debugging Phase 4):
- reproduziert → Bug bestätigt, mit Sicherheit fixen.
- reproduziert nicht → Analyse hat eine Mitigation übersehen, kein Fix für Nicht-Bug.
2026-05-28 — Crash-Debris im Working Tree: stashen, nicht verwerfen
Muster: Eine abgestürzte Session (API 400) hinterließ ein uncommittetes Working Tree,
das drei releaste Commits revertierte. Verlockung: git checkout/discard, um clean HEAD
zu bekommen.
Regel: Fremde/unverstandene uncommittete Änderungen git stash (non-destruktiv,
recoverable), nie blind verwerfen. Gibt clean HEAD, nichts geht verloren, kein Stall auf
User-Rückfrage. Danach dem User sagen WAS gestasht wurde und WARUM.
Wiring-Lock vs. Mechanism-Test
Ein Test, der eine Hilfsfunktion mit dem richtigen Flag direkt aufruft, beweist nur, dass das Flag funktioniert — NICHT, dass der Produktionspfad das Flag setzt. Für echte Absicherung einen End-to-End-Test durch den realen Einstiegspunkt fahren und per Negativ-Gate (Flag temporär entfernen → Test muss fallen) verifizieren.
2026-05-31 — Log-Symptom ≠ User-Wortlaut: greppen, bevor man auf eine Meldung triggert
Muster: User meldete Mega-Debrid-Tageslimit als „Kein Server für diesen Hoster". Ich
wollte den Fix an genau diese Meldung (MEGA_DEBRID_NO_SERVER_RE) hängen. Der Advisor
stoppte: der Screenshot zeigte als Cooldown-Grund „Antwort leer", nicht „Kein Server".
Beweis (Support-Bundle gegrept): „Kein Server"/„Erreur"/„aucun serveur" = 0 Treffer
im ganzen Bundle, „Antwort leer" = 20.861 Treffer. Der limitierte Account liefert im
Web-Pfad NIE eine unterscheidbare Meldung — generate() findet ohne processDebrid-Code
keinen Code → return null → der Aufrufer macht daraus „Antwort leer". Ein Trigger auf
„Kein Server" wäre toter Code gewesen (= die v1.7.172-Falle, zum 2. Mal fast getreten).
Regel: Bevor man einen Fix an einen bestimmten Meldungstext hängt, in den ECHTEN Logs
greppen, ob dieser Text dort überhaupt vorkommt (count-Mode, alt-Text vs. Ist-Text). Sind
zwei Fälle auf Message-Ebene nicht unterscheidbar (Tageslimit vs. transienter Blip → beide
„Antwort leer"), nicht raten — über ein Verhaltens-Signal klassifizieren: hier eine
Streak (3× hintereinander leer → geparkt), nicht der einmalige Wortlaut.
Wiring-Test nicht vergessen (eigene Lesson): die Helfer-Unit-Tests beweisen nur den
Zähler. Ein E2E-Test muss eine ECHTE leere Antwort durch den realen Einstiegspunkt
(unrestrictWithAccounts → classifyAccountFailure → catch → Park) treiben, sonst bleibt
unbewiesen, dass der Produktionspfad das Signal überhaupt setzt.
2026-06-01 — Ein Verifizierer muss dieselbe Pfad-Normalisierung nutzen wie die verifizierte Operation
Muster: Neues Renaming-Logging sollte nach jedem Rename verifizieren, ob die Datei
wirklich unter dem Zielnamen liegt. verifyRename machte statSync/readdirSync auf den
ROHEN Pfaden — der echte Rename lief aber über toWindowsLongPathIfNeeded (?-Prefix
ab >=248 Zeichen). Bei langen Scene-Release-Pfaden (genau das, was die App routinemäßig
umbenennt) scheiterten die rohen fs-Calls → falsches „Ziel nicht gefunden" UND — schlimmer —
die Quell-Prüfung scheiterte ebenfalls → sourceGone fälschlich true → falsches „OK",
das einen halb-fertigen Verschiebevorgang maskiert. Der Diagnose-Log hätte genau die
schwersten Fälle vergiftet. (Adversarialer Review-Workflow fand es, Confidence 0.8.)
Regel: Wenn Code eine Operation VERIFIZIERT, muss er exakt dieselbe Pfad-/Encoding-/ Normalisierung verwenden wie die Operation selbst (hier: ?-Long-Path-Prefix). Sonst mis-reportet der Verifizierer still — und am verlässlichsten bei den Edge-Cases, die man eigentlich fangen wollte. Ein falsches OK in einem Diagnose-Log ist schlimmer als ein falsches ERROR. Zusatz: readdir-Fehler darf nicht zu „Schreibweise ok" degradieren (stilles False-OK) → eigenes WARN-Level „nicht verifizierbar".
Meta: Bei einem Feature, dessen ganzer Zweck Beobachtbarkeit/Verifikation ist, lohnt ein adversarialer Review mit Fokus „würde die Verifikation auf der ECHTEN Last (lange Pfade, case-insensitive FS, EXDEV) korrekt urteilen?" — nicht nur „kompiliert + Happy-Path-Test".
2026-06-03 — Renaming „nie 100%": entkoppelte Scans + Namens-Fabrikation aus token-losen Ordnern
Symptom (aus dem Desktop-Rename-Log diagnostiziert): 17 Dateien landeten ROH in der Library ("tvarchiv...s07e12-720.mkv", "4sf-...s04e01.mkv"). KEINE [ERROR]-Zeile — alle [INFO], weil die Verifikation nur „liegt die Datei am Zielnamen?" prüft, nicht „ist der Zielname sinnvoll?". Das Logging hat den Bug sichtbar gemacht (genau sein Zweck).
Root Cause 1 (entkoppelte Scans): Auto-Rename (scannt nur extractDir, nur present-and- stable Dateien, Freshness-Gate loggt nur via logger.info → keine Session-Spur) und collectMkvFilesToLibrary (verschiebt JEDE .mkv, behielt den rohen Basename) sind getrennte Scans. Eine von Auto-Rename verpasste Datei (verpasster Zyklus ODER lag in „Downloader Unfertig" außerhalb extractDir) wurde von collect roh weggeschoben. Fix: collect leitet den sauberen Namen SELBST ab — über dieselbe Funktion wie Auto-Rename (decideAutoRenameBaseName, single source of truth) → Race wird egal, beide Pfade können nicht mehr divergieren.
Root Cause 2 (latente Fabrikation, vom Advisor gefunden): decideAutoRenameBaseName
fabrizierte „Mega-Direct-Pack.S01E01" für einen generischen Paketordner, weil
hasSceneGroupSuffix("Mega-Direct-Pack") auf „-Pack" falsch-positiv matcht und Guard B dann
die Quell-Episode an einen token-losen Ordner anhängt. Das hätte AUTO-RENAME genauso getroffen
(nur dormant, weil echte Releases saubere Ordner haben). Fix an der Wurzel: Rename nur,
wenn IRGENDEIN folderCandidate einen echten Season-/Episode-Token trägt — ein token-loser
Ordner kann keine Episode autoritativ benennen.
Meta-Lektionen:
- Bei „X nie 100%": die Fehler aus dem ECHTEN Log ziehen (greppen), nicht raten. Hier: „Kein Server" 0×, „Antwort leer" 20k×; und 17 vs vermutete 12 (5 begannen mit Ziffer „4").
- Symptom-Fix vs Wurzel-Fix: ein collect-seitiger Guard (Quell-Auflösung+Codec) hätte das Symptom kaschiert + eine Restlücke gelassen; der Wurzel-Fix in der gemeinsamen Funktion schließt BEIDE Pfade + ermöglicht ehrliches 100%.
- Wenn ein (Sub-)Agent eine empirische Behauptung aufstellt, die der beobachteten Realität widerspricht (Review: „liefert no-target" vs Test: „benennt um"), NICHT raten — mit einem Wegwerf-Diagnose-Test die echte Rückgabe sichtbar machen, DANN entscheiden.
- „raw-keep ist der Boden" als Guard-Prinzip: ein Rename darf nie einen schlechteren Namen erzeugen als der Originalname.
2026-06-03 (2) — Renaming „verschlimmbessert" guten Quellnamen (Scene-Gruppe mit Unterstrich)
Symptom (neues Desktop-Log): castle.s08e02.german.dl.720p.web.h264-idtv_int.mkv (bereits
SAUBER) im Ordner Castle.S08E02.GERMAN.DL.720p.WEB.H264-idTV_iNT (Paket scn2-cstl7) wurde zu
scn2-cstl7.S08E02.mkv — also GUTER Name → obfuskierter Paketname. Andere Klasse als die 17
(roh→nicht-angefasst); hier gut→schlechter.
Ursache (reproduziert, kein Raten): hasSceneGroupSuffix("...H264-idTV_iNT") = false, weil
SCENE_GROUP_SUFFIX_RE/_FALLBACK_RE Unterstriche im Gruppen-Suffix verbieten. → buildAutoRenameBaseName
verwarf den sauberen Episoden-Ordner (return null) → fiel auf den Paketordner scn2-cstl7 zurück
→ Episode angehängt = scn2-cstl7.S08E02. Guard A (Quelle-besser) griff nicht, weil
hasMeaningfulSeriesPrefix("scn2-cstl7.S08E02")=true (Gruppe sieht aus wie Serien-Prefix).
Fix: extractFlexibleSceneGroupSuffix (existierte, war nicht verdrahtet) in hasSceneGroupSuffix
einbinden → Unterstrich-Gruppen erkannt → sauberer Ordner gewinnt → idealer Name.
Meta-Lektionen:
- „100%" gilt nur fuer die DATEN, die man hatte. Mein lueckenloser Check des 2026-06-02-Logs war korrekt — aber ein NEUER Download (Castle/idTV_iNT) brachte eine Gruppen-Form, die im alten Log nicht vorkam. Bei „nie 100%" ehrlich sagen: „fuer die bekannten Faelle 100%, neue Muster brauchen neue Logs". Das Desktop-Log liefert genau diese neuen Muster.
- Reproduzieren statt raten: ein 3-Zeilen-Diagnose-Test (buildAutoRenameBaseName pro Ordner + decideAutoRenameBaseName) zeigte sofort, WELCHER Ordner verworfen wird und warum — nicht spekulieren.
- Offener Backstop-Gedanke fuer echte Robustheit: ein generelles Guard "ersetze nie einen bereits VOLLSTAENDIGEN Quellnamen (Serie+Episode+Aufloesung+Codec) durch einen, der die Serien-Identitaet verliert" wuerde KUENFTIGE unbekannte Gruppen-Formate abfangen — riskanter Eingriff in Guard A, nur mit Tests + auf User-Wunsch.
2026-06-03 (3) — Renaming-Klasse „Junk-Quellname + sauberer Release-Ordner" (Folge-Nummer statt SxxExx)
Symptom (Log 18-18): „Kreuzfahrt ins Glück" — 25 Folgen bet_kig_01_hdt.mkv (obfuskiert, KEIN
SxxExx-Token) im sauberen Episoden-Ordner Kreuzfahrt.ins.Glueck.01.Hochzeitsreise.nach.Burma.2007. German.720p.HDTV.x264-BET (Episode als bloße „01"). Auto-Rename: „kein Zielname" → 25× roh in die
Library. Diesmal SICHTBAR als 25 [WARN] (vorher 0 WARN) — das Log zeigt die Klasse direkt.
Ursache (reproduziert): buildAutoRenameBaseName gibt null zurück, sobald die QUELLE keinen
SxxExx-Token hat (Z.1288) — egal wie sauber der Ordner ist. Das „Folge 01"-Nummernformat (kein
S01E01) wurde nie unterstuetzt. VORBESTEHEND, nicht meine v1.7.178/179.
Fix: Fallback in decideAutoRenameBaseName — wenn kein Zielname UND Quelle hat keinen
Episode-Token, den ersten folderCandidate nehmen, der ein VOLLSTAENDIGER Scene-Release-Ordner ist:
hasSceneGroupSuffix(f) && (RESOLUTION_RE.test(f) || CODEC_RE.test(f)) && !SCENE_SEASON_ONLY_RE.test(f).
Greift NUR ohne Quell-Episode-Token → schliesst sich mit dem Fabrikations-Guard aus (Mega-Direct hat
Quell-Token → unerreicht). note:"folder-as-is".
Advisor-Punkt (wichtig): NICHT nur Aufloesung pruefen — alte deutsche TV-Serien gibt es als
DVDRip/XviD OHNE 720p-Token. RESOLUTION_RE ODER CODEC_RE → sonst die naechste Runde. Pin-Test:
DVDRip-Variante (kein 720p, nur x264).
Edge (Advisor): Bonus/Sample muss VOR diesem Fallback gefiltert werden (sonst kriegt ein Featurette/Sample im Episoden-Ordner den Episodennamen). Bestaetigt: Auto-Rename-Loop (Sample-Size + BONUS_FILENAME_RE) und Collect filtern beide vor der Namensherleitung → gedeckt.
Meta: 3. „anderes Format" in Folge — diese Klasse (Junk-Quelle + sauberer Ordner) ist die groesste verbleibende. Scene-Naming hat aber einen langen Schwanz: ehrlich „diese Klasse ist abgedeckt", nicht „jetzt 100%". Das Desktop-Log liefert jede neue Klasse sofort.
2026-06-04 — KEINE „Claude/AI"-Spuren in oeffentlichen Releases (GitHub)
Korrektur: „kein SCHAU MAL wie ich mit claude gearbeitet hab release … entfern alles was da drin
steckt." Beim einmaligen GitHub-Sync (Sucukdeluxe/real-debrid-downloader) waren oeffentlich: CLAUDE.md,
design-mockups/, tasks/lessons.md+todo.md, historisch .claude/, und 357 Commits mit
Co-Authored-By: Claude-Trailer.
Regel ab jetzt: Fuer dieses Projekt KEINE Co-Authored-By: Claude-Trailer mehr an Commits
(ueberschreibt die Default-Git-Anweisung — User-Wunsch hat Vorrang). Keine KI-Artefakte (CLAUDE.md,
Mockups, lessons/todo, .claude/) in irgendetwas, das oeffentlich gepusht wird.
Wie sauber gemacht (ohne Gitea/lokal anzufassen): isolierter git clone → git filter-repo
(--invert-paths --path … + --message-callback der Trailer-Zeilen droppt) → Force-Push NUR main +
v1.7.180 zu GitHub. Alte Tags NICHT geloescht, sondern via .git/filter-repo/commit-map auf ihre
sauberen Commits umgehaengt (89 Tags, alle Releases bleiben erhalten) — besser als Loeschen.
Ehrliche Grenze (Advisor): Force-Push säubert nur ref-erreichbare Historie. Verwaiste alte Commits
bleiben per voller SHA erreichbar, bis GitHub GC'd ODER das Repo neu angelegt wird (nur der User kann
das — Token hat kein delete_repo). Lokaler Klon verifiziert ≠ GitHub-Zustand: immer per gh api
gegenpruefen (Datei 404 am Tag, Commit-Messages trailer-frei).
Methodik: vor Force-Push Voll-Range-Secret-Scan (push-protection killt sonst mitten im Push) +
Tree-Content-Grep auf claude|anthropic (filter-repo tilgt Pfad-NAMEN + Trailer, nicht Datei-INHALTE).
2026-06-04 — Folge bleibt bei „Downloader Fertig" haengen: Episodentitel == Bonus-Wort
Symptom (User-Screenshot + rd-support-bundle): Revenge.2011.S04E19.Interview...mkv extrahiert +
korrekt umbenannt, aber NIE in die Library verschoben — kein Fehler. „selten, 4-5 Folgen pro 1,5TB".
Diagnose (Bundle): Paket-Log zeigte 22/23 „MKV verschoben", E19 fehlte, KEIN WARN/ERROR. Im
HAUPT-Log (rd_downloader.log) dann 5× MKV-Sammelordner: Bonus-Datei uebersprungen: ...S04E19.Interview.
Root Cause: BONUS_FILENAME_RE enthaelt interview (+ outtakes/special/featurette/bloopers/...). Der
Episodentitel „Interview" (UND der Episoden-Ordnername — isInsideBonusDir macht .includes() Substring)
matchte → collectMkvFilesToLibrary stufte die echte Folge als Bonus/Extras ein und skippte sie. Trifft
auch ganze Serien deren NAME ein Bonus-Wort ist. Skip war nur logger.info → im Paket-Log UNSICHTBAR
(darum „silent orphan", nur via Forensik gefunden).
Fix: neue exportierte isBonusContent(filePath, packageDir, nameWithoutExt) — eine Datei MIT echtem
SxxExx-Token (extractEpisodeToken) ist eine nummerierte Episode, NIE Bonus (egal welches Titelwort).
Echte Extras (kein Token / Extras-Subordner) bleiben gefiltert. Beide Call-Sites umgestellt (Auto-Rename
~4312 + Collect ~5054). 2 Integrationstests (Interview wird gesammelt / Making.Of bleibt) + 5 Unit-Tests.
Diagnose-Lektion (Advisor-Gate): „4-5 Folgen" plural → NICHT beim 1. Fund stoppen. Bundle-weit
gegengeprueft: 0 Move-Fehler, nur 1 Bonus-Skip. 4 weitere „noch frisch"-Defers sahen wie Orphans aus,
waren aber FALSE POSITIVES — Moves loggen NICHT ins Haupt-Log (nur Paket-Log), und deren Paket-Logs fehlten
im Bundle. Per Code bewiesen: finaler Deferred-Collect laeuft fuer jedes fertige Paket (success =
completed-Items, Z.11904) mit deferFreshFiles=false → faengt Frische-Defers. Also Frische orphan't NICHT;
Bonus schon (Filter ignoriert deferFreshFiles, skippt in JEDEM Pass inkl. final). Lehre: bevor man „X ist
Orphan" behauptet, pruefen ob der GEGENBEWEIS (Move) im verfuegbaren Log ueberhaupt sichtbar WAERE.
2026-06-05 — Folge bleibt ROH: vollstaendiger Episoden-Ordner OHNE -GROUP-Suffix
Symptom (rename-session 2026-06-04): safari-fm-s04e08a.avi / ...b.avi landeten ROH in der Library
(entpackt2). Log: Auto-Rename übersprungen: kein Zielname. Funktionierende S01E02 hatte Ordner
...XviD-SAFARi (Gruppe), die kaputten S04E08a/b hatten ...SATRiP.XviD (KEIN -GROUP).
Root Cause (Wegwerf-Diagnose, NICHT geraten): Erste Hypothese „a/b-Token nicht erkannt" war FALSCH —
extractEpisodeToken("...s04e08a")="S04E08" (das Lookahead (?!\d) verbietet nur Ziffern, nicht Buchstaben).
Echte Ursache: das Gate in buildAutoRenameBaseName (isLegacy4sf || isSceneGroupFolder) lehnt einen
vollstaendigen Episoden-Ordner OHNE -GROUP ab (endet auf bare Codec .XviD). Die QUELLE hat aber einen
Token → der v1.7.180-Fallback (greift NUR ohne Quell-Token) feuert nicht → no-target → roh gemoved.
Fix: Gate um isCompleteEpisodeFolder erweitert = echter Episoden-Token IM Ordner UND Codec-/
Aufloesungs-Marker (neue Module-Consts SCENE_RESOLUTION_MARKER_RE / SCENE_CODEC_MARKER_RE, inkl.
xvid/divx). Part-Buchstabe a/b bleibt erhalten (Ordnername dient unveraendert als Zielname; nur der
RANGE-Zweig schreibt Token um, und a/b ist kein Range). Konservativ: bare „Show.S01E01" ohne Marker bleibt
abgelehnt (kein Over-Firing). v1.7.180-Fallback nutzt jetzt dieselben Module-Consts (DRY). Greift in
Auto-Rename UND Collect (beide via decideAutoRenameBaseName). 5 Unit- + 1 Collect-Integrationstest.
Methodik-Lektion: Die naheliegende Hypothese (a/b-Suffix) per Diagnose-Test widerlegt, BEVOR gefixt —
das Lookahead genau gelesen statt angenommen. Spart einen Fix am falschen Ort.
2026-06-05 — Collect zerstoert fertigen S01E01-Namen via Episoden-Titel-Ordner (Miniserie)
Symptom (rename-session 2026-06-05): Miniserie "Steven Spielbergs Taken" landete als
"...E01.Hinter.dem.Himmel...-GTVG.S01E01.mkv" (Episodentitel + hinten angehaengtes S01E01) statt sauber
"...S01E01...-GTVG.mkv". User: "keine Staffel, nur Episodentitel".
Root Cause (diagnostisch bewiesen): Auto-Rename benannte korrekt zu "...S01E01...-GTVG.mkv" (kombiniert
S01 aus dem Paket/Season-Ordner + E01 aus der Quelle). Der COLLECT (deriveCleanCollectFileName ->
decideAutoRenameBaseName) leitet die Datei NEU ab — Quelle ist nun der schon-saubere Name. Der per-Episode-
Ordner traegt aber nur einen Episode-only-Token + Titel ("...E01.Hinter.dem.Himmel...-GTVG", KEIN S01).
buildAutoRenameBaseName nimmt den Ordner (Gruppen-Suffix -GTVG vorhanden). In Guard B if (!targetEpisodeToken)
wird der Quell-Token an den Ordnernamen ANGEHAENGT (applyEpisodeTokenToFolderName) -> "...-GTVG.S01E01"
(Token HINTER der Gruppe = verkrueppelt). Der Root-Guard greift NICHT, weil der Season-Ordner einen S01-Token
liefert (anyFolderHasSeasonOrEpisode=true).
Fix: In Guard B, im !targetEpisodeToken-Zweig VOR dem Anhaengen: ist die QUELLE ein NICHT
obfuskierter Scene-Name (!looksLikeObfuscatedSceneFileName(sourceName)), dann
return {kind:"skip", reason:"source-better"} -> Collect behaelt den fertigen Namen. In diesem Zweig
traegt die Quelle den EINZIGEN SxxExx-Token (Ordner hat keinen) -> obfuskiert? -> Ordner gewinnt (Append),
sauber? -> Quelle gewinnt. Greift NUR im !targetEpisodeToken-Zweig (Ordner ohne SxxExx); safari
(Ordner MIT Token) unberuehrt. 4 Unit- + 1 Collect-Integrationstest. tsc 6 (Baseline), 700/700 gruen, Build gruen.
Methodik: Erst Diagnose (decideAutoRenameBaseName mit Collect-Inputs) -> exakt der mangled Name
reproduziert. Per User-Wunsch adversarial via Workflow gegengeprueft (ultracode, 3 Lenses + Synthese).
Adversarialer Befund (Workflow fing's): Mein erster Guard hatte einen ZWEITEN Konjunkt
hasMeaningfulSeriesPrefix(sourceBaseName) (>=3 Alpha vor S0x). Der ist sachfremd: KURZE Serien (ER, V,
24, Yu) fallen durch -> selber verkrueppelter Name. Gestrichen -> nur !obfuskiert gaten. Lehre: ein
zusaetzlicher "klingt-vernuenftig"-Konjunkt (Praefix-Laenge) kann eine ganze reale Klasse (Kurz-Titel)
stumm ausschliessen; adversariale Verifikation mit konkretem Gegenbeispiel (ER.S01E01) hat's gefunden.