Files
rippy/AGENTS.md
T
Hitonabi b4a0dd551a
Ampel / ampel (push) Successful in 30s
docs(savepoint): v3.19 - sieben Meldungen, drei echte Fehler, eine Kette
Der Savepoint haelt fest, was gemessen wurde und was nicht. Die wichtigste Lehre
steht auch in AGENTS.md: Bei "zu langsam" die DAUER je Schritt messbar machen
statt die plausibelste Ursache zu beheben. Meine erste Erklaerung fuer die 150 s
war falsch und machte es sogar langsamer (202 s); erst Zeitstempel im Log zeigten
die Stelle - ein os.makedirs, das drei Minuten im Kernel hing. Danach 8 s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 15:25:46 +02:00

7.5 KiB

AGENTS.md — Arbeits-Konventionen für Rippy

Grundregeln

1. Plan vor Code. Vor jeder Etappe: Klare Akzeptanzkriterien schreiben, dann bauen. 2. Kleine Schritte. Max. eine Feature pro Commit. Commit-Nachrichten beschreiben WAS und WARUM. 3. Beweisen statt behaupten. Testen, was gebaut wurde. Keine "es sollte funktionieren"-Commits. 4. Deutsch-Nicht-Entwickler. Der Commander liest alles — Variablennamen auf Englisch, aber Kommentare und Docs auf Deutsch.

HARTE Regeln (mechanisch geprüft — seit 22.07.2026)

A. Die CI-Ampel muss GRÜN sein, bevor irgendetwas „fertig" heißt. .gitea/workflows/ci.yml läuft bei jedem Push auf dem Gitea-Runner (NICHT löschen, NICHT abschwächen). Keine Tests = rot = nicht fertig. Der Commander liest die Ampel, nicht den Code.

