4.1 KiB
4.1 KiB
Guarded Shutdown And History Savepoints
Date: 2026-07-13 Issue: BS-PR-709 / Gitea #55 Milestone: M7 - Production Readiness
Goal
Route every editor-exit surface through one non-blocking dirty-document decision and make scene dirtiness follow the authored undo timeline. Closing the native window, choosing File > Quit, and requesting an editor exit programmatically must never silently discard dirty scene tabs.
Decisions
- The editor disables Bevy's automatic close-and-exit systems and owns primary-window close
requests. A guarded-shutdown resource is the single authority that may emit
AppExit. - The final dirty-state recheck and
AppExitauthorization run in a dedicated schedule after Bevy'sLast; session clean-marker persistence runs after that finalizer. - Native window close, File > Quit, and internal editor quit requests enqueue the same shutdown intent. Repeated requests while confirmation or Save As is pending are coalesced.
- Clean sessions exit immediately. Dirty sessions use the shared
NativeDialogBrokerand a single Save All / Discard / Cancel decision. Cancel preserves all tabs and live editor state; Discard exits without rewriting authored files; Save All exits only after every dirty tab is saved. - Untitled tabs and other asynchronous Save As work keep shutdown pending until their dialog result
is applied on the main thread. Cancellation or any failed write returns the editor to an idle,
dirty, recoverable state and does not emit
AppExit. - Each scene tab retains a canonical authored-content checkpoint established only by a successful load or save. The checkpoint is keyed by stable actor identity, canonical component order, stable parent identity, and scene composition rather than transient Bevy entity numbers.
- Push marks the active document dirty. Undo and redo serialize the authoritative authored projection and compare it with that tab's checkpoint, so saving at nonzero history depth, returning to that point, branching, tab switches, and direct non-history mutations remain exact.
Implementation
- Add per-tab canonical clean checkpoints to scene I/O and reconcile
SceneIo::dirtyafter push, undo, redo, save, load, and document switches without persisting Entity-ID-based history stacks. - Add focused history tests for undo-to-clean, redo-away-from-clean, save at nonzero depth, branching before/after the clean point, and tab isolation.
- Add a guarded-shutdown plugin/resource that intercepts
WindowCloseRequested, accepts menu and programmatic requests, coordinates non-blocking confirmation/save completion, and emits the only finalAppExitfor the full editor. - Make Save All report complete, pending, cancelled, or failed explicitly. Resume pending shutdown after untitled Save As completion and preserve the originating tab plus dirty state on failure.
- Add File > Quit and route Switch Project's exit half through the same guard without spawning a replacement process until the dirty-document decision succeeds.
- Update ADR 0023, native-dialog/session-recovery documentation, editor architecture guidance, root README controls/checklist, and production-readiness evidence.
- Run formatting, strict Clippy, workspace tests, and focused headless state-machine tests. Keep packaged tests deferred by project-owner direction.
- Launch the exact editor commit under Hyprland. Exercise Cancel, Discard, Save All, clean close, File > Quit, and the compositor close button; verify the editor remains responsive during native dialogs, no state is lost on cancel/failure, and no process or warning remains after exit.
Acceptance
- Native close, File > Quit, and programmatic editor exit share one guarded implementation.
- One or many dirty tabs cannot be lost without explicit Discard; failed or cancelled saves do not exit and preserve the session.
- Undoing exactly to the saved state clears the dirty marker; redo or a divergent edit restores it.
- Saving at nonzero history depth establishes a new clean point without deleting useful history.
- Headless state-machine tests and exact-commit native Linux QA pass with clean logs.