# πŸ”΄ KRITISCH β€” Vorschau/Liste um 1 verschoben (Index-Drift durch SQLITE_BUSY) **Der Reader-Aliasing-Fix (6c4073a) war richtig, aber es gibt einen ZWEITEN, unabhaengigen Bug.** Klick auf β€žiTagPro" zeigt β€žOrivelle Pen" (die Nachbarzeile). ## Root Cause β€” bewiesen, nicht vermutet **Zwei Fehler, die sich verketten:** 1. **6 Mails sind in der mbox-DATEI, aber nicht im `mbox_index`.** Ursache: `PRAGMA busy_timeout` steht immer noch auf **0** (der `rettungslauf-fixes.md`-Brief ist nicht umgesetzt). Beim Rettungslauf gab es pro betroffenem Ordner `SQLITE_BUSY`: `mbox.Append` schrieb den Record in die Datei, der darauffolgende **Index-INSERT scheiterte sofort**. Ergebnis: Datei-Record vorhanden, Index-Zeile fehlt. 2. **Der plain-Reader liest nach DATEI-POSITION statt nach `file_offset`.** [`06-mbox.go` `readMboxMessageFromIndex`](backend/06-mbox.go): holt zwar die Index-Zeile (mit korrektem `file_offset`), wirft sie fuer plain aber weg (`if !strings.HasSuffix(path, ".zst") { return nil, false, nil }`) und faellt auf `msgs[index]` = Datei-Position zurueck. Ab der fehlenden Index-Zeile ist Position != seq β†’ alles um 1 verschoben. **Der Beweis (drift-scan ueber alle 66 Ordner):** ``` byrne/INBOX Datei 5869 Index 5868 +1 (Rettungslauf errors=1) crystal/INBOX Datei 4874 Index 4873 +1 (errors=2 -> crystal/INBOX.Sent Datei 2146 Index 2145 +1 genau 2 Ordner) gb/INBOX Datei 6675 Index 6674 +1 (errors=1) rb/INBOX Datei 8628 Index 8627 +1 (errors=1) roby/INBOX Datei 2718 Index 2717 +1 (errors=1) ``` **Drift-Anzahl == SQLITE_BUSY-Fehleranzahl, exakt.** Die 60 fehlerfreien Ordner haben 0 Drift. Und: Lesen ueber `file_offset` liefert fuer gb **4129/4129** Mails korrekt (0 echte Fehler) β€” die gespeicherten Offsets sind die Wahrheit. ## Fix (drei Teile, in dieser Reihenfolge) ### A. `busy_timeout` setzen β€” PFLICHT, sonst passiert es wieder `ConnectDB` (02-database.go): `PRAGMA busy_timeout = 10000;` neben `journal_mode=WAL`. ### B. Plain-mbox ueber `file_offset` lesen (der eigentliche Fix) `readMboxMessageFromIndex` **darf fuer plain nicht mehr auf die Positions- Zerlegung zurueckfallen.** Analog zum zstd-Pfad: - Index-Zeile holen β†’ `file_offset` seeken β†’ Record bis zum `file_offset` der **naechsten** Zeile (bzw. EOF) lesen, `TrimRight("\n")`. - Die fragile Neu-Zerlegung `readMboxMessages`+`msgs[index]` **ganz aus dem Vorschau-/Export-/Copy-Pfad entfernen.** Zwei getrennte Zerleger (Writer- Offsets vs. Reader-Resplit), die auseinanderlaufen koennen, sind die Wurzel. - Damit lesen Liste UND Vorschau aus **derselben** Quelle (`mbox_index`) β†’ sie koennen nicht mehr auseinanderlaufen. ### C. Die 6 verlorenen Index-Zeilen nachziehen β€” VOLLER Reindex aus der Datei Das bestehende `--reindex` haengt nur ab `indexed_bytes` an β€” es findet die **mittendrin** fehlenden Mails nicht. Es braucht einen **Full-Rebuild**: - `--reindex --rebuild `: `mbox_index` fuer den Ordner **loeschen** und aus der Datei **komplett neu** aufbauen (jeder Record β†’ seq, file_offset, message_id, subject, date). Danach ist Index-Anzahl == Datei-Records. - **Wichtig β€” `copied` mitziehen:** Diese 6 Mails stehen auch **nicht** in `copied` (der Busy-Fehler hat auch `MarkCopied` verhindert). Ein Watch-Lauf wuerde sie sonst erneut ins Ziel-Postfach **und** erneut in die mbox kopieren (Dublette in beiden!). Beim Rebuild deshalb pro Record die Message-ID (+ ggf. `body_sha256`) in `copied` mit `mbox_done=1` eintragen. ## Sekundaerbefunde (mitnehmen, kein Blocker) - **Ungueltiges UTF-8 in `mbox_index.subject`** (z.B. eine Tesla-Newsletter-Mail mit kaputtem Byte) β€” bricht sogar `sqlite3`-Abfragen. Subject vor dem Speichern nach UTF-8 sanitisieren (ungueltige Bytes ersetzen). - **Envelope-Message-ID vs. Roh-Header:** In ~8 gb-Faellen steht im Index ein `sha256:`-Fallback, obwohl die Datei einen echten `Message-ID`-Header hat. Beim Indizieren zuerst den Roh-Header parsen, bevor auf Hash zurueckgefallen wird β€” sonst greift Dedup fuer diese Mails nicht. ## Abnahme 1. `go test ./...` gruen; `PRAGMA busy_timeout` = 10000. 2. Test, der die Drift faengt: mbox mit N Records schreiben, eine Index-Zeile **in der Mitte** loeschen, dann Vorschau von seq > Luecke abrufen β†’ muss die **richtige** Mail liefern (heute die Nachbarmail). 3. `--reindex --rebuild all`, danach **drift-scan == 0** fuer ALLE 66 Ordner (Datei-Records == Index == Listen-Eintraege). 4. Watch-Lauf nach dem Rebuild: `copied=0` fuer alle Konten (keine Neukopie der nachgezogenen Mails). 5. Voller Sweep (Betreff **und** Datum) ueber gb/INBOX und crystal/INBOX: **0 Abweichungen**. Ich (Claude) fahre nach dem Fix den drift-scan ueber alle Ordner UND einen Voll-Sweep gegen `mbox_index` β€” diesmal ueber ALLE betroffenen Postfaecher, nicht nur eines.