B. Abweichung vom KONZEPT = STOPP + fragen. Anderes Werkzeug, andere Bibliothek, gestrichenes Muss-Feature → erst den Commander fragen, NIE still ersetzen. (Vorgefallen: Muss-Feature „MakeMKV lossless" wurde still durch lossy HandBrake ersetzt.)

C. Single Source of Truth ist main — es gibt nur diesen einen Branch (seit 24.07.2026; der stable-Zwischenbranch ist abgeschafft). Die CI-Ampel läuft bei jedem Push und PRÜFT nur (Ruff/pytest/Vite-Build) — sie befördert nichts mehr. Deployt wird direkt aus main: auf der VM git pull bzw. ./deploy.sh (verifiziere vorher, dass die Ampel für den Commit GRÜN ist — rot heißt: nicht deployen). Nie freihändig per SSH auf der VM bauen. (Vorgefallen: Doppel-Anlage Rippy + rippy auf der VM durch Freihand-Deploys.)

D. Externe Schnittstellen NIE aus dem Kopf. Vor Nutzung fremder CLI-Flags oder Bibliotheks-APIs: --help/Doku prüfen und die Fundstelle im Commit nennen. (Vorgefallen: erfundene Celery-Methode self.send_task, erfundene abcde-Flags.)

Workflow

  1. Read: KONZEPT.md + ROADMAP.md lesen. Verstehen, welche Etappe dran ist.
  2. Plan: Was genau soll diese Sitzung bauen? Eine Zeile.
  3. Build: Code schreiben, testen, committen.
  4. Verify: docker compose up — funktioniert das Ganze?
  5. Savepoint: SAVEPOINT.md aktualisieren. Nächster Chat beginnt nicht von Null.

Modelle je Aufgabe

  • Planung: Heavy-Modell (Reasoning) für Architektur-Entscheidungen.
  • Bauen: schnelles Coding-Modell.
  • Review: Heavy nochmal für Code-Review vor Merge.

Docker-Praxis

  • Alles läuft in Containern: docker compose up -d
  • Keine System-Pakete auf dem Host — alles im Container.
  • Dockerfile immer multi-stage, kleinste Images.

Was NICHT gebaut wird

  • Kein Code direkt im Host-OS.
  • Kein Proxmox-LXC-Nesting-Workaround — das Projekt lebt bewusst in normalem Docker.
  • Kein ARM-Fork — Rippy ist ein Eigenbau von Grund auf.

Aktueller Stand (26.07.2026)

  • E2E bewiesen (v3.1): BD-50 komplett durch die Kette (43 GB → 4,8 GB)
  • Etappe 13 (v3.2): Universal-Komfort-Runde — Media-Server-Integration, echte Benachrichtigungen, SMB-Klartext-Fehler, UHD-Arbeitsverzeichnis, MakeMKV-Key via UI, Job-Detail-Popup, Toast-Feedback, Ampel-Blocker behoben
  • Etappe 17 (v3.10): 4K-UHD-Disc-Schlüssel — persistentes MakeMKV-Datenverzeichnis + KEYDB.cfg im UI
  • Etappe 18 (v3.11): 4K-UHD gelöst — makemkvcon holt Schlüssel unter Linux nie, unter Windows schon; Schlüsselspeicher übernehmbar. Akira-UHD geht auf der VM auf (TCOUNT:5, bewiesen)
  • Etappe 19 (v3.14): Durchsicht Frontend/Backend — vier Placebos weg (Fortschritt log, Auswurf tat nichts, „Alle Tracks" konnte nichts, Encoder wurden behauptet statt gemessen), Zombie-Erkennung gebaut, Pfad-Prüfung gehärtet, und der Platten-Schutz aus c065967 als unwirksam entlarvt
  • Etappe 20 (v3.15): Aufräum-Runde — /capabilities 1,010 s → 0,003 s (Ping im Hintergrund), Kompression je Disc-Typ abwählbar (4K verlustfrei), Wizard empfiehlt nach gemessener CPU, vier tote Routen entfernt
  • Etappe 21 (v3.17): Der Blocker externes Encoden ist zu — RIPPY_PATH_MAP leitet Rippy aus seinen eigenen Mounts ab, der Installer holt es selbst; Presets kommen vom Worker statt aus dem Quelltext; Dashboard ohne Placebos, mit Restzeit. Dazu vier Bestandsfehler, alle live gemessen (u. a. „Neu komprimieren" ging nie, und os.path.isdir hing im Kernel)
  • Etappe 22 (v3.18): Auswurf wirkt endlich (MakeMKV verriegelt die Tür — erst entriegeln, dann prüfen statt glauben), externer Worker meldet sein Log nach Rippy, zeigt den laufenden Job und nimmt mehrere Aufträge an. Dazu die Mount-Ursache: Die CIFS-Verbindung lebt in der Netz-Namespace des api-Containers und stirbt mit ihm — eine Wache heilt das jetzt selbst
  • Etappe 23 (v3.19): Sprachwahl vor dem Rip (Ton + Untertitel, Automatik einstellbar), Auswurf wirkt wirklich (MakeMKV verriegelt die Tür), externer Worker mit Verwaltungsfenster/Deinstaller/Slots, Schlüssel-Automatik für 4K, Mount-Wiederanbindung 202 s → 8 s, Weitergabe an einem frischen Klon geprüft
  • 📝 Details immer in SAVEPOINT.md — diese Sektion nennt nur die Etappe

Was diese Sitzungen wiederholt gekostet hat

Nicht aus einem Zustandswert auf einen Mechanismus schließen. Vorgefallen: aus „kein Schlüssel da" → „Server abgeschaltet" (falsch), aus Status transcoding → „Celery hat neu zugestellt" (falsch), aus progress=99 → „Altwert aus dem Absturz" (falsch — ein Bug), aus gleichem st_dev → „os.rename funktioniert" (falsch — der Kernel vergleicht den Mount). Jedes Mal hätte eine Messung von unter einer Minute gereicht.

Und die Umkehrung gilt genauso: gleiches st_dev heißt NICHT gleicher Mount. Wo eine Eigenschaft ausprobierbar ist, probiere sie aus, statt sie vorherzusagen.

Ein Hintergrund-Prozess, der still scheitert, ist schlimmer als einer, der laut scheitert (26.07.2026). Ein except Exception: pass in einer Vorrats-Schleife hat eine Stunde gekostet: Der Vorrat blieb leer, die Funktion lief direkt aufgerufen einwandfrei, und der Grund stand nirgends. Gefunden erst über die Thread-Zustände (/proc/<pid>/task/*/stat, Zustand D = im Kernel blockiert). Jede Hintergrund-Schleife MELDET ihren Fehler, und wer einen Vorrat anlegt, macht sein Alter abfragbar (GET /health/vorraete) — sonst ist am Endpunkt selbst nichts zu sehen.

Ein Rückgabewert ist kein Beweis, wo die Wirkung prüfbar ist (26.07.2026, zweimal am selben Abend). CDROMEJECT quittiert Erfolg auf einem verriegelten Laufwerk und wirft nichts aus; mount quittiert Erfolg auf einer Verbindung, die Sekunden später stirbt. Beide Fehler waren monatelang unsichtbar, weil der Code dem Rückgabewert glaubte. Nach einer Aktion den ZUSTAND fragen — und wenn er flattert, zweimal mit Abstand.

Bei „zu langsam" die DAUER je Schritt messbar machen, nicht die plausibelste Ursache beheben (26.07.2026). Eine Mount-Wiederanbindung brauchte 150 s; die erste, sehr plausible Erklärung war falsch, und die Änderung machte es langsamer (202 s). Erst Zeitstempel im Log zeigten die Stelle: ein os.makedirs in Zeile eins, das drei Minuten im Kernel hing. Danach 8 s. Wer eine Dauer nicht aufschlüsselt, optimiert die falsche Stelle.

Netz-Pfade nie ungebremst anfassen. os.path.isdir/open auf einem toten CIFS-Mount blockieren im Kernel und lassen sich aus Python NICHT abbrechen. Ein Kind-Prozess lässt sich abbrechen: timeout N ls -d <pfad> (Muster in mounts.ist_erreichbar und rohdaten.verzeichnis_da). Und „konnte nicht nachsehen" ist etwas anderes als „ist nicht da" — beides zu vermischen erzeugt falsche Aussagen im UI.