Stellt eine Aussage aus v3.17 richtig: Dort stand die Mount-Sache als "Ursache liegt beim NAS, nicht gefunden". Gemessen liegt sie bei uns - die CIFS-Verbindung lebt in der Netz-Namespace des api-Containers und stirbt mit ihm. Das NAS ist unschuldig. AGENTS.md bekommt die Lehre, die diesen Abend zweimal gekostet hat: Ein Rueckgabewert ist kein Beweis, wo die Wirkung pruefbar ist. CDROMEJECT quittiert Erfolg auf einem verriegelten Laufwerk, `mount` quittiert Erfolg auf einer Verbindung, die Sekunden spaeter stirbt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6.8 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
- Read: KONZEPT.md + ROADMAP.md lesen. Verstehen, welche Etappe dran ist.
- Plan: Was genau soll diese Sitzung bauen? Eine Zeile.
- Build: Code schreiben, testen, committen.
- Verify:
docker compose up— funktioniert das Ganze? - 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.cfgim UI - ✅ Etappe 18 (v3.11): 4K-UHD gelöst —
makemkvconholt 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
c065967als unwirksam entlarvt - ✅ Etappe 20 (v3.15): Aufräum-Runde —
/capabilities1,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_MAPleitet 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, undos.path.isdirhing 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
- 📝 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.
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.