diff --git a/SAVEPOINT.md b/SAVEPOINT.md index 40d4721..c306eb8 100644 --- a/SAVEPOINT.md +++ b/SAVEPOINT.md @@ -1,6 +1,149 @@ # SAVEPOINT — Rippy -## Aktueller Stand: v3.21 — Rippy Windows Worker (27.07.2026) +## Aktueller Stand: v4.0-alpha — Rippy v2 beginnt, Etappe V2-0 steht (28.08.2026) + +> **Zwei Dinge sind passiert: Das Konzept für Rippy v2 liegt vor und ist vom +> Commander entschieden — und die erste Etappe ist gebaut, geprüft und grün.** + +### ZUSTAND, gemessen + +| | | +|---|---| +| Repo | `b98dc5e` auf `main`, Ampel **GRÜN** (Gitea-Lauf b98dc5ee: success) | +| VM | **NICHT deployt** — bewusst, siehe „Was als Nächstes ansteht" | +| Tests lokal | **290 grün**, 1 übersprungen (ohne die zwei Linux-only-Module) | +| Doppelte Module im Repo | **0** — vorher 3 (`detection`, `makemkv_daten`, `notify`) | +| Zeilen netto | **−492** (234 hinzu, 726 weg) | + +### 1. Das v2-Konzept: drei Betriebsmodi statt einem + +Der Commander wollte Rippy in drei Ausprägungen: Docker (wie heute), eine +native Windows-App **ohne Docker**, und eine Headless-Linux-Anwendung — alle +drei mit demselben Webinterface. + +**Vollständige Spezifikation: `KONZEPT-V2.md`** (Systemarchitektur, +Daten-/Queue-Strategie, die drei Plattformen im Detail, API-/Event-Design, +Migrationsplan). Etappen V2-0 bis V2-7 stehen in `ROADMAP.md`. + +**Der tragende Gedanke:** Rippy v2 ist EINE Anwendung mit DREI Verdrahtungen, +keine drei Produkte. Ein Betriebsmodus ist nur die Auswahl der Treiber hinter +vier Ports — Store, Queue, Bus, Drives. Standalone heißt SQLite + lokale +Queue + asyncio-Bus, verteilt heißt Postgres + Celery/Redis. Derselbe +Rip-Code, dieselben Tests, dasselbe UI. + +Zwei Entwurfs-Entscheidungen, die von der naheliegenden Option abweichen: + +- **Keine Queue-Bibliothek** (nicht Taskiq, nicht ARQ). Stattdessen der + Grundsatz *„die Datenbank ist die Wahrheit, der Broker ist nur der Wecker"* + plus zwei sehr kleine Treiber. Nebenwirkung: `zombies.py` (206 Z + 263 Z + Tests) wird durch eine Lease ersetzt, statt portiert zu werden. +- **Mounten wandert auf den Host.** Die CIFS-Ausfälle waren kein Bug, sondern + die Folge davon, dass der Container mountet (Befund 26.07.2026). Damit + fallen `SYS_ADMIN`, `DAC_READ_SEARCH`, `apparmor:unconfined`, `rshared` und + die Mount-Wache weg. Die PRÜF-Logik aus `mounts.py` bleibt vollständig. + +### 2. Drei Entscheide des Commanders (28.08.2026) + +Alle drei stehen jetzt in `KONZEPT.md` § 10 — **wer nur eine Datei liest, +findet sie dort, nicht nur in KONZEPT-V2.md.** + +1. **Reihenfolge:** Echtzeit (SSE statt Polling) VOR der Windows-App. +2. **Disc-Schlüssel: automatischer Abruf MIT Rückfallebene.** Das verschiebt + die Grenze vom 25.07.2026 (*„Rippy verteilt KEINE Disc-Schlüssel"*) + **bewusst**. Umgesetzt als Kette in drei Stufen: eigener Bestand → + automatischer Abruf → Import von Hand. Fünf Regeln gehören dazu, allen + voran: **die Bezugsadresse steht in der Konfiguration und ist LEER + vorbelegt** (eine vorbelegte tote Adresse wäre genau die Falle aus + `.env.example`), ein Fehlschlag ist LAUT, ein funktionierender Bestand + wird nie still überschrieben, und Rippy bringt selbst nichts mit. +3. **Speicherziele:** Der Host mountet, Rippy erzeugt die kopierbare Zeile + (fstab / `.mount`-Unit / `compose.yml`). Die Eingabemaske im UI bleibt, + nur der Knopf „Verbinden" wird zu „Zeile kopieren". + +### 3. Etappe V2-0 gebaut: ein Paket statt drei Zwillingen + +`detection.py`, `makemkv_daten.py` und `notify.py` lagen je ZWEIMAL im Repo, +byte-identisch, weil es kein geteiltes Paket gab. Jetzt gibt es `src/rippy/` +(`core` / `drives` / `rip`); beide Container importieren dieselbe Datei. + +- `conftest.py` in der Wurzel legt `src/` auf den `sys.path` (die Ampel ruft + pytest dort auf). +- Beide Dockerfiles kopieren `src/rippy` nach `/app/rippy` — `/app` ist + Arbeitsverzeichnis und uvicorn-App-Dir, also ohne `PYTHONPATH` findbar. + Die Anordnung wurde nachgestellt und der Import geprüft, nicht vermutet. +- **`worker_setup_paket` packt das Paket ausdrücklich mit ins Zip.** Die + Schleife dort sah nur die oberste Ebene — ein Unterordner wäre nie + mitgekommen, und der Windows-Worker beim Start gestorben. +- Die zwei Test-Dateien für `makemkv_daten` sind zu einer verschmolzen. + Damit entfällt auch der `importlib`-Umweg im Worker-Test (er war nötig, + weil bei `pytest -q` aus der Wurzel `docker/api` zuerst eingesammelt wird + und jeder weitere Import nur noch den `sys.modules`-Cache trifft — die + Worker-Kopie wurde also nie angefasst). Netto −13 doppelte Tests. +- `test_zwillinge_sind_byteweise_identisch` ist weg. Der Wächter war nötig, + weil die Konstruktion falsch war; jetzt ist sie es nicht mehr. + +### 4. DER FEHLER, DEN DIESER UMBAU FAST AUSGELIEFERT HÄTTE + +In `tasks.py` steht der `detection`-Import in einem `try/except ImportError` — +**absichtlich**, denn der native Windows-Worker hat kein `fcntl` und soll +trotzdem starten (er komprimiert nur). + +Das `except` verschluckt aber **jeden** ImportError, auch einen falschen +Modulpfad. Auf Linux hätte der Worker ab sofort still `detect_disc_type=None` +gesetzt und **jeden Rip verweigert, ohne dass irgendwo ein Fehler gestanden +hätte** — die Klasse „still scheiternder Hintergrund-Prozess" aus `AGENTS.md`, +diesmal selbst gebaut. + +**Warum er fast durchkam:** Die erste Suche nach Import-Stellen prüfte nur +Zeilenanfänge (`^from detection import`). Dieser Import ist eingerückt. +Gefunden hat ihn erst eine zweite Suche ohne Zeilenanker. + +**Lehre fürs nächste Mal:** Bei einem Modul-Umzug reicht `grep '^import x'` +nicht — Importe stehen auch in `try`-Blöcken, in Funktionen und in +`if TYPE_CHECKING`. Und ein `except ImportError` um einen Umzug herum ist die +gefährlichste Stelle im ganzen Vorgang, weil sie den Fehler frisst. + +**Wächter dagegen:** `src/rippy/test_paket.py` prüft mit +`importlib.util.find_spec`, dass es die drei Modulpfade wirklich gibt. +`find_spec` **führt nichts aus** — der Test läuft deshalb auch auf Windows +ohne `fcntl` und ist nicht nur in der Ampel wirksam. Gegengeprüft: Modul +weggenommen → Test rot, zurückgelegt → grün. Ein Wächter, den man nicht hat +scheitern sehen, ist Deko. + +### WAS ALS NÄCHSTES ANSTEHT + +- [ ] **Deploy auf die VM steht aus — bewusst.** Ein Deploy startet die + Container neu, und der `api`-Container HÄLT die NAS-Verbindung: mitten + in einem Rip wäre das ein Datenverlust. Erst prüfen, ob etwas läuft + (`docker compose -p rippy ps`, Job-Liste im UI), dann + `git pull --ff-only && docker compose up -d --build`. + **Beim ersten Deploy nach V2-0 gezielt nachsehen:** Liegt `/app/rippy` + in BEIDEN Containern? (`docker exec ls /app/rippy`) Und enthält das + Worker-Zip den Ordner? (`GET /worker-setup/paket` herunterladen, + hineinschauen — der Windows-Worker startet sonst nicht.) +- [ ] **Etappe V2-1: Ports einziehen** — `Store`/`Queue`/`Bus`/`Drives` als + Protocol, v1-Verhalten läuft weiter über Postgres/Celery/Redis/Linux. + Wieder ohne Verhaltensänderung. Danach ist jeder weitere Betriebsmodus + eine Treiber-Datei statt eines Umbaus. +- [ ] Die zweite echte Doppelung anfassen: `api/db.py` und `worker/db.py` + (überlappend, nicht identisch) sowie `devices.eject` gegen + `ripping.wirf_disc_aus` — beides gehört in V2-1 (Store bzw. DAL). + +### HINWEIS FÜR DIE NÄCHSTE SITZUNG (lokale Umgebung) + +Die pydantic-Falle hat erneut zugeschlagen: global lag `pydantic 2.13.4` mit +`pydantic-core 2.48.0` (unverträglich, 2.13.4 verlangt 2.46.4), und das +Einsammeln ALLER API-Tests brach mit `SystemError`. Repariert mit +`python -m pip install "pydantic-core==2.46.4"`. Die Ampel merkt das nie, weil +sie in ein frisches venv aus `requirements.txt` installiert. Wenn lokal +plötzlich alle API-Tests wegbrechen: erst die Versionspaarung prüfen. + +Und der Ignore-Pfad hat sich geändert — Linux-only-Module beim lokalen +pytest-Lauf: +`--ignore=src/rippy/drives/test_detection.py --ignore=docker/api/test_prescan_helpers.py` +(vorher `docker/worker/test_detection.py`). + +## Letzter Stand davor: v3.21 — Rippy Windows Worker (27.07.2026) > **Etappe 25: Rippy Windows Worker komplett modernisiert.** > Der Installer und der Worker selbst wurden auf eine Flet-App (mit pystray) migriert,