diff --git a/src/main/app-controller.ts b/src/main/app-controller.ts index 1a28278..a550a16 100644 --- a/src/main/app-controller.ts +++ b/src/main/app-controller.ts @@ -673,15 +673,23 @@ export class AppController { resetHistoryForRetention(this.storagePaths, this.settings.historyRetentionMode); - // Prevent prepareForShutdown from overwriting the restored data + // Block runtime + shutdown persistence so the STALE in-memory session (the + // manager still holds the PRE-import session — importBackup only wrote to disk) + // cannot overwrite the restored data in the brief window before the auto-relaunch. + // The relaunch (triggered in main.ts when restored===true) starts a fresh process + // that loads the restored session cleanly via the normal startup path, so these + // flags never linger in a live session. + // M2-Fix: vorher blieb blockAllPersistence dauerhaft true wenn der User die + // manuelle "Bitte neustarten"-Aufforderung ignorierte → stille Persistenz-Blockade, + // alle weiteren Änderungen gingen bei hartem Crash verloren. Jetzt: Auto-Relaunch. this.manager.skipShutdownPersist = true; this.manager.blockAllPersistence = true; - logger.info("Backup wiederhergestellt (verschlüsseltes Format)"); + logger.info("Backup wiederhergestellt — App startet automatisch neu"); this.audit("WARN", "Backup importiert", { historyEntries: Array.isArray(parsed.history) ? parsed.history.length : 0, accountSummary: buildAccountSummary(this.settings) }); - return { restored: true, message: "Backup wiederhergestellt. Bitte App neustarten." }; + return { restored: true, message: "Backup wiederhergestellt – App startet automatisch neu…" }; } public getSessionLogPath(): string | null { diff --git a/src/main/main.ts b/src/main/main.ts index d4fbdd7..d05e425 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -664,7 +664,20 @@ function registerIpcHandlers(): void { return { restored: false, message: `Backup-Datei zu groß (max 50 MB, Datei hat ${(stat.size / 1024 / 1024).toFixed(1)} MB)` }; } const data = await fs.promises.readFile(filePath); - return controller.importBackup(data); + const importResult = controller.importBackup(data); + if (importResult.restored) { + // M2: Nach erfolgreichem Import die App automatisch neu starten. Der frische + // Prozess lädt die wiederhergestellte Session sauber vom Disk (kein Stale- + // In-Memory-State, kein dauerhaft blockierter Persist in einer lebenden Session). + // Vom Main getrieben (nicht Renderer), damit ein Renderer-Fehler den Restart + // nicht verhindern kann. Kurze Verzögerung, damit das Ergebnis den Renderer + // erreicht (Toast "App startet automatisch neu…"). + setTimeout(() => { + app.relaunch(); + app.quit(); + }, 1500); + } + return importResult; }); controller.onState = (snapshot) => { diff --git a/tasks/todo.md b/tasks/todo.md index c4caa30..f1e63cc 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -64,7 +64,7 @@ App läuft headless auf Windows-Server → Nutzer sitzt nicht davor. Größte L - ✅ **N1** — toter Disk-Fallback-Block in `findReadyArchiveSets` + verwaiste `pendingItemStatus`-Map entfernt (verhaltensneutral). **Bewusst NICHT umgesetzt (mit Begründung):** -- ⏭️ **M2** (`blockAllPersistence` nie zurückgesetzt) — der vom Report vorgeschlagene Reset wäre **unsicher**: nach Backup-Import ist die In-Memory-Session stale (Import schreibt nur auf Disk, lädt den Manager nicht neu). Ein Reset würde beim nächsten persist die restored Daten mit Stale-State überschreiben. `blockAllPersistence` ist absichtlich bis-Neustart. Sauberer Fix = In-Memory-Reload nach Import (größerer Umbau, separat). +- ✅ **M2** (`blockAllPersistence` nie zurückgesetzt) — GELÖST in v1.7.159 via **Auto-Relaunch**. In-Memory-Reload wäre unsicher (Task-finally-Blöcke settlen async gegen `this.session.items[id]` → Race beim Session-Swap, bräuchte async-Refactor). Stattdessen: nach erfolgreichem Import startet die App automatisch neu (main-getrieben in main.ts, nicht Renderer — robust gegen Renderer-Fehler). Der frische Prozess lädt die restored Session sauber via Standard-Startup-Pfad. `skipShutdownPersist`/`blockAllPersistence` schützen das ~1.5s-Fenster + den Quit (verifiziert: prepareForShutdown:5680 überspringt Persistenz sauber). Footgun eliminiert — User kann nicht mehr im blockierten Zustand weiterarbeiten. - ⏭️ **M3** (`cancelPendingAsyncSaves` wartet nicht auf laufenden Save) — Report stuft selbst als reines I/O-Overlap ein; die Generation-Guard (storage.ts:1022) schützt die Datenintegrität bereits (stale Write wird verworfen). Kein Korrektheitsgewinn, daher kein Eingriff. **Verifikation:** 30 Test-Dateien, 621 Tests grün. Build sauber. Advisor-Review vor Implementierung (fing H2-Falle: Hybrid-Controller nicht in die Deferred-Map legen, sonst killt `runDeferredPostExtraction` sie selbst).