65 lines
4.2 KiB
Markdown
65 lines
4.2 KiB
Markdown
# Guarded Shutdown And History Savepoints
|
|
|
|
> **Historical plan — not current implementation guidance.** Use the [documentation index](../../docs/README.md) for current behavior and architecture.
|
|
|
|
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 `AppExit` authorization run in a dedicated schedule after
|
|
Bevy's `Last`; 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 `NativeDialogBroker` and 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
|
|
|
|
1. Add per-tab canonical clean checkpoints to scene I/O and reconcile `SceneIo::dirty` after push,
|
|
undo, redo, save, load, and document switches without persisting Entity-ID-based history stacks.
|
|
2. 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.
|
|
3. Add a guarded-shutdown plugin/resource that intercepts `WindowCloseRequested`, accepts menu and
|
|
programmatic requests, coordinates non-blocking confirmation/save completion, and emits the only
|
|
final `AppExit` for the full editor.
|
|
4. 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.
|
|
5. 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.
|
|
6. Update ADR 0023, native-dialog/session-recovery documentation, editor architecture guidance,
|
|
root README controls/checklist, and production-readiness evidence.
|
|
7. Run formatting, strict Clippy, workspace tests, and focused headless state-machine tests. Keep
|
|
packaged tests deferred by project-owner direction.
|
|
8. 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.
|