docs: SAVEPOINT fuer v4.0-alpha — v2-Konzept entschieden, Etappe V2-0 gruen
Ampel / ampel (push) Successful in 39s

WAS: Neuer Kopfeintrag mit dem gemessenen Zustand (b98dc5e, Ampel gruen,
290 Tests, 0 doppelte Module), den drei Commander-Entscheiden, der
gebauten Etappe V2-0 und dem naechsten Schritt.

WARUM: AGENTS-Workflow Punkt 5 — die naechste Sitzung faengt nicht bei
Null an. Besonders wichtig diesmal, weil der Deploy auf die VM bewusst
aussteht und beim ersten Deploy nach V2-0 zwei Dinge gezielt zu pruefen
sind (liegt /app/rippy in beiden Containern, enthaelt das Worker-Zip
den Ordner).

Dokumentiert ist ausserdem der Fehler, den der Umbau fast ausgeliefert
haette: ein "try/except ImportError" um den detection-Import in
tasks.py haette einen falschen Modulpfad STILL geschluckt und auf Linux
jeden Rip verweigert. Die Lehre steht dabei — bei einem Modul-Umzug
reicht grep auf Zeilenanfaenge nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-28 08:41:29 +02:00
co-authored by Claude Opus 5
parent b98dc5eef5
commit edfe0d4313
+144 -1
View File
@@ -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 <c> 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,