Files
rippy/AGENTS.md
T
Hitonabi 4c2331563c
Ampel / ampel (push) Successful in 31s
docs(savepoint): v3.18 - Auswurf, externer Worker, und die Mount-Ursache
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>
2026-07-26 14:40:31 +02:00

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

  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
  • 📝 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